Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -315,185 +315,222 @@ def summarize_resume_text(resume_text):
|
|
315 |
#####################################
|
316 |
def analyze_google_fit(resume_summary):
|
317 |
"""
|
318 |
-
Analyze how well the candidate fits Google's requirements
|
|
|
319 |
"""
|
320 |
-
|
321 |
-
|
322 |
-
# Define Google's key skill categories with more detailed keywords
|
323 |
-
google_keywords = {
|
324 |
-
"technical_skills": ["python", "java", "c++", "javascript", "go", "sql", "algorithms", "data structures",
|
325 |
-
"coding", "software development", "git", "programming", "backend", "frontend", "full-stack"],
|
326 |
-
"advanced_tech": ["machine learning", "ai", "artificial intelligence", "cloud", "data science", "big data",
|
327 |
-
"tensorflow", "deep learning", "distributed systems", "kubernetes", "microservices"],
|
328 |
-
"problem_solving": ["problem solving", "analytical", "critical thinking", "troubleshooting", "debugging",
|
329 |
-
"optimization", "scalability", "system design", "complexity", "efficiency"],
|
330 |
-
"innovation": ["innovation", "creative", "creativity", "design thinking", "research", "novel solutions",
|
331 |
-
"patents", "publications", "unique approaches", "cutting-edge"],
|
332 |
-
"soft_skills": ["team", "leadership", "collaboration", "communication", "agile", "project management",
|
333 |
-
"mentoring", "cross-functional", "presentation", "stakeholder management"]
|
334 |
-
}
|
335 |
|
336 |
-
#
|
337 |
-
|
338 |
-
|
339 |
-
|
340 |
-
|
341 |
-
|
342 |
-
"soft_skills": {"weight": 0.10, "label": "Collaboration & Leadership"}
|
343 |
-
}
|
344 |
|
345 |
-
|
|
|
346 |
|
347 |
-
#
|
348 |
-
|
349 |
-
|
350 |
-
|
351 |
|
352 |
-
|
353 |
-
|
354 |
-
|
355 |
-
found_skills[category] = category_matches
|
356 |
-
|
357 |
-
# Count matches but cap at a reasonable level
|
358 |
-
matches = len(category_matches)
|
359 |
-
total_keywords = len(keywords)
|
360 |
-
|
361 |
-
# Calculate raw percentage for this category
|
362 |
-
raw_percentage = int((matches / total_keywords) * 100)
|
363 |
-
|
364 |
-
# Apply logarithmic scaling for more realistic scores
|
365 |
-
if matches == 0:
|
366 |
-
adjusted_score = 0.0
|
367 |
-
else:
|
368 |
-
# Logarithmic scaling to prevent perfect scores
|
369 |
-
adjusted_score = min(0.95, (math.log(matches + 1) / math.log(min(total_keywords, 8) + 1)))
|
370 |
-
|
371 |
-
# Store both raw and adjusted scores for feedback
|
372 |
-
category_scores[category] = adjusted_score
|
373 |
-
category_details[category] = {
|
374 |
-
"raw_percentage": raw_percentage,
|
375 |
-
"adjusted_score": int(adjusted_score * 100),
|
376 |
-
"matching_keywords": category_matches,
|
377 |
-
"total_keywords": total_keywords,
|
378 |
-
"matches": matches
|
379 |
-
}
|
380 |
-
|
381 |
-
# Calculate weighted score
|
382 |
-
weighted_score = sum(score * category_weights[category]["weight"] for category, score in category_scores.items())
|
383 |
-
|
384 |
-
# Apply final curve to keep scores in a realistic range
|
385 |
-
match_percentage = min(92, max(35, int(weighted_score * 100)))
|
386 |
-
|
387 |
-
# Find top strengths and areas for improvement
|
388 |
-
strengths = [(category_weights[cat]["label"], details["adjusted_score"])
|
389 |
-
for cat, details in category_details.items()
|
390 |
-
if details["adjusted_score"] >= 60]
|
391 |
-
|
392 |
-
weaknesses = [(category_weights[cat]["label"], details["adjusted_score"])
|
393 |
-
for cat, details in category_details.items()
|
394 |
-
if details["adjusted_score"] < 50]
|
395 |
-
|
396 |
-
# Sort strengths and weaknesses by score
|
397 |
-
strengths.sort(key=lambda x: x[1], reverse=True)
|
398 |
-
weaknesses.sort(key=lambda x: x[1])
|
399 |
-
|
400 |
-
# Create a more detailed prompt for assessment
|
401 |
-
strength_text = ", ".join([f"{s[0]}" for s in strengths[:3]]) if strengths else "limited applicable skills"
|
402 |
-
weakness_text = ", ".join([f"{w[0]}" for w in weaknesses[:3]]) if weaknesses else "no obvious weaknesses"
|
403 |
-
|
404 |
-
# Extract key resume elements
|
405 |
-
skills_match = re.search(r'Skills:.*?(?=\n\n|$)', resume_summary, re.DOTALL)
|
406 |
-
skills_text = skills_match.group(0) if skills_match else ""
|
407 |
-
|
408 |
-
work_match = re.search(r'Previous Work Experience:.*?(?=\n\n|$)', resume_summary, re.DOTALL)
|
409 |
-
work_text = work_match.group(0) if work_match else ""
|
410 |
-
|
411 |
-
# List specific matching skills for more detailed assessment
|
412 |
-
specific_skills = []
|
413 |
-
for category, matches in found_skills.items():
|
414 |
-
if matches:
|
415 |
-
specific_skills.extend(matches[:3]) # Take up to 3 skills from each category
|
416 |
|
417 |
-
|
|
|
|
|
418 |
|
|
|
419 |
prompt = f"""
|
420 |
-
|
421 |
-
|
422 |
-
|
423 |
-
|
424 |
-
|
425 |
-
|
426 |
-
|
427 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
428 |
"""
|
429 |
|
430 |
try:
|
431 |
-
# Generate the assessment
|
432 |
assessment_results = models['evaluator'](
|
433 |
prompt,
|
434 |
-
max_length=
|
435 |
do_sample=True,
|
436 |
-
temperature=0.7,
|
437 |
num_return_sequences=3
|
438 |
)
|
439 |
|
440 |
# Find the best response
|
441 |
-
|
442 |
for result in assessment_results:
|
443 |
text = result['generated_text'].strip()
|
444 |
|
445 |
-
#
|
446 |
-
text = re.sub(r'
|
447 |
-
text = re.sub(r'Write a detailed
|
448 |
|
449 |
-
# Check if it
|
450 |
-
if "this candidate"
|
451 |
-
|
452 |
break
|
453 |
|
454 |
-
#
|
455 |
-
if
|
456 |
-
assessment =
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
457 |
|
458 |
except Exception as e:
|
459 |
-
#
|
460 |
-
|
461 |
-
|
462 |
|
463 |
# Final cleanup
|
|
|
|
|
|
|
464 |
assessment = assessment.strip()
|
465 |
-
if not assessment.startswith("This candidate"):
|
466 |
-
assessment = f"This candidate {assessment}"
|
467 |
|
468 |
-
|
|
|
|
|
|
|
469 |
|
470 |
return assessment, match_percentage, category_details, execution_time
|
471 |
|
472 |
-
|
|
|
473 |
"""
|
474 |
-
Generate a
|
|
|
475 |
"""
|
476 |
-
#
|
477 |
-
|
478 |
-
|
479 |
-
|
480 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
481 |
else:
|
482 |
-
|
483 |
-
|
484 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
485 |
|
486 |
-
# Add
|
487 |
-
if
|
488 |
-
|
|
|
|
|
|
|
|
|
489 |
|
490 |
-
# Add
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
491 |
if match_percentage >= 70:
|
492 |
-
assessment += f"Overall,
|
493 |
elif match_percentage >= 50:
|
494 |
-
assessment += f"With
|
495 |
else:
|
496 |
-
assessment += f"Significant
|
497 |
|
498 |
return assessment
|
499 |
|
|
|
315 |
#####################################
|
316 |
def analyze_google_fit(resume_summary):
|
317 |
"""
|
318 |
+
Analyze how well the candidate fits Google's requirements.
|
319 |
+
Only modifying the T5 prompt to get better expert assessments.
|
320 |
"""
|
321 |
+
# [Keep all the existing code for score calculation unchanged]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
322 |
|
323 |
+
# Get more specific information for a better prompt
|
324 |
+
# Get top skills across all categories (up to 5 total)
|
325 |
+
all_matching_skills = []
|
326 |
+
for category, matches in found_skills.items():
|
327 |
+
if matches:
|
328 |
+
all_matching_skills.extend(matches)
|
|
|
|
|
329 |
|
330 |
+
top_skills = list(set(all_matching_skills))[:5] # Remove duplicates and take top 5
|
331 |
+
skills_text = ", ".join(top_skills) if top_skills else "limited relevant skills"
|
332 |
|
333 |
+
# Get strongest and weakest categories for more specific feedback
|
334 |
+
categories_sorted = sorted(category_details.items(), key=lambda x: x[1]["adjusted_score"], reverse=True)
|
335 |
+
top_category = category_weights[categories_sorted[0][0]]["label"]
|
336 |
+
weak_category = category_weights[categories_sorted[-1][0]]["label"]
|
337 |
|
338 |
+
# Extract work experience highlights
|
339 |
+
experience_match = re.search(r'Previous Work Experience:.*?(?=\n\n|$)', resume_summary, re.DOTALL)
|
340 |
+
experience_text = experience_match.group(0) if experience_match else ""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
341 |
|
342 |
+
# Extract just 1-2 key experiences
|
343 |
+
experiences = re.findall(r'([A-Z][^.]*?company|[A-Z][^.]*?engineer|[A-Z][^.]*?developer|[A-Z][^.]*?Google|[A-Z][^.]*?Microsoft|[A-Z][^.]*?Amazon)', experience_text)
|
344 |
+
experience_highlights = ", ".join(experiences[:2]) if experiences else "work experience"
|
345 |
|
346 |
+
# Create a more specific prompt for T5 that focuses on detailed assessment
|
347 |
prompt = f"""
|
348 |
+
Generate Google candidate assessment.
|
349 |
+
Skills detected: {skills_text}.
|
350 |
+
Experience: {experience_highlights}.
|
351 |
+
Strongest area: {top_category} ({categories_sorted[0][1]["adjusted_score"]}%).
|
352 |
+
Weakest area: {weak_category} ({categories_sorted[-1][1]["adjusted_score"]}%).
|
353 |
+
Overall match: {match_percentage}%.
|
354 |
+
|
355 |
+
Write a detailed 3-4 sentence assessment for this Google applicant.
|
356 |
+
Start with "This candidate" and include:
|
357 |
+
1. Specific strengths relating to Google's needs
|
358 |
+
2. Technical skills evaluation
|
359 |
+
3. Areas for improvement
|
360 |
+
4. Overall Google fit assessment
|
361 |
+
|
362 |
+
This candidate
|
363 |
"""
|
364 |
|
365 |
try:
|
366 |
+
# Generate the assessment using T5
|
367 |
assessment_results = models['evaluator'](
|
368 |
prompt,
|
369 |
+
max_length=300,
|
370 |
do_sample=True,
|
371 |
+
temperature=0.7,
|
372 |
num_return_sequences=3
|
373 |
)
|
374 |
|
375 |
# Find the best response
|
376 |
+
best_assessment = None
|
377 |
for result in assessment_results:
|
378 |
text = result['generated_text'].strip()
|
379 |
|
380 |
+
# Clean up and check if valid
|
381 |
+
text = re.sub(r'Generate Google candidate assessment.*?Overall match:.*?%\.', '', text, flags=re.DOTALL)
|
382 |
+
text = re.sub(r'Write a detailed.*?This candidate', 'This candidate', text, flags=re.DOTALL)
|
383 |
|
384 |
+
# Check if it's a good response
|
385 |
+
if text.lower().startswith("this candidate") and len(text) > 100 and "1." not in text and "2." not in text:
|
386 |
+
best_assessment = text
|
387 |
break
|
388 |
|
389 |
+
# Use the best response or the first one if none were ideal
|
390 |
+
if best_assessment:
|
391 |
+
assessment = best_assessment
|
392 |
+
else:
|
393 |
+
# Use first response but clean it up
|
394 |
+
text = assessment_results[0]['generated_text']
|
395 |
+
text = re.sub(r'Generate Google candidate assessment.*?Overall match:.*?%\.', '', text, flags=re.DOTALL)
|
396 |
+
text = re.sub(r'Write a detailed.*?This candidate', 'This candidate', text, flags=re.DOTALL)
|
397 |
+
|
398 |
+
# Remove numbering if present
|
399 |
+
text = re.sub(r'\d\.\s', '', text)
|
400 |
+
|
401 |
+
if not text.lower().startswith("this candidate"):
|
402 |
+
text = "This candidate " + text
|
403 |
+
|
404 |
+
assessment = text
|
405 |
|
406 |
except Exception as e:
|
407 |
+
# Fall back to manual assessment
|
408 |
+
print(f"Error in T5 assessment generation: {e}")
|
409 |
+
assessment = f"""This candidate demonstrates some skills relevant to Google, particularly in {top_category}. Their experience with {skills_text} could be valuable, though they would benefit from strengthening their {weak_category}. Based on the resume analysis, they appear to be a {match_percentage}% match for Google's requirements."""
|
410 |
|
411 |
# Final cleanup
|
412 |
+
# Remove any remaining artifacts or formatting
|
413 |
+
assessment = re.sub(r'\n+', ' ', assessment)
|
414 |
+
assessment = re.sub(r'\s+', ' ', assessment)
|
415 |
assessment = assessment.strip()
|
|
|
|
|
416 |
|
417 |
+
# Make sure percentages are consistent
|
418 |
+
assessment = re.sub(r'\b\d{1,2}%\b', f"{match_percentage}%", assessment)
|
419 |
+
|
420 |
+
# [Keep the return statement and rest of function the same]
|
421 |
|
422 |
return assessment, match_percentage, category_details, execution_time
|
423 |
|
424 |
+
|
425 |
+
def generate_expert_assessment(resume_summary, match_percentage, category_details, found_skills):
|
426 |
"""
|
427 |
+
Generate a comprehensive expert assessment based on the resume analysis.
|
428 |
+
This is a specialized function to create high-quality, specific assessments.
|
429 |
"""
|
430 |
+
# Sort categories by score to identify top strengths and weaknesses
|
431 |
+
categories = list(category_details.keys())
|
432 |
+
categories.sort(key=lambda cat: category_details[cat]["adjusted_score"], reverse=True)
|
433 |
+
|
434 |
+
# Identify top strengths (top 2 categories)
|
435 |
+
top_strengths = categories[:2]
|
436 |
+
|
437 |
+
# Identify main weaknesses (bottom 2 categories, but only if score is below 50%)
|
438 |
+
weaknesses = [cat for cat in categories if category_details[cat]["adjusted_score"] < 50]
|
439 |
+
|
440 |
+
# Extract relevant skills for top strengths (up to 3 skills per strength)
|
441 |
+
strength_skills = []
|
442 |
+
for category in top_strengths:
|
443 |
+
matches = found_skills[category][:3] if found_skills[category] else []
|
444 |
+
strength_skills.extend(matches)
|
445 |
+
|
446 |
+
# Extract experience snippets from resume
|
447 |
+
experience_match = re.search(r'Previous Work Experience:(.*?)(?=\n\n|$)', resume_summary, re.DOTALL)
|
448 |
+
experience_text = experience_match.group(1) if experience_match else ""
|
449 |
+
|
450 |
+
# Find relevant company names or roles that might be impressive
|
451 |
+
company_pattern = r'\b(Google|Microsoft|Amazon|Apple|Facebook|Meta|Twitter|LinkedIn|Uber|Airbnb|Netflix|Oracle|IBM|Intel|Adobe|Salesforce)\b'
|
452 |
+
companies = re.findall(company_pattern, experience_text, re.IGNORECASE)
|
453 |
+
|
454 |
+
# Determine the expertise level based on score
|
455 |
+
if match_percentage >= 75:
|
456 |
+
expertise_level = "strong"
|
457 |
+
elif match_percentage >= 60:
|
458 |
+
expertise_level = "solid"
|
459 |
+
elif match_percentage >= 45:
|
460 |
+
expertise_level = "moderate"
|
461 |
else:
|
462 |
+
expertise_level = "limited"
|
463 |
+
|
464 |
+
# Start building assessment
|
465 |
+
assessment = f"This candidate demonstrates {expertise_level} potential for Google, with particular strengths in "
|
466 |
+
|
467 |
+
# Add strengths with specific skills
|
468 |
+
if top_strengths:
|
469 |
+
strength_labels = []
|
470 |
+
for strength in top_strengths:
|
471 |
+
label = {"technical_skills": "technical programming",
|
472 |
+
"advanced_tech": "advanced technology",
|
473 |
+
"problem_solving": "problem-solving",
|
474 |
+
"innovation": "innovation",
|
475 |
+
"soft_skills": "collaboration and leadership"}[strength]
|
476 |
+
strength_labels.append(label)
|
477 |
+
|
478 |
+
assessment += f"{' and '.join(strength_labels)}. "
|
479 |
+
|
480 |
+
# Add specific skills if available
|
481 |
+
if strength_skills:
|
482 |
+
assessment += f"Their experience with {', '.join(strength_skills[:4])} "
|
483 |
+
|
484 |
+
# Add relevance to Google
|
485 |
+
if any(skill in ['machine learning', 'ai', 'python', 'java', 'c++', 'cloud'] for skill in strength_skills):
|
486 |
+
assessment += "directly aligns with Google's technical requirements. "
|
487 |
+
else:
|
488 |
+
assessment += "is relevant to Google's technology stack. "
|
489 |
+
else:
|
490 |
+
assessment += "few areas that align closely with Google's requirements. "
|
491 |
|
492 |
+
# Add context from work experience if relevant companies found
|
493 |
+
if companies:
|
494 |
+
unique_companies = list(set([c.lower() for c in companies]))
|
495 |
+
if len(unique_companies) > 1:
|
496 |
+
assessment += f"Their experience at companies like {', '.join(unique_companies[:2])} provides valuable industry context. "
|
497 |
+
else:
|
498 |
+
assessment += f"Their experience at {unique_companies[0]} provides relevant industry context. "
|
499 |
|
500 |
+
# Add weaknesses and improvement suggestions
|
501 |
+
if weaknesses:
|
502 |
+
assessment += "However, to improve their candidacy, they should strengthen their "
|
503 |
+
|
504 |
+
weakness_labels = []
|
505 |
+
for weakness in weaknesses[:2]: # Only mention top 2 weaknesses
|
506 |
+
label = {"technical_skills": "technical programming skills",
|
507 |
+
"advanced_tech": "knowledge of advanced technologies",
|
508 |
+
"problem_solving": "problem-solving capabilities",
|
509 |
+
"innovation": "innovation mindset",
|
510 |
+
"soft_skills": "teamwork and collaboration abilities"}[weakness]
|
511 |
+
weakness_labels.append(label)
|
512 |
+
|
513 |
+
assessment += f"{' and '.join(weakness_labels)}, "
|
514 |
+
|
515 |
+
# Add specific improvement suggestion
|
516 |
+
if "technical_skills" in weaknesses:
|
517 |
+
assessment += "particularly by building projects with modern languages like Python, Java, or Go. "
|
518 |
+
elif "advanced_tech" in weaknesses:
|
519 |
+
assessment += "ideally by gaining exposure to machine learning, cloud systems, or distributed computing. "
|
520 |
+
elif "problem_solving" in weaknesses:
|
521 |
+
assessment += "by practicing algorithmic problems and system design challenges. "
|
522 |
+
elif "innovation" in weaknesses:
|
523 |
+
assessment += "through projects that demonstrate creative thinking and novel solutions. "
|
524 |
+
elif "soft_skills" in weaknesses:
|
525 |
+
assessment += "by highlighting collaborative projects and leadership experiences. "
|
526 |
+
|
527 |
+
# Add final evaluation with match percentage
|
528 |
if match_percentage >= 70:
|
529 |
+
assessment += f"Overall, this candidate shows good alignment with Google's culture of innovation and technical excellence, with a {match_percentage}% match to the company's requirements."
|
530 |
elif match_percentage >= 50:
|
531 |
+
assessment += f"With these improvements, the candidate could become more competitive for Google positions, currently showing a {match_percentage}% match to the company's requirements."
|
532 |
else:
|
533 |
+
assessment += f"Significant development in these areas would be needed before they could be considered a strong Google candidate, with a current match of {match_percentage}% to requirements."
|
534 |
|
535 |
return assessment
|
536 |
|