| import re |
|
|
| PLACEHOLDER_PATTERNS = [ |
| r"company\s*name", r"\bname\s+of\s+company\b", |
| r"\byour\s+company\b", r"\[\w+\]", r"\bplaceholder\b", |
| r"\bcompany\b.*\bname\b", |
| ] |
|
|
| CLICHES = [ |
| "i am excited", "i am writing", "i am confident", "i would be a valuable addition", |
| "i am impressed by", "proven track record", "leverage my", "drive business growth", |
| "i believe", "i think", "i am passionate", "i am enthusiastic", |
| "i am eager", "i am thrilled", "it would be an honor", "i would love to", |
| "i am prepared", "i look forward to", |
| ] |
|
|
| THRESHOLDS = { |
| "achievement_density": 0.60, |
| "bullets_per_role": 3, |
| "min_skills": 5, |
| "summary_min_words": 30, |
| "education_required": False, |
| } |
|
|
|
|
| def _has_metric(text: str) -> bool: |
| return bool(re.search(r"\d+%|\$\d+|\d+[kKmMx×+]|\b\d+\b", text)) |
|
|
|
|
| def _check_achievement_density(experience: list) -> dict: |
| bullets = [] |
| for exp in experience: |
| bullets.extend(exp.get("achievements", [])) |
| if not bullets: |
| return {"pass": False, "score": 0.0, "reason": "No achievement bullets found"} |
| scored = sum(1 for b in bullets if _has_metric(b)) |
| ratio = scored / len(bullets) |
| return { |
| "pass": ratio >= THRESHOLDS["achievement_density"], |
| "score": ratio, |
| "reason": f"{scored}/{len(bullets)} bullets have metrics ({ratio:.0%})" |
| } |
|
|
|
|
| def _check_bullet_depth(experience: list) -> dict: |
| if not experience: |
| return {"pass": False, "score": 0, "reason": "No experience entries"} |
| counts = [len(e.get("achievements", [])) for e in experience] |
| avg = sum(counts) / len(counts) |
| return { |
| "pass": avg >= THRESHOLDS["bullets_per_role"], |
| "score": avg, |
| "reason": f"Average {avg:.1f} bullets per role (need {THRESHOLDS['bullets_per_role']})" |
| } |
|
|
|
|
| def _check_skills(skills: list) -> dict: |
| if not skills: |
| return {"pass": False, "score": 0, "reason": "No skills"} |
| if isinstance(skills[0], str): |
| count = len(skills) |
| return { |
| "pass": count >= THRESHOLDS["min_skills"], |
| "score": count, |
| "reason": f"{count} skills listed (need {THRESHOLDS['min_skills']})" |
| } |
| if isinstance(skills[0], dict): |
| all_items = [] |
| has_all_descriptions = True |
| for g in skills: |
| items = g.get("items", []) |
| all_items.extend(items) |
| if not g.get("description", ""): |
| has_all_descriptions = False |
| count = len(all_items) |
| return { |
| "pass": count >= THRESHOLDS["min_skills"] and has_all_descriptions, |
| "score": count, |
| "reason": f"{count} skill items across {len(skills)} groups" + ("" if has_all_descriptions else " — missing descriptions") |
| } |
| return {"pass": False, "score": 0, "reason": "Unknown skills format"} |
|
|
|
|
| def _check_summary(summary: str) -> dict: |
| words = len(summary.split()) if summary else 0 |
| return { |
| "pass": words >= THRESHOLDS["summary_min_words"], |
| "score": words, |
| "reason": f"{words} words (need {THRESHOLDS['summary_min_words']})" |
| } |
|
|
|
|
| def _check_cover_letter(cover_letter: str) -> dict: |
| if not cover_letter: |
| return {"pass": False, "score": 0, "reason": "Missing cover letter"} |
| lower = cover_letter.lower() |
| for pat in PLACEHOLDER_PATTERNS: |
| if re.search(pat, lower): |
| return {"pass": False, "score": 0, "reason": f"Placeholder text detected: '{pat}'"} |
| cliches_found = [c for c in CLICHES if c in lower] |
| if cliches_found: |
| return { |
| "pass": False, |
| "score": 0, |
| "reason": f"Cliche phrases found: {', '.join(cliches_found[:3])}" |
| } |
| words = len(cover_letter.split()) |
| if words < 50: |
| return {"pass": False, "score": words, "reason": f"Only {words} words (need 50+)"} |
| return {"pass": True, "score": words, "reason": f"{words} words, no cliches"} |
|
|
|
|
| def _check_education(education: list) -> dict: |
| has = bool(education) |
| return { |
| "pass": not THRESHOLDS["education_required"] or has, |
| "score": 1 if has else 0, |
| "reason": "Present" if has else "Missing — required" |
| } |
|
|
|
|
| CHECKERS = [ |
| ("achievement_density", _check_achievement_density, "experience"), |
| ("bullet_depth", _check_bullet_depth, "experience"), |
| ("skills", _check_skills, "skills"), |
| ("summary", _check_summary, "professional_summary"), |
| ("education", _check_education, "education"), |
| ("cover_letter", _check_cover_letter, "cover_letter"), |
| ] |
|
|
|
|
| def score_cv_quality(tailored_cv: dict) -> dict: |
| scores = {} |
| feedback_parts = [] |
| all_pass = True |
|
|
| for name, checker, var_name in CHECKERS: |
| value = tailored_cv.get(var_name, []) |
| result = checker(value) |
| scores[name] = {"pass": result["pass"], "score": result["score"], "reason": result["reason"]} |
| if not result["pass"]: |
| all_pass = False |
| feedback_parts.append(f"- {result['reason']}") |
|
|
| feedback = "\n".join(feedback_parts) if feedback_parts else "All quality checks passed." |
| return {"pass": all_pass, "scores": scores, "feedback": feedback} |
|
|