JAA-ATS-Tool / scripts /verify_phase4_canonical.py
saitejatirunagari's picture
feat(phase4): canonical Resume model + single locked visual format
0e70529
Raw
History Blame
7.52 kB
"""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)}')