indhupamula commited on
Commit
e65743d
·
verified ·
1 Parent(s): 60bbb42

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +79 -96
app.py CHANGED
@@ -1,4 +1,5 @@
1
  import gradio as gr
 
2
  import re
3
  import numpy as np
4
  import pandas as pd
@@ -8,17 +9,22 @@ from sentence_transformers import SentenceTransformer
8
  from sklearn.metrics.pairwise import cosine_similarity
9
  import spacy
10
  from fpdf import FPDF
 
11
 
12
  # ---------------------------
13
  # Load SpaCy model
14
  # ---------------------------
15
- nlp = spacy.load("en_core_web_sm")
 
 
 
 
16
 
17
  # Load sentence-transformers model
18
  model = SentenceTransformer('all-MiniLM-L6-v2')
19
 
20
  # ---------------------------
21
- # Resume Parsing Functions
22
  # ---------------------------
23
  def extract_text_from_pdf(file):
24
  try:
@@ -27,8 +33,7 @@ def extract_text_from_pdf(file):
27
  for page in reader.pages:
28
  text += page.extract_text() or ""
29
  return text
30
- except Exception as e:
31
- print("Error reading PDF:", e)
32
  return ""
33
 
34
  def extract_text_from_docx(file):
@@ -36,8 +41,7 @@ def extract_text_from_docx(file):
36
  doc = Document(file)
37
  text = "\n".join([p.text for p in doc.paragraphs])
38
  return text
39
- except Exception as e:
40
- print("Error reading DOCX:", e)
41
  return ""
42
 
43
  def extract_skills(jd_text):
@@ -53,75 +57,61 @@ def split_sections(resume_text):
53
  if edu: sections["Education"] = edu.group(2).strip()
54
  if exp: sections["Experience"] = exp.group(2).strip()
55
  if skills: sections["Skills"] = skills.group(2).strip()
56
- except Exception as e:
57
- print("Error splitting sections:", e)
58
  return sections
59
 
60
  def compute_scores(resume_text, jd_text, required_skills):
61
- try:
62
- present_skills = [kw for kw in required_skills if kw.lower() in resume_text.lower()]
63
- keyword_score = len(present_skills)/max(len(required_skills),1)
64
- res_vec = model.encode(resume_text)
65
- jd_vec = model.encode(jd_text)
66
- semantic_score = cosine_similarity([res_vec],[jd_vec])[0][0]
67
- sections = split_sections(resume_text)
68
- section_scores = {}
69
- for sec, text in sections.items():
70
- sec_present = [kw for kw in required_skills if kw.lower() in text.lower()]
71
- section_scores[sec] = len(sec_present)/max(len(required_skills),1)
72
- final_score = 0.6*keyword_score + 0.4*semantic_score
73
- tips = [f"⚠️ Add '{skill}' to improve ATS match" for skill in required_skills if skill.lower() not in resume_text.lower()]
74
- return final_score, keyword_score, semantic_score, section_scores, tips
75
- except Exception as e:
76
- print("Error computing scores:", e)
77
- return 0,0,0,{"Education":0,"Experience":0,"Skills":0},[]
78
 
79
  # ---------------------------
80
  # CSV & PDF Export
81
  # ---------------------------
82
  def export_csv(df, filename="ats_report.csv"):
83
- try:
84
- df.to_csv(filename, index=False)
85
- except Exception as e:
86
- print("Error exporting CSV:", e)
87
  return filename
88
 
89
  def export_pdf(df, filename="ats_report.pdf"):
90
- try:
91
- pdf = FPDF()
92
- pdf.add_page()
93
- pdf.set_font("Arial", size=12)
94
- pdf.cell(200, 10, txt="ATS Resume Screening Report", ln=True, align="C")
95
- pdf.ln(10)
96
- for i, row in df.iterrows():
97
- pdf.cell(200, 10, txt=f"JD {i+1}: {row['JD']}", ln=True)
98
- pdf.cell(200, 10, txt=f"Final Score: {row['Final Score']}", ln=True)
99
- pdf.cell(200, 10, txt=f"Keyword Score: {row['Keyword Score']}", ln=True)
100
- pdf.cell(200, 10, txt=f"Semantic Score: {row['Semantic Score']}", ln=True)
101
- pdf.cell(200, 10, txt="Section Scores:", ln=True)
102
- pdf.multi_cell(0, 10, row["Section Scores"])
103
- pdf.cell(200, 10, txt="Tips:", ln=True)
104
- pdf.multi_cell(0, 10, row["Tips"])
105
- pdf.ln(5)
106
- pdf.output(filename)
107
- except Exception as e:
108
- print("Error exporting PDF:", e)
109
  return filename
110
 
111
  # ---------------------------
112
- # AI Resume Rewriter
113
  # ---------------------------
114
  def ai_resume_rewriter(resume_text, jd_text):
115
- try:
116
- required_skills = extract_skills(jd_text)
117
- missing_skills = [skill for skill in required_skills if skill.lower() not in resume_text.lower()]
118
- rewritten = resume_text
119
- if missing_skills:
120
- rewritten += "\n\n### Suggested Skills to Add:\n" + "\n".join([f"- {s}" for s in missing_skills])
121
- return rewritten
122
- except Exception as e:
123
- print("Error in AI rewriter:", e)
124
- return resume_text
125
 
126
  # ---------------------------
127
  # Feedback Generator
@@ -143,42 +133,32 @@ certification_mapping = {
143
  }
144
 
145
  def generate_feedback(resume_text, jd_text):
146
- try:
147
- required_skills = extract_skills(jd_text)
148
- resume_lower = resume_text.lower()
149
- missing_skills = [skill for skill in required_skills if skill.lower() not in resume_lower]
150
- skill_suggestions = [f"{s}: {', '.join(skill_course_mapping[s])}" for s in missing_skills if s in skill_course_mapping]
151
- cert_suggestions = [f"Consider certification: {certification_mapping[s]}" for s in missing_skills if s in certification_mapping]
152
- resume_tips = []
153
- if "Education" not in resume_text:
154
- resume_tips.append("Include an Education section if missing.")
155
- if "Experience" not in resume_text:
156
- resume_tips.append("Include an Experience section with quantified achievements.")
157
- if "Skills" not in resume_text:
158
- resume_tips.append("Add a Skills section highlighting relevant skills.")
159
- if len(resume_text.split()) < 200:
160
- resume_tips.append("Consider adding more details to increase resume length and content richness.")
161
- feedback_text = "### Missing Skills:\n" + ("\n".join(missing_skills) if missing_skills else "None")
162
- feedback_text += "\n\n### Suggested Courses:\n" + ("\n".join(skill_suggestions) if skill_suggestions else "No suggestions")
163
- feedback_text += "\n\n### Suggested Certifications:\n" + ("\n".join(cert_suggestions) if cert_suggestions else "No suggestions")
164
- feedback_text += "\n\n### Resume Optimization Tips:\n" + ("\n".join(resume_tips) if resume_tips else "Your resume looks well-structured.")
165
- return feedback_text
166
- except Exception as e:
167
- print("Error generating feedback:", e)
168
- return "Feedback unavailable."
169
 
170
  # ---------------------------
171
  # Multi-JD Analysis
172
  # ---------------------------
173
  def analyze_multi_jd(resume_file, jd_texts):
174
- file_ext = resume_file.name.split('.')[-1].lower()
175
- if file_ext == "pdf":
176
  resume_text = extract_text_from_pdf(resume_file)
177
- elif file_ext == "docx":
178
  resume_text = extract_text_from_docx(resume_file)
179
  else:
180
  resume_text = ""
181
-
182
  jd_list = [jd.strip() for jd in jd_texts.split("\n\n") if jd.strip()]
183
  results = []
184
  for jd in jd_list:
@@ -197,27 +177,30 @@ def analyze_multi_jd(resume_file, jd_texts):
197
  df = pd.DataFrame(results)
198
  export_csv(df)
199
  export_pdf(df)
200
- feedback = generate_feedback(resume_text, jd_texts)
201
- rewritten_resume = ai_resume_rewriter(resume_text, jd_texts)
202
- return "ats_report.csv", "ats_report.pdf", feedback, rewritten_resume
203
 
204
  # ---------------------------
205
  # Gradio Interface
206
  # ---------------------------
 
 
 
 
207
  iface = gr.Interface(
208
- fn=analyze_multi_jd,
209
  inputs=[
210
  gr.File(label="Upload Resume (PDF/DOCX)"),
211
- gr.Textbox(label="Paste Job Description(s) (Separate multiple JDs with double line breaks)", lines=10)
212
  ],
213
  outputs=[
214
  gr.File(label="Download CSV Report"),
215
  gr.File(label="Download PDF Report"),
216
- gr.Textbox(label="Personalized Feedback", lines=15),
217
- gr.Textbox(label="AI Suggested Resume Revisions", lines=15)
218
  ],
219
- title="AI-Powered Resume Screening System",
220
- description="Upload your resume, paste job descriptions, and get ATS scoring, personalized feedback, and AI suggestions."
221
  )
222
 
223
- iface.launch()
 
 
1
  import gradio as gr
2
+ import sqlite3
3
  import re
4
  import numpy as np
5
  import pandas as pd
 
9
  from sklearn.metrics.pairwise import cosine_similarity
10
  import spacy
11
  from fpdf import FPDF
12
+ import subprocess
13
 
14
  # ---------------------------
15
  # Load SpaCy model
16
  # ---------------------------
17
+ try:
18
+ nlp = spacy.load("en_core_web_sm")
19
+ except OSError:
20
+ subprocess.run(["python", "-m", "spacy", "download", "en_core_web_sm"])
21
+ nlp = spacy.load("en_core_web_sm")
22
 
23
  # Load sentence-transformers model
24
  model = SentenceTransformer('all-MiniLM-L6-v2')
25
 
26
  # ---------------------------
27
+ # Resume Parsing
28
  # ---------------------------
29
  def extract_text_from_pdf(file):
30
  try:
 
33
  for page in reader.pages:
34
  text += page.extract_text() or ""
35
  return text
36
+ except:
 
37
  return ""
38
 
39
  def extract_text_from_docx(file):
 
41
  doc = Document(file)
42
  text = "\n".join([p.text for p in doc.paragraphs])
43
  return text
44
+ except:
 
45
  return ""
46
 
47
  def extract_skills(jd_text):
 
57
  if edu: sections["Education"] = edu.group(2).strip()
58
  if exp: sections["Experience"] = exp.group(2).strip()
59
  if skills: sections["Skills"] = skills.group(2).strip()
60
+ except:
61
+ pass
62
  return sections
63
 
64
  def compute_scores(resume_text, jd_text, required_skills):
65
+ present_skills = [kw for kw in required_skills if kw.lower() in resume_text.lower()]
66
+ keyword_score = len(present_skills)/max(len(required_skills),1)
67
+ res_vec = model.encode(resume_text)
68
+ jd_vec = model.encode(jd_text)
69
+ semantic_score = cosine_similarity([res_vec],[jd_vec])[0][0]
70
+ sections = split_sections(resume_text)
71
+ section_scores = {}
72
+ for sec, text in sections.items():
73
+ sec_present = [kw for kw in required_skills if kw.lower() in text.lower()]
74
+ section_scores[sec] = len(sec_present)/max(len(required_skills),1)
75
+ final_score = 0.6*keyword_score + 0.4*semantic_score
76
+ tips = [f"⚠️ Add '{skill}' to improve ATS match" for skill in required_skills if skill.lower() not in resume_text.lower()]
77
+ return final_score, keyword_score, semantic_score, section_scores, tips
 
 
 
 
78
 
79
  # ---------------------------
80
  # CSV & PDF Export
81
  # ---------------------------
82
  def export_csv(df, filename="ats_report.csv"):
83
+ df.to_csv(filename, index=False)
 
 
 
84
  return filename
85
 
86
  def export_pdf(df, filename="ats_report.pdf"):
87
+ pdf = FPDF()
88
+ pdf.add_page()
89
+ pdf.set_font("Arial", size=12)
90
+ pdf.cell(200, 10, txt="ATS Resume Screening Report", ln=True, align="C")
91
+ pdf.ln(10)
92
+ for i, row in df.iterrows():
93
+ pdf.cell(200, 10, txt=f"JD {i+1}: {row['JD']}", ln=True)
94
+ pdf.cell(200, 10, txt=f"Final Score: {row['Final Score']}", ln=True)
95
+ pdf.cell(200, 10, txt=f"Keyword Score: {row['Keyword Score']}", ln=True)
96
+ pdf.cell(200, 10, txt=f"Semantic Score: {row['Semantic Score']}", ln=True)
97
+ pdf.cell(200, 10, txt="Section Scores:", ln=True)
98
+ pdf.multi_cell(0, 10, row["Section Scores"])
99
+ pdf.cell(200, 10, txt="Tips:", ln=True)
100
+ pdf.multi_cell(0, 10, row["Tips"])
101
+ pdf.ln(5)
102
+ pdf.output(filename)
 
 
 
103
  return filename
104
 
105
  # ---------------------------
106
+ # AI Resume Rewriter (Simple)
107
  # ---------------------------
108
  def ai_resume_rewriter(resume_text, jd_text):
109
+ required_skills = extract_skills(jd_text)
110
+ rewritten = resume_text
111
+ for skill in required_skills:
112
+ if skill.lower() not in resume_text.lower():
113
+ rewritten += f"\n- Experience with {skill}"
114
+ return rewritten
 
 
 
 
115
 
116
  # ---------------------------
117
  # Feedback Generator
 
133
  }
134
 
135
  def generate_feedback(resume_text, jd_text):
136
+ required_skills = extract_skills(jd_text)
137
+ resume_lower = resume_text.lower()
138
+ missing_skills = [skill for skill in required_skills if skill.lower() not in resume_lower]
139
+ skill_suggestions = [f"{s}: {', '.join(skill_course_mapping[s])}" for s in missing_skills if s in skill_course_mapping]
140
+ cert_suggestions = [f"Consider certification: {certification_mapping[s]}" for s in missing_skills if s in certification_mapping]
141
+ resume_tips = []
142
+ if "Education" not in resume_text: resume_tips.append("Include an Education section if missing.")
143
+ if "Experience" not in resume_text: resume_tips.append("Include an Experience section with quantified achievements.")
144
+ if "Skills" not in resume_text: resume_tips.append("Add a Skills section highlighting relevant skills.")
145
+ if len(resume_text.split()) < 200: resume_tips.append("Consider adding more details to increase resume length and content richness.")
146
+ feedback_text = "### Missing Skills:\n" + ("\n".join(missing_skills) if missing_skills else "None")
147
+ feedback_text += "\n\n### Suggested Courses:\n" + ("\n".join(skill_suggestions) if skill_suggestions else "No suggestions")
148
+ feedback_text += "\n\n### Suggested Certifications:\n" + ("\n".join(cert_suggestions) if cert_suggestions else "No suggestions")
149
+ feedback_text += "\n\n### Resume Optimization Tips:\n" + ("\n".join(resume_tips) if resume_tips else "Your resume looks well-structured.")
150
+ return feedback_text
 
 
 
 
 
 
 
 
151
 
152
  # ---------------------------
153
  # Multi-JD Analysis
154
  # ---------------------------
155
  def analyze_multi_jd(resume_file, jd_texts):
156
+ if resume_file.name.endswith(".pdf"):
 
157
  resume_text = extract_text_from_pdf(resume_file)
158
+ elif resume_file.name.endswith(".docx"):
159
  resume_text = extract_text_from_docx(resume_file)
160
  else:
161
  resume_text = ""
 
162
  jd_list = [jd.strip() for jd in jd_texts.split("\n\n") if jd.strip()]
163
  results = []
164
  for jd in jd_list:
 
177
  df = pd.DataFrame(results)
178
  export_csv(df)
179
  export_pdf(df)
180
+ return df, generate_feedback(resume_text, jd_texts), ai_resume_rewriter(resume_text, jd_texts)
 
 
181
 
182
  # ---------------------------
183
  # Gradio Interface
184
  # ---------------------------
185
+ def analyze_gradio(resume_file, jd_text):
186
+ df, feedback, rewritten_resume = analyze_multi_jd(resume_file, jd_text)
187
+ return "ats_report.csv", "ats_report.pdf", feedback, rewritten_resume
188
+
189
  iface = gr.Interface(
190
+ fn=analyze_gradio,
191
  inputs=[
192
  gr.File(label="Upload Resume (PDF/DOCX)"),
193
+ gr.Textbox(label="Paste Job Description(s) (Separate multiple JDs with double newline)")
194
  ],
195
  outputs=[
196
  gr.File(label="Download CSV Report"),
197
  gr.File(label="Download PDF Report"),
198
+ gr.Textbox(label="AI Feedback"),
199
+ gr.Textbox(label="AI Rewritten Resume")
200
  ],
201
+ title="AI Resume Screening & Optimization",
202
+ description="Upload resume and job description(s) to get ATS scores, section scores, AI feedback, and rewritten resume."
203
  )
204
 
205
+ if __name__ == "__main__":
206
+ iface.launch()