Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -41,7 +41,7 @@ def load_models():
|
|
41 |
# Load T5-small model for evaluation
|
42 |
models['evaluator'] = pipeline(
|
43 |
"text2text-generation",
|
44 |
-
model="
|
45 |
max_length=200
|
46 |
)
|
47 |
|
@@ -303,94 +303,121 @@ def summarize_resume_text(resume_text):
|
|
303 |
return formatted_summary, execution_time
|
304 |
|
305 |
#####################################
|
306 |
-
# Function: Calculate Google Match Score
|
307 |
#####################################
|
308 |
def calculate_google_match_score(candidate_summary):
|
309 |
"""
|
310 |
-
Calculate a match score based on skills and experience in the candidate summary
|
311 |
compared with what Google requires.
|
|
|
|
|
|
|
|
|
|
|
312 |
"""
|
313 |
-
#
|
314 |
-
|
315 |
-
"
|
316 |
-
"python", "java", "c++", "go", "javascript",
|
317 |
-
|
318 |
-
"
|
319 |
-
|
320 |
-
|
321 |
-
|
322 |
-
|
323 |
-
|
324 |
-
"
|
325 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
326 |
}
|
327 |
|
328 |
summary_lower = candidate_summary.lower()
|
329 |
|
330 |
-
#
|
331 |
-
|
332 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
333 |
|
334 |
-
#
|
335 |
-
|
336 |
|
337 |
-
#
|
338 |
-
|
339 |
|
340 |
-
|
|
|
|
|
|
|
|
|
|
|
341 |
|
342 |
#####################################
|
343 |
-
# Function:
|
344 |
#####################################
|
345 |
@st.cache_data(show_spinner=False)
|
346 |
-
def
|
347 |
"""
|
348 |
-
Use T5-small model to
|
349 |
-
|
350 |
"""
|
351 |
start_time = time.time()
|
352 |
|
353 |
evaluator = _evaluator or models['evaluator']
|
354 |
|
355 |
-
#
|
356 |
-
|
357 |
-
|
|
|
358 |
|
359 |
-
# Create a
|
360 |
-
# T5 works well with task prefixes
|
361 |
prompt = f"""
|
362 |
-
|
363 |
-
|
364 |
-
|
365 |
-
Resume Summary: {candidate_summary[:500]}
|
366 |
-
|
367 |
-
Google Requirements: {GOOGLE_DESCRIPTION[:500]}
|
368 |
-
|
369 |
-
Score: {score_percent}/100
|
370 |
"""
|
371 |
|
372 |
-
# Generate
|
373 |
-
|
374 |
-
|
375 |
-
# Ensure the output uses third-person tone
|
376 |
-
# T5-small may not always follow instructions perfectly, so we'll check and adjust
|
377 |
-
first_person_pronouns = ["i ", "i'm", "i am", "my ", "mine ", "we ", "our "]
|
378 |
-
second_person_pronouns = ["you ", "your ", "yours "]
|
379 |
|
380 |
-
|
381 |
-
|
382 |
-
|
383 |
-
if any(pronoun in evaluation_lower for pronoun in first_person_pronouns + second_person_pronouns):
|
384 |
-
evaluation_result = f"The candidate {evaluation_result}"
|
385 |
-
|
386 |
-
# Ensure evaluation starts with third-person phrasing if it doesn't already
|
387 |
-
if not any(evaluation_result.lower().startswith(phrase) for phrase in
|
388 |
-
["this candidate", "the candidate", "candidate", "this applicant", "the applicant"]):
|
389 |
-
evaluation_result = f"This candidate {evaluation_result}"
|
390 |
|
391 |
execution_time = time.time() - start_time
|
392 |
|
393 |
-
return
|
394 |
|
395 |
#####################################
|
396 |
# Main Streamlit Interface - with Progress Reporting
|
@@ -398,10 +425,10 @@ Score: {score_percent}/100
|
|
398 |
st.title("Google Resume Match Analyzer")
|
399 |
st.markdown(
|
400 |
"""
|
401 |
-
Upload your resume file in **.docx**, **.doc**, or **.txt** format to see how well
|
402 |
-
1. Extracts text from
|
403 |
2. Uses AI to generate a structured candidate summary.
|
404 |
-
3. Evaluates
|
405 |
"""
|
406 |
)
|
407 |
|
@@ -413,7 +440,7 @@ with st.expander("Google's Requirements", expanded=False):
|
|
413 |
uploaded_file = st.file_uploader("Upload your resume (.docx, .doc, or .txt)", type=["docx", "doc", "txt"])
|
414 |
|
415 |
# Process button with optimized flow
|
416 |
-
if uploaded_file is not None and st.button("Analyze Google Fit"):
|
417 |
# Create a placeholder for the progress bar
|
418 |
progress_bar = st.progress(0)
|
419 |
status_text = st.empty()
|
@@ -429,17 +456,18 @@ if uploaded_file is not None and st.button("Analyze Google Fit"):
|
|
429 |
# Step 2: Generate summary
|
430 |
status_text.text("Step 2/3: Analyzing resume and generating summary...")
|
431 |
summary, summarization_time = summarize_resume_text(resume_text)
|
432 |
-
progress_bar.progress(
|
433 |
|
434 |
# Display summary
|
435 |
-
st.subheader("
|
436 |
st.markdown(summary)
|
437 |
st.info(f"Summary generated in {summarization_time:.2f} seconds")
|
438 |
|
439 |
-
# Step 3:
|
440 |
-
status_text.text("Step 3/3:
|
441 |
-
|
442 |
-
|
|
|
443 |
)
|
444 |
progress_bar.progress(100)
|
445 |
|
@@ -449,40 +477,50 @@ if uploaded_file is not None and st.button("Analyze Google Fit"):
|
|
449 |
# Display Google fit results
|
450 |
st.subheader("Google Fit Assessment")
|
451 |
|
452 |
-
# Display score with appropriate color and emoji
|
453 |
-
score_percent = int(
|
454 |
-
if
|
455 |
-
st.success(f"**Google Match Score:** {score_percent}% π")
|
456 |
-
elif
|
457 |
-
st.success(f"**Google Match Score:** {score_percent}% β
")
|
458 |
-
elif
|
459 |
-
st.warning(f"**Google Match Score:** {score_percent}% β οΈ")
|
460 |
else:
|
461 |
-
st.error(f"**Google Match Score:** {score_percent}% π")
|
462 |
|
463 |
-
# Display
|
464 |
-
st.markdown("###
|
465 |
-
st.markdown(
|
466 |
-
|
467 |
-
|
|
|
|
|
|
|
|
|
468 |
|
469 |
# Add potential next steps based on the score
|
470 |
st.subheader("Recommended Next Steps")
|
471 |
-
|
|
|
|
|
|
|
|
|
472 |
st.markdown("""
|
473 |
- Consider applying for positions at Google that match your experience
|
474 |
- Prepare for technical interviews by practicing algorithms and system design
|
475 |
- Review Google's interview process and STAR method for behavioral questions
|
476 |
""")
|
477 |
-
elif
|
478 |
-
|
479 |
-
|
|
|
480 |
- Work on projects that demonstrate your skills in Google's key technology areas
|
481 |
- Consider taking additional courses in algorithms, system design, or other Google focus areas
|
482 |
""")
|
483 |
else:
|
484 |
-
|
485 |
-
|
|
|
486 |
- Develop projects showcasing problem-solving abilities and technical skills
|
487 |
- Consider gaining more experience before applying, or target specific Google roles that better match your profile
|
488 |
""")
|
|
|
41 |
# Load T5-small model for evaluation
|
42 |
models['evaluator'] = pipeline(
|
43 |
"text2text-generation",
|
44 |
+
model="google-t5/t5-small",
|
45 |
max_length=200
|
46 |
)
|
47 |
|
|
|
303 |
return formatted_summary, execution_time
|
304 |
|
305 |
#####################################
|
306 |
+
# Function: Calculate Google Match Score - Detailed Breakdown
|
307 |
#####################################
|
308 |
def calculate_google_match_score(candidate_summary):
|
309 |
"""
|
310 |
+
Calculate a detailed match score breakdown based on skills and experience in the candidate summary
|
311 |
compared with what Google requires.
|
312 |
+
|
313 |
+
Returns:
|
314 |
+
- overall_score: A normalized score between 0 and 1
|
315 |
+
- category_scores: A dictionary with scores for each category
|
316 |
+
- score_breakdown: A formatted string explanation of the scoring
|
317 |
"""
|
318 |
+
# Define categories that Google values with specific keywords
|
319 |
+
google_categories = {
|
320 |
+
"Technical Skills": {
|
321 |
+
"keywords": ["python", "java", "c++", "go", "javascript", "sql", "nosql",
|
322 |
+
"algorithms", "data structures", "system design"],
|
323 |
+
"weight": 0.35
|
324 |
+
},
|
325 |
+
"Advanced Technologies": {
|
326 |
+
"keywords": ["artificial intelligence", "machine learning", "cloud computing",
|
327 |
+
"ai", "ml", "cloud", "data science", "big data",
|
328 |
+
"tensorflow", "pytorch", "deep learning"],
|
329 |
+
"weight": 0.25
|
330 |
+
},
|
331 |
+
"Problem Solving": {
|
332 |
+
"keywords": ["problem solving", "algorithms", "analytical", "critical thinking",
|
333 |
+
"debugging", "troubleshooting", "optimization"],
|
334 |
+
"weight": 0.20
|
335 |
+
},
|
336 |
+
"Innovation & Creativity": {
|
337 |
+
"keywords": ["innovation", "creative", "creativity", "novel", "cutting-edge",
|
338 |
+
"research", "design thinking", "innovative"],
|
339 |
+
"weight": 0.10
|
340 |
+
},
|
341 |
+
"Teamwork & Leadership": {
|
342 |
+
"keywords": ["team", "leadership", "collaborate", "collaboration", "communication",
|
343 |
+
"mentoring", "lead", "coordinate", "agile", "scrum"],
|
344 |
+
"weight": 0.10
|
345 |
+
}
|
346 |
}
|
347 |
|
348 |
summary_lower = candidate_summary.lower()
|
349 |
|
350 |
+
# Calculate scores for each category
|
351 |
+
category_scores = {}
|
352 |
+
for category, details in google_categories.items():
|
353 |
+
keywords = details["keywords"]
|
354 |
+
max_possible = len(keywords) # Maximum possible matches
|
355 |
+
|
356 |
+
# Count matches (unique keywords found)
|
357 |
+
matches = sum(1 for keyword in keywords if keyword in summary_lower)
|
358 |
+
|
359 |
+
# Calculate category score (0-1 range)
|
360 |
+
if max_possible > 0:
|
361 |
+
raw_score = matches / max_possible
|
362 |
+
# Apply a curve to reward having more matches
|
363 |
+
category_scores[category] = min(1.0, raw_score * 1.5)
|
364 |
+
else:
|
365 |
+
category_scores[category] = 0
|
366 |
+
|
367 |
+
# Calculate weighted overall score
|
368 |
+
overall_score = sum(
|
369 |
+
score * google_categories[category]["weight"]
|
370 |
+
for category, score in category_scores.items()
|
371 |
+
)
|
372 |
|
373 |
+
# Ensure overall score is in 0-1 range
|
374 |
+
overall_score = min(1.0, max(0.0, overall_score))
|
375 |
|
376 |
+
# Create score breakdown explanation
|
377 |
+
score_breakdown = "**Score Breakdown by Category:**\n\n"
|
378 |
|
379 |
+
for category, score in category_scores.items():
|
380 |
+
percentage = int(score * 100)
|
381 |
+
weight = int(google_categories[category]["weight"] * 100)
|
382 |
+
score_breakdown += f"β’ **{category}** ({weight}% of total): {percentage}%\n"
|
383 |
+
|
384 |
+
return overall_score, category_scores, score_breakdown
|
385 |
|
386 |
#####################################
|
387 |
+
# Function: Generate Aspect-Based Feedback with T5
|
388 |
#####################################
|
389 |
@st.cache_data(show_spinner=False)
|
390 |
+
def generate_aspect_feedback(candidate_summary, category_scores, _evaluator=None):
|
391 |
"""
|
392 |
+
Use T5-small model to generate feedback on the candidate's strongest and weakest areas
|
393 |
+
for Google, based on the category scores.
|
394 |
"""
|
395 |
start_time = time.time()
|
396 |
|
397 |
evaluator = _evaluator or models['evaluator']
|
398 |
|
399 |
+
# Sort categories by score
|
400 |
+
sorted_categories = sorted(category_scores.items(), key=lambda x: x[1], reverse=True)
|
401 |
+
top_categories = sorted_categories[:2]
|
402 |
+
bottom_categories = sorted_categories[-2:]
|
403 |
|
404 |
+
# Create a prompt for T5
|
|
|
405 |
prompt = f"""
|
406 |
+
Generate specific third-person feedback on the candidate's fit for Google.
|
407 |
+
Focus on these strengths: {', '.join([cat for cat, _ in top_categories])}.
|
408 |
+
And these improvement areas: {', '.join([cat for cat, _ in bottom_categories])}.
|
|
|
|
|
|
|
|
|
|
|
409 |
"""
|
410 |
|
411 |
+
# Generate focused feedback
|
412 |
+
feedback = evaluator(prompt)[0]['generated_text']
|
|
|
|
|
|
|
|
|
|
|
413 |
|
414 |
+
# Ensure third-person tone
|
415 |
+
if not any(feedback.lower().startswith(start) for start in ["the candidate", "this candidate"]):
|
416 |
+
feedback = f"This candidate {feedback}"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
417 |
|
418 |
execution_time = time.time() - start_time
|
419 |
|
420 |
+
return feedback, execution_time
|
421 |
|
422 |
#####################################
|
423 |
# Main Streamlit Interface - with Progress Reporting
|
|
|
425 |
st.title("Google Resume Match Analyzer")
|
426 |
st.markdown(
|
427 |
"""
|
428 |
+
Upload your resume file in **.docx**, **.doc**, or **.txt** format to see how well you match with Google's hiring requirements. The app performs the following tasks:
|
429 |
+
1. Extracts text from your resume.
|
430 |
2. Uses AI to generate a structured candidate summary.
|
431 |
+
3. Evaluates your fit for Google across key hiring criteria with a detailed score breakdown.
|
432 |
"""
|
433 |
)
|
434 |
|
|
|
440 |
uploaded_file = st.file_uploader("Upload your resume (.docx, .doc, or .txt)", type=["docx", "doc", "txt"])
|
441 |
|
442 |
# Process button with optimized flow
|
443 |
+
if uploaded_file is not None and st.button("Analyze My Google Fit"):
|
444 |
# Create a placeholder for the progress bar
|
445 |
progress_bar = st.progress(0)
|
446 |
status_text = st.empty()
|
|
|
456 |
# Step 2: Generate summary
|
457 |
status_text.text("Step 2/3: Analyzing resume and generating summary...")
|
458 |
summary, summarization_time = summarize_resume_text(resume_text)
|
459 |
+
progress_bar.progress(50)
|
460 |
|
461 |
# Display summary
|
462 |
+
st.subheader("Your Resume Summary")
|
463 |
st.markdown(summary)
|
464 |
st.info(f"Summary generated in {summarization_time:.2f} seconds")
|
465 |
|
466 |
+
# Step 3: Calculate scores and generate feedback
|
467 |
+
status_text.text("Step 3/3: Calculating Google fit scores...")
|
468 |
+
overall_score, category_scores, score_breakdown = calculate_google_match_score(summary)
|
469 |
+
feedback, feedback_time = generate_aspect_feedback(
|
470 |
+
summary, category_scores, _evaluator=models['evaluator']
|
471 |
)
|
472 |
progress_bar.progress(100)
|
473 |
|
|
|
477 |
# Display Google fit results
|
478 |
st.subheader("Google Fit Assessment")
|
479 |
|
480 |
+
# Display overall score with appropriate color and emoji
|
481 |
+
score_percent = int(overall_score * 100)
|
482 |
+
if overall_score >= 0.85:
|
483 |
+
st.success(f"**Overall Google Match Score:** {score_percent}% π")
|
484 |
+
elif overall_score >= 0.70:
|
485 |
+
st.success(f"**Overall Google Match Score:** {score_percent}% β
")
|
486 |
+
elif overall_score >= 0.50:
|
487 |
+
st.warning(f"**Overall Google Match Score:** {score_percent}% β οΈ")
|
488 |
else:
|
489 |
+
st.error(f"**Overall Google Match Score:** {score_percent}% π")
|
490 |
|
491 |
+
# Display score breakdown
|
492 |
+
st.markdown("### Score Calculation")
|
493 |
+
st.markdown(score_breakdown)
|
494 |
+
|
495 |
+
# Display focused feedback
|
496 |
+
st.markdown("### Expert Assessment")
|
497 |
+
st.markdown(feedback)
|
498 |
+
|
499 |
+
st.info(f"Assessment completed in {feedback_time:.2f} seconds")
|
500 |
|
501 |
# Add potential next steps based on the score
|
502 |
st.subheader("Recommended Next Steps")
|
503 |
+
|
504 |
+
# Find the weakest categories
|
505 |
+
weakest_categories = sorted(category_scores.items(), key=lambda x: x[1])[:2]
|
506 |
+
|
507 |
+
if overall_score >= 0.80:
|
508 |
st.markdown("""
|
509 |
- Consider applying for positions at Google that match your experience
|
510 |
- Prepare for technical interviews by practicing algorithms and system design
|
511 |
- Review Google's interview process and STAR method for behavioral questions
|
512 |
""")
|
513 |
+
elif overall_score >= 0.60:
|
514 |
+
improvement_areas = ", ".join([cat for cat, _ in weakest_categories])
|
515 |
+
st.markdown(f"""
|
516 |
+
- Focus on strengthening these areas: {improvement_areas}
|
517 |
- Work on projects that demonstrate your skills in Google's key technology areas
|
518 |
- Consider taking additional courses in algorithms, system design, or other Google focus areas
|
519 |
""")
|
520 |
else:
|
521 |
+
improvement_areas = ", ".join([cat for cat, _ in weakest_categories])
|
522 |
+
st.markdown(f"""
|
523 |
+
- Build experience in these critical areas: {improvement_areas}
|
524 |
- Develop projects showcasing problem-solving abilities and technical skills
|
525 |
- Consider gaining more experience before applying, or target specific Google roles that better match your profile
|
526 |
""")
|