ML_PRO / app.py
indhupamula's picture
Update app.py
a7d4e72 verified
import gradio as gr
import re
import numpy as np
import pandas as pd
from PyPDF2 import PdfReader
from docx import Document
from sentence_transformers import SentenceTransformer
from sklearn.metrics.pairwise import cosine_similarity
import spacy
from fpdf import FPDF
import subprocess
# ---------------------------
# Load SpaCy model (runtime download if needed)
# ---------------------------
try:
nlp = spacy.load("en_core_web_sm")
except OSError:
subprocess.run(["python", "-m", "spacy", "download", "en_core_web_sm"])
nlp = spacy.load("en_core_web_sm")
# Load sentence-transformers model
model = SentenceTransformer('all-MiniLM-L6-v2')
# ---------------------------
# Resume Parsing Functions
# ---------------------------
def extract_text_from_pdf(file):
try:
reader = PdfReader(file)
text = ""
for page in reader.pages:
text += page.extract_text() or ""
return text
except:
return ""
def extract_text_from_docx(file):
try:
doc = Document(file)
text = "\n".join([p.text for p in doc.paragraphs])
return text
except:
return ""
def extract_skills(jd_text):
skills = re.split(r"[,\n;]", jd_text)
return [s.strip() for s in skills if s.strip()]
def split_sections(resume_text):
sections = {"Education":"","Experience":"","Skills":""}
try:
edu = re.search(r'(Education|EDUCATION)(.*?)(Experience|EXPERIENCE|Skills|SKILLS|$)', resume_text, re.DOTALL)
exp = re.search(r'(Experience|EXPERIENCE)(.*?)(Skills|SKILLS|$)', resume_text, re.DOTALL)
skills = re.search(r'(Skills|SKILLS)(.*)', resume_text, re.DOTALL)
if edu: sections["Education"] = edu.group(2).strip()
if exp: sections["Experience"] = exp.group(2).strip()
if skills: sections["Skills"] = skills.group(2).strip()
except:
pass
return sections
def compute_scores(resume_text, jd_text, required_skills):
try:
present_skills = [kw for kw in required_skills if kw.lower() in resume_text.lower()]
keyword_score = len(present_skills)/max(len(required_skills),1)
res_vec = model.encode(resume_text)
jd_vec = model.encode(jd_text)
semantic_score = cosine_similarity([res_vec],[jd_vec])[0][0]
sections = split_sections(resume_text)
section_scores = {}
for sec, text in sections.items():
sec_present = [kw for kw in required_skills if kw.lower() in text.lower()]
section_scores[sec] = len(sec_present)/max(len(required_skills),1)
final_score = 0.6*keyword_score + 0.4*semantic_score
tips = [f"⚠️ Add '{skill}' to improve ATS match" for skill in required_skills if skill.lower() not in resume_text.lower()]
return final_score, keyword_score, semantic_score, section_scores, tips
except:
return 0,0,0,{"Education":0,"Experience":0,"Skills":0},[]
# ---------------------------
# CSV & PDF Export
# ---------------------------
def export_csv(df, filename="ats_report.csv"):
try:
df.to_csv(filename, index=False)
except:
pass
return filename
def export_pdf(df, filename="ats_report.pdf"):
try:
pdf = FPDF()
pdf.add_page()
pdf.set_font("Arial", size=12)
pdf.cell(200, 10, txt="ATS Resume Screening Report", ln=True, align="C")
pdf.ln(10)
for i, row in df.iterrows():
pdf.cell(200, 10, txt=f"JD {i+1}: {row['JD']}", ln=True)
pdf.cell(200, 10, txt=f"Final Score: {row['Final Score']}", ln=True)
pdf.cell(200, 10, txt=f"Keyword Score: {row['Keyword Score']}", ln=True)
pdf.cell(200, 10, txt=f"Semantic Score: {row['Semantic Score']}", ln=True)
pdf.cell(200, 10, txt="Section Scores:", ln=True)
pdf.multi_cell(0, 10, row["Section Scores"])
pdf.cell(200, 10, txt="Tips:", ln=True)
pdf.multi_cell(0, 10, row["Tips"])
pdf.ln(5)
pdf.output(filename)
except:
pass
return filename
# ---------------------------
# AI Resume Rewriter & Feedback
# ---------------------------
def ai_resume_rewriter(resume_text, jd_text):
required_skills = extract_skills(jd_text)
missing_skills = [skill for skill in required_skills if skill.lower() not in resume_text.lower()]
rewritten = resume_text
if missing_skills:
rewritten += "\n\n### Suggested Skills to Add:\n" + "\n".join([f"- {s}" for s in missing_skills])
return rewritten
skill_course_mapping = {
"Python": ["Complete 'Python for Everybody' on Coursera", "Try Python projects on GitHub"],
"Machine Learning": ["Take 'Machine Learning' by Andrew Ng on Coursera", "Kaggle ML competitions"],
"Deep Learning": ["DeepLearning.AI TensorFlow Developer Course", "Build neural network projects"],
"SQL": ["SQL for Data Science - Coursera", "Practice on LeetCode SQL problems"],
"AWS": ["AWS Certified Solutions Architect - Associate", "AWS Free Tier practice"],
"TensorFlow": ["TensorFlow in Practice Specialization - Coursera", "Hands-on DL projects"]
}
certification_mapping = {
"AWS": "AWS Certified Solutions Architect",
"ML": "Machine Learning by Andrew Ng",
"Python": "PCAP: Python Certified Associate Programmer",
"TensorFlow": "TensorFlow Developer Certificate"
}
def generate_feedback(resume_text, jd_text):
required_skills = extract_skills(jd_text)
resume_lower = resume_text.lower()
missing_skills = [skill for skill in required_skills if skill.lower() not in resume_lower]
skill_suggestions = [f"{s}: {', '.join(skill_course_mapping[s])}" for s in missing_skills if s in skill_course_mapping]
cert_suggestions = [f"Consider certification: {certification_mapping[s]}" for s in missing_skills if s in certification_mapping]
resume_tips = []
if "Education" not in resume_text:
resume_tips.append("Include an Education section if missing.")
if "Experience" not in resume_text:
resume_tips.append("Include an Experience section with quantified achievements.")
if "Skills" not in resume_text:
resume_tips.append("Add a Skills section highlighting relevant skills.")
if len(resume_text.split()) < 200:
resume_tips.append("Consider adding more details to increase resume length and content richness.")
feedback_text = "### Missing Skills:\n" + ("\n".join(missing_skills) if missing_skills else "None")
feedback_text += "\n\n### Suggested Courses:\n" + ("\n".join(skill_suggestions) if skill_suggestions else "No suggestions")
feedback_text += "\n\n### Suggested Certifications:\n" + ("\n".join(cert_suggestions) if cert_suggestions else "No suggestions")
feedback_text += "\n\n### Resume Optimization Tips:\n" + ("\n".join(resume_tips) if resume_tips else "Your resume looks well-structured.")
return feedback_text
# ---------------------------
# Multi-JD Analysis
# ---------------------------
def analyze_multi_jd(resume_file, jd_texts):
file_ext = resume_file.name.split('.')[-1].lower()
if file_ext == "pdf":
resume_text = extract_text_from_pdf(resume_file)
elif file_ext == "docx":
resume_text = extract_text_from_docx(resume_file)
else:
resume_text = ""
jd_list = [jd.strip() for jd in jd_texts.split("\n\n") if jd.strip()]
results = []
for jd in jd_list:
required_skills = extract_skills(jd)
final_score, keyword_score, semantic_score, section_scores, tips = compute_scores(resume_text, jd, required_skills)
section_scores_str = "\n".join([f"{k}: {v:.2%}" for k,v in section_scores.items()])
tips_str = "\n".join(tips) if tips else "No suggestions"
results.append({
"JD": jd[:50]+"..." if len(jd)>50 else jd,
"Final Score": f"{final_score:.2%}",
"Keyword Score": f"{keyword_score:.2%}",
"Semantic Score": f"{semantic_score:.2%}",
"Section Scores": section_scores_str,
"Tips": tips_str
})
df = pd.DataFrame(results)
export_csv(df)
export_pdf(df)
feedback = generate_feedback(resume_text, jd_texts)
rewritten_resume = ai_resume_rewriter(resume_text, jd_texts)
return "ats_report.csv", "ats_report.pdf", feedback, rewritten_resume
# ---------------------------
# Gradio Interface
# ---------------------------
iface = gr.Interface(
fn=analyze_multi_jd,
inputs=[
gr.File(label="Upload Resume (PDF/DOCX)"),
gr.Textbox(label="Paste Job Description(s) (Separate multiple JDs with double line breaks)", lines=10)
],
outputs=[
gr.File(label="Download CSV Report"),
gr.File(label="Download PDF Report"),
gr.Textbox(label="Personalized Feedback", lines=15),
gr.Textbox(label="AI Suggested Resume Revisions", lines=15)
],
title="AI-Powered Resume Screening System",
description="Upload your resume, paste job descriptions, and get ATS scoring, personalized feedback, and AI suggestions."
)
iface.launch()