Spaces:
Sleeping
Sleeping
File size: 20,629 Bytes
dc95236 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 | #what is specifically wrong in my resume?
import re
from typing import List, Dict, Any, Optional
from backend.models.schemas import IssueDetail
def analyze_issues(
resume_text: str,
parsed_resume: Dict,
skills: List[str],
projects: List[Dict],
action_verbs: List[str],
skill_validation: Dict,
scores: Dict,
contact_info: Optional[Dict]=None,
) -> List[IssueDetail]:
detected: List[IssueDetail]=[]
#Unpack frequently-used structured fields once at the top
exp_entries = [e for e in parsed_resume.get('experience', []) if isinstance(e, dict)]
edu_entries = [e for e in parsed_resume.get('education', []) if isinstance(e, dict)]
proj_entries = [p for p in parsed_resume.get('projects', []) if isinstance(p, dict)]
summary = (parsed_resume.get('professional_summary') or '').strip() # Handle None to prevent string concatenation errors
#Build a combined experience text for regex-based checks that still need text
experience_text = '\n'.join(e.get('description', '') for e in exp_entries).strip()
#1. missig project section
resume_lower = resume_text.lower()
has_projects_signal = any(kw in resume_lower for kw in [
'project', 'github.com', 'deployed', 'built a', 'developed a',
'created a', 'implemented a', 'live demo', 'tech stack',
])
if not proj_entries and len(projects) == 0 and not has_projects_signal:
detected.append(IssueDetail(
issue_title="Missing Projects Section",
severity_level="High",
ats_impact="High",
explanation=(
"Your resume does not have a dedicated Projects section. "
"ATS systems and recruiters look for concrete projects to validate "
"that your listed skills have been applied in practice."
),
where_it_appears="Resume structure β no 'Projects' header was detected",
how_to_fix=(
"Add a 'Projects' section with 2β3 significant projects. "
"For each project, include the title, technologies used, "
"what you built, and a measurable outcome."
),
action_items=[
"Add a 'PROJECTS' section heading after your Experience section",
"Include 2β3 projects (personal, academic, or open-source)",
"For each project: write the title, tech stack used, what you built, and a measurable result",
"Example: 'E-Commerce Platform β React, Node.js, MongoDB. Handled 500+ transactions/month'",
"Link to GitHub or a live demo if available (e.g., github.com/yourname/project)",
],
example_improvement=(
"Add:\n"
"PROJECTS\n"
"β’ E-Commerce Platform β Built a full-stack shopping site using "
"React, Node.js, and MongoDB. Implemented payment processing with "
"Stripe, handling 500+ transactions/month.\n"
"β’ ML Sentiment Analyzer β Trained a BERT model on 10K reviews "
"achieving 92% accuracy. Deployed as a REST API with FastAPI."
),
))
#2. missing experience section
has_experience_signal = any(kw in resume_lower for kw in [
'intern', 'internship', 'employed', 'worked at', 'working at',
'company', 'organization', 'job', 'role', 'position', 'designation',
'manager', 'engineer', 'developer', 'analyst', 'consultant',
])
if not exp_entries and not has_experience_signal:
detected.append(IssueDetail(
issue_title="Missing Work Experience Section",
severity_level="High",
ats_impact="High",
explanation=(
"No work experience section was detected. Even for freshers, "
"internships, freelance work, or volunteer experience help "
"demonstrate professional capability."
),
where_it_appears="Resume structure β no 'Experience' or 'Work History' header found",
how_to_fix=(
"Add an 'Experience' section. If you lack formal employment, "
"include internships, freelance projects, open-source contributions, "
"or relevant volunteer work."
),
action_items=[
"Add an 'EXPERIENCE' or 'INTERNSHIPS' section to your resume",
"List each role with: Job Title β Company Name (Month Year β Month Year)",
"Add 2β4 bullet points per role describing what you did and its impact",
"If no formal job: include internships, freelance work, open-source, or college clubs",
"Start every bullet with a past-tense action verb (Developed, Built, Led, etc.)",
],
example_improvement=(
"Add:\n"
"EXPERIENCE\n"
"Software Engineering Intern β XYZ Corp (Jun 2025 β Aug 2025)\n"
"β’ Developed REST APIs with FastAPI serving 10K requests/day\n"
"β’ Reduced page load time by 40% through caching optimization"
),
))
#3. Missing Education Section
has_education_signal = any(kw in resume_lower for kw in [
'b.tech', 'btech', 'b.e.', 'b.sc', 'bsc', 'm.tech', 'mtech', 'm.sc',
'bachelor', 'master', 'phd', 'university', 'college',
'institute of technology', 'cgpa', 'gpa', 'graduated', 'diploma',
'class of', '20', 'batch of',
])
if not edu_entries and not has_education_signal:
detected.append(IssueDetail(
issue_title="Missing Education Section",
severity_level="Moderate",
ats_impact="Medium",
explanation=(
"No education section was found. Most ATS systems expect an "
"Education section with at least your degree and institution."
),
where_it_appears="Resume structure β no 'Education' header detected",
how_to_fix=(
"Add an 'Education' section listing your degree, university, "
"graduation year, and optionally your GPA or relevant coursework."
),
action_items=[
"Add an 'EDUCATION' section (usually at the bottom for experienced candidates, top for freshers)",
"Include: Degree name β Institution name (Start Year β End Year)",
"Add your CGPA or percentage if it is 7.0+ or 70%+",
"Optionally list 3β5 relevant courses (e.g., Data Structures, DBMS, ML)",
],
example_improvement=(
"Add:\n"
"EDUCATION\n"
"B.Tech in Computer Science β IIT Delhi (2021β2025)\n"
"CGPA: 8.5 | Relevant Coursework: Data Structures, ML, DBMS"
),
))
#4. Missing Skills Section
if not parsed_resume.get('skills') and len(skills) < 3: # only flag if extraction also found very few skills
detected.append(IssueDetail(
issue_title="Missing or Weak Skills Section",
severity_level="High",
ats_impact="High",
explanation=(
f"Only {len(skills)} skill(s) were detected. ATS systems "
"rely heavily on keyword matching from a dedicated Skills section. "
"Without clear skills listed, your resume may fail automated filters."
),
where_it_appears="Skills section β either missing or contains very few items",
how_to_fix=(
"Add a clear 'Skills' section organized by category. "
"Include programming languages, frameworks, tools, and soft skills "
"that match your target role."
),
action_items=[
"Add a 'TECHNICAL SKILLS' or 'SKILLS' section",
"Organize by category: Languages, Frameworks, Tools, Databases, Cloud",
"List at least 10β15 skills relevant to your target role",
"Use the exact names that appear in job descriptions (e.g., 'React.js' not just 'React')",
"Include tools you use daily: Git, VS Code, Postman, Docker, etc.",
],
example_improvement=(
"Add:\n"
"TECHNICAL SKILLS\n"
"Languages: Python, JavaScript, TypeScript, SQL\n"
"Frameworks: React, FastAPI, Django, Express.js\n"
"Tools: Docker, Git, AWS, PostgreSQL, MongoDB"
),
))
# 5. Skills Lack Supporting Evidence
unvalidated = skill_validation.get('unvalidated_skills', [])
validated = skill_validation.get('validated_skills', [])
total_skills = len(unvalidated) + len(validated)
if total_skills > 0 and len(unvalidated) > len(validated):
unsupported_list = ', '.join(unvalidated[:8])
pct_unsupported = round((len(unvalidated) / total_skills) * 100)
action_items_skills = [
f"Mention '{skill}' in a project or experience bullet point" for skill in unvalidated[:5]
]
action_items_skills.append("Remove skills you cannot demonstrate with any project or experience")
if len(unvalidated) > 5:
action_items_skills.append(f"({len(unvalidated) - 5} more unvalidated skills β review each one)")
detected.append(IssueDetail(
issue_title="Most Skills Lack Supporting Evidence",
severity_level="Moderate",
ats_impact="High",
explanation=(
f"{pct_unsupported}% of your listed skills ({len(unvalidated)} out of "
f"{total_skills}) are not backed by any mention in your projects or "
"experience sections. Recruiters and ATS systems cross-reference "
"skills against actual work to verify credibility."
),
where_it_appears=f"These skills have no supporting context: {unsupported_list}",
how_to_fix=(
"For each skill in your Skills section, ensure it appears at least "
"once in a project description or experience bullet point. "
"Describe how and where you used that technology."
),
action_items=action_items_skills,
example_improvement=(
f"Your skill '{unvalidated[0]}' has no supporting evidence.\n\n"
f"Fix: Add to a project or experience bullet:\n"
f"'Built a data pipeline using {unvalidated[0]} that processed "
"10K records daily, reducing manual effort by 60%.'"
),
))
#6. Weak Action Verbs
description_lines = [
line.strip()
for exp in exp_entries
for line in exp.get('description', '').split('\n')
if line.strip()
]
if len(description_lines) > 3 and len(action_verbs) < 3:
detected.append(IssueDetail(
issue_title="Bullet Points Lack Strong Action Verbs",
severity_level="Moderate",
ats_impact="Medium",
explanation=(
f"Your experience section has {len(description_lines)} bullet points but "
f"only {len(action_verbs)} start with strong action verbs. "
"ATS systems and recruiters favor bullets that begin with verbs like "
"'Developed', 'Implemented', 'Designed', 'Optimized'."
),
where_it_appears="Experience section β bullet point openings",
how_to_fix=(
"Start every bullet point with a past-tense action verb. "
"Avoid starting with 'Responsible for', 'Worked on', or 'Helped with'. "
"Use verbs like: Developed, Built, Designed, Implemented, Led, Automated, Optimized."
),
action_items=[
"Rewrite every bullet that starts with 'Responsible for', 'Helped', 'Worked on', or 'Assisted'",
"Use: Developed, Built, Designed, Implemented, Led, Automated, Optimized, Deployed, Reduced, Increased",
"Make verbs past-tense for previous roles, present-tense for current role",
f"Review all {len(description_lines)} bullet points β at least {len(description_lines)} should start with action verbs",
"Avoid weak openers like 'Was responsible for...' or 'Involved in...'",
],
example_improvement=(
"Before:\n"
"β’ Responsible for building the backend\n"
"β’ Worked on the payment feature\n\n"
"After:\n"
"β’ Developed a REST API with FastAPI handling 5K daily requests\n"
"β’ Implemented Stripe payment integration reducing checkout time by 30%"
),
))
#7. No Quantifiable Achievements
number_pattern = r'\d+[%+]?|\$\d+'
has_metrics = bool(re.findall(number_pattern, experience_text)) if experience_text else False
if experience_text and not has_metrics:
detected.append(IssueDetail(
issue_title="No Quantifiable Achievements Found",
severity_level="Moderate",
ats_impact="Medium",
explanation=(
"Your experience section does not contain any measurable outcomes "
"(numbers, percentages, or dollar amounts). Quantified achievements "
"make your impact concrete and are strongly preferred by recruiters."
),
where_it_appears="Experience section β bullet point content",
how_to_fix=(
"Add numbers to at least 50% of your bullet points. "
"Include metrics like: users served, response time improved, "
"revenue generated, lines of code, team size, etc."
),
action_items=[
"Go through each bullet point and ask: 'How much?', 'How many?', 'By what %?'",
"Add metrics: users served, requests/day, % improvement, team size, time saved",
"Examples: '500+ daily users', 'reduced load time by 40%', 'handled 10K API calls/day'",
"If exact numbers aren't known, use reasonable estimates (e.g., '~200 users')",
"Aim for numbers in at least 50% of your experience bullets",
],
example_improvement=(
"Before:\n"
"β’ Improved application performance\n"
"β’ Managed a team of developers\n\n"
"After:\n"
"β’ Improved API response time by 45% through Redis caching\n"
"β’ Led a team of 5 developers delivering 3 features per sprint"
),
))
#8. Missing Contact Information
if contact_info:
missing_contacts = []
if not contact_info.get('email'):
missing_contacts.append('email')
if not contact_info.get('phone'):
missing_contacts.append('phone number')
if not contact_info.get('linkedin'):
missing_contacts.append('LinkedIn URL')
if len(missing_contacts) >= 2:
contact_action_items = [f"Add your {item} to the header section" for item in missing_contacts]
contact_action_items += [
"Format the contact line as: email | phone | linkedin | github",
"Make sure your LinkedIn URL is a custom short URL (linkedin.com/in/yourname)",
"Add a GitHub link if you have public projects (github.com/yourname)",
]
detected.append(IssueDetail(
issue_title="Incomplete Contact Information",
severity_level="High",
ats_impact="High",
explanation=(
f"Your resume is missing: {', '.join(missing_contacts)}. "
"Recruiters need reliable ways to reach you. Missing contact "
"details can cause your application to be skipped entirely."
),
where_it_appears="Header / Contact section at the top of the resume",
how_to_fix=(
"Add your full name, email, phone number, LinkedIn profile, "
"and optionally a GitHub or portfolio link at the top of your resume."
),
action_items=contact_action_items,
example_improvement=(
"Add at top:\n"
"John Doe\n"
"john.doe@email.com | +91-9876543210\n"
"linkedin.com/in/johndoe | github.com/johndoe"
),
))
# 9. Low Formatting Score
formatting_score = scores.get('formatting_score', 20)
if formatting_score < 10:
detected.append(IssueDetail(
issue_title="Poor Resume Formatting",
severity_level="High",
ats_impact="High",
explanation=(
f"Your formatting score is {formatting_score}/20, which indicates "
"problems like missing section headers, inconsistent structure, "
"or non-standard layout that ATS parsers struggle with."
),
where_it_appears="Overall document structure and formatting",
how_to_fix=(
"Use a clean, single-column layout with standard section headers "
"(Experience, Education, Skills, Projects). Use consistent bullet "
"points, standard fonts, and avoid tables, columns, or graphics."
),
action_items=[
"Switch to a single-column layout (avoid two-column templates for ATS)",
"Use standard section headers: EXPERIENCE, EDUCATION, SKILLS, PROJECTS",
"Use bullet points (β’) consistently β don't mix with dashes or asterisks",
"Remove all tables, text boxes, headers/footers, and images β ATS cannot parse them",
"Use a standard font (Calibri, Arial, Times New Roman) at 10β12pt",
"Order sections: Contact β Summary β Experience β Projects β Education β Skills",
],
example_improvement=(
"Use this structure:\n"
"NAME & CONTACT\n"
"SUMMARY (2-3 lines)\n"
"EXPERIENCE (reverse chronological)\n"
"PROJECTS (2-3 key projects)\n"
"EDUCATION\n"
"SKILLS (categorized)"
),
))
#10. Missing Summary/Objective
if not summary:
detected.append(IssueDetail(
issue_title="Missing Professional Summary",
severity_level="Low",
ats_impact="Low",
explanation=(
"Your resume does not include a Professional Summary or Objective "
"section at the top. While not required, a 2-3 line summary helps "
"recruiters quickly understand your profile and target role."
),
where_it_appears="Top of resume β below contact info",
how_to_fix=(
"Add a 2-3 sentence summary highlighting your experience level, "
"key skills, and career focus. Tailor it to the job you're applying for."
),
action_items=[
"Add a 'PROFESSIONAL SUMMARY' or 'OBJECTIVE' section at the top of your resume",
"Write 2β3 sentences: who you are, your key skills, and what role you seek",
"Mention your years of experience or education level upfront",
"Tailor this section for each job application β reference the specific role",
"Keep it under 60 words β recruiters spend only 6 seconds on first scan",
],
example_improvement=(
"Add:\n"
"PROFESSIONAL SUMMARY\n"
"Full-stack developer with 2+ years of experience building scalable "
"web applications using React, Node.js, and AWS. Passionate about "
"clean architecture and performance optimization."
),
))
return detected
def generate_issues_summary(detected_issues: List[IssueDetail]) -> List[str]:
"""Extract issue titles to formulate the issues_summary list."""
return [issue.issue_title for issue in detected_issues]
|