Spaces:
Sleeping
Sleeping
File size: 7,517 Bytes
0e70529 | 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 | """Verify Phase 4 canonical flow on all 4 failing JDs with handcrafted v4 LLM responses."""
import os, sys, io, shutil, pdfplumber
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
# Make sure the source PDF is in the expected place
src_pdf = r'C:\Users\Nxtwave\Desktop\resume\Saiteja_Tirunagari_Resume A 26 - Copy.pdf'
dst_pdf = 'data/resume/resume.pdf'
os.makedirs(os.path.dirname(dst_pdf), exist_ok=True)
if not os.path.exists(dst_pdf):
shutil.copyfile(src_pdf, dst_pdf)
from src.resume_parser_v2 import parse_resume_pdf_cached
from src.resume_renderer import render_resume_docx
from src.resume_model import Resume, Role, Education, Contact
from src.ats_scorer import score_resume
from src.resume_customizer import ResumeCustomizer, _read_docx_text
# Load canonical resume
base = parse_resume_pdf_cached(dst_pdf)
print(f'Loaded canonical resume: {base.name}, {len(base.roles)} roles, {len(base.education)} edu entries')
def make_tailored_for(jd_text: str, role: str, company: str, kw_focus: list[str]) -> Resume:
"""Build a handcrafted tailored Resume for a JD, weaving focus keywords."""
# Recruiter pitch + summary
summary = (
f"Strong-fit candidate for {role} at {company}: 5+ years of PM experience "
f"directly applicable to {kw_focus[0] if kw_focus else 'this role'}. "
f"Owned end-to-end product modules from discovery through launch, authored PRDs, "
f"user stories, wireframes, and acceptance criteria, and partnered with engineering, "
f"design, and QA across sprints. Drove A/B testing on Mixpanel, Amplitude, and GA4 to "
f"track activation, adoption, retention, funnel conversion, and revenue impact. "
f"Hands-on with Jira and Figma; integrated CRM workflows via APIs and webhooks. "
f"{' '.join(['Familiar with ' + k + '.' for k in kw_focus[:3]])}"
)
# Tailored roles — pick 5-7 bullets per role, rewritten
roles = [
Role(
title=base.roles[0].title,
company=base.roles[0].company,
location=base.roles[0].location,
dates=base.roles[0].dates,
bullets=[
"Owned end-to-end product modules for the NIAT Application Portal, partnering with engineering, design, and QA across sprint planning and releases.",
"Authored PRDs, user stories, wireframes, and acceptance criteria for 8 cross-functional releases tracked via Jira and Figma.",
"Tracked activation, adoption, retention, funnel conversion, and revenue impact via Mixpanel, Amplitude, and GA4 dashboards integrated with CRM via APIs and webhooks.",
"Drove A/B experiments lifting payment conversion from 27.37% to 63.24% (+35.87 pp), scaling to 141,269 OTP-verified leads.",
"Translated business goals into roadmap items aligned with founders/leadership; established KPIs and prioritization frameworks.",
"Partnered with data science on AI chatbot productization; gained exposure to MLOps workflows and model validation pipelines.",
f"Worked adjacent to {', '.join(kw_focus[:2])} through cross-functional product integrations.",
],
),
Role(
title=base.roles[1].title,
company=base.roles[1].company,
location=base.roles[1].location,
dates=base.roles[1].dates,
bullets=[
"Managed 20 customer-success specialists covering 40,000 customers; maintained 95%+ satisfaction and <5% refund rate via SLA-driven production support.",
"Coordinated UAT with business stakeholders; documented test results and obtained formal sign-off before production deployment.",
"Led 0→1 product initiatives partnering with engineering on Xplore and Social Emotional Learning pilots.",
],
),
Role(
title=base.roles[2].title,
company=base.roles[2].company,
location=base.roles[2].location,
dates=base.roles[2].dates,
bullets=[
"Increased user retention by 8% by redesigning onboarding using UX research and customer empathy frameworks.",
"Conducted extensive A/B testing and user research to identify pain points and refine features.",
"Gathered and analysed user requirements through stakeholder interviews; produced FSDs and acceptance criteria.",
],
),
Role(
title=base.roles[3].title,
company=base.roles[3].company,
location=base.roles[3].location,
dates=base.roles[3].dates,
bullets=[
"Launched EdTech portfolio of 275 apps with 3M+ cumulative downloads; managed end-to-end IT projects from requirements gathering to production.",
"Drove user acquisition via Google Ads, LinkedIn, and paid social; established strategic partnerships and end-to-end P&L.",
"Built data-driven culture focused on conversion optimization, KPIs, and stakeholder alignment.",
],
),
]
achievements = [
"Scaled product funnel to 141,269 verified leads with ₹1,120+ Cr annual pipeline",
"Lifted A/B-tested payment conversion +35.87 pp via CRM-integrated nudges",
"Reduced OCR processing cost 97.5% (₹4 → ₹0.10/page) through automation",
"Generated 6,776 leads via AI-driven conversational chatbot",
]
return Resume(
name=base.name,
contact=base.contact,
summary=summary,
roles=roles,
achievements=achievements,
education=base.education,
)
# Test each of the 4 failing JDs
jd_focus = {
'airtel_pm': ('Product Manager', 'Airtel', ['MLOps', 'A/B testing']),
'sumo_logic_pm': ('Product Manager', 'Sumo Logic', ['SIEM', 'SOAR', 'threat detection']),
'edgeverve_pm': ('Product Manager', 'EdgeVerve', ['MLOps', 'foundation models', 'AI-First']),
'aditya_birla_apm': ('Associate Product Manager', 'Aditya Birla Capital', ['FSD', 'UAT', 'requirements elicitation']),
}
out_dir = 'data/output/resumes/_phase4_canonical_test'
os.makedirs(out_dir, exist_ok=True)
rc = ResumeCustomizer.__new__(ResumeCustomizer)
rc.resume_text = '' # not used in v4 path
print()
print('━' * 75)
print(f'{"JD":<48} {"Before":>7} {"After":>7} {"JD-match":>10} {"Pages":>6}')
print('━' * 75)
for jd_file, (role, company, focus) in jd_focus.items():
with open(f'tests/fixtures/jds/{jd_file}.txt', encoding='utf-8') as f:
jd = f.read()
tailored = make_tailored_for(jd, role, company, focus)
docx_path = os.path.join(out_dir, f'{company.replace(" ", "_")}_{jd_file}.docx')
render_resume_docx(tailored, docx_path)
# Score BEFORE injection
r_before = score_resume(_read_docx_text(docx_path), jd)
# Apply injection safety net
rc._inject_missing_keywords(docx_path, jd)
# Score AFTER injection
text = _read_docx_text(docx_path)
r = score_resume(text, jd)
# Get page count
from src.pdf_writer import docx_to_pdf
try:
pdf = docx_to_pdf(docx_path)
with pdfplumber.open(pdf) as p:
pages = len(p.pages)
except Exception:
pages = '?'
print(f'{(role + " @ " + company):<48} {r_before["ats_score"]:>6} {r["ats_score"]:>6} {r["matched_count"]}/{r["total_jd_kw"]:<3} {pages}')
print('━' * 75)
print(f'\nOutput at: {os.path.abspath(out_dir)}')
|