indhupamula commited on
Commit
8adefa9
·
verified ·
1 Parent(s): 8c1299d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +54 -141
app.py CHANGED
@@ -1,5 +1,4 @@
1
  import gradio as gr
2
- import sqlite3
3
  import re
4
  import numpy as np
5
  import pandas as pd
@@ -9,86 +8,17 @@ from sentence_transformers import SentenceTransformer
9
  from sklearn.metrics.pairwise import cosine_similarity
10
  import spacy
11
  from fpdf import FPDF
12
- import hashlib
13
- import subprocess
14
 
15
  # ---------------------------
16
  # Load SpaCy model
17
  # ---------------------------
18
- try:
19
- nlp = spacy.load("en_core_web_sm")
20
- except OSError:
21
- subprocess.run(["python", "-m", "spacy", "download", "en_core_web_sm"])
22
- nlp = spacy.load("en_core_web_sm")
23
 
24
  # Load sentence-transformers model
25
  model = SentenceTransformer('all-MiniLM-L6-v2')
26
 
27
  # ---------------------------
28
- # SQLite DB setup
29
- # ---------------------------
30
- conn = sqlite3.connect('resumes.db', check_same_thread=False)
31
- cursor = conn.cursor()
32
-
33
- cursor.execute("""
34
- CREATE TABLE IF NOT EXISTS users (
35
- id INTEGER PRIMARY KEY,
36
- username TEXT UNIQUE,
37
- password_hash TEXT
38
- )
39
- """)
40
-
41
- cursor.execute("""
42
- CREATE TABLE IF NOT EXISTS analyses (
43
- id INTEGER PRIMARY KEY,
44
- user_id INTEGER,
45
- resume_text TEXT,
46
- jd_text TEXT,
47
- final_score REAL,
48
- keyword_score REAL,
49
- semantic_score REAL,
50
- section_scores TEXT,
51
- tips TEXT,
52
- date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
53
- FOREIGN KEY(user_id) REFERENCES users(id)
54
- )
55
- """)
56
- conn.commit()
57
-
58
- # ---------------------------
59
- # Authentication Functions
60
- # ---------------------------
61
- def hash_password(password):
62
- return hashlib.sha256(password.encode()).hexdigest()
63
-
64
- def is_valid_username(username):
65
- return re.match(r'^\w+$', username) is not None
66
-
67
- def signup(username, password):
68
- username = username.strip()
69
- password = password.strip()
70
- if not is_valid_username(username):
71
- return "❌ Invalid username. Use only letters, numbers, underscore."
72
- try:
73
- cursor.execute("INSERT INTO users (username, password_hash) VALUES (?,?)",
74
- (username, hash_password(password)))
75
- conn.commit()
76
- return "✅ Signup successful! Please login."
77
- except sqlite3.IntegrityError:
78
- return "❌ Username already exists. Try a different one."
79
-
80
- def login(username, password):
81
- username = username.strip()
82
- password = password.strip()
83
- cursor.execute("SELECT id, password_hash FROM users WHERE username=?", (username,))
84
- row = cursor.fetchone()
85
- if row and row[1] == hash_password(password):
86
- return f"✅ Login successful! User ID: {row[0]}", row[0]
87
- else:
88
- return "❌ Invalid username or password", None
89
-
90
- # ---------------------------
91
- # Resume Parsing
92
  # ---------------------------
93
  def extract_text_from_pdf(file):
94
  try:
@@ -97,7 +27,8 @@ def extract_text_from_pdf(file):
97
  for page in reader.pages:
98
  text += page.extract_text() or ""
99
  return text
100
- except:
 
101
  return ""
102
 
103
  def extract_text_from_docx(file):
@@ -105,7 +36,8 @@ def extract_text_from_docx(file):
105
  doc = Document(file)
106
  text = "\n".join([p.text for p in doc.paragraphs])
107
  return text
108
- except:
 
109
  return ""
110
 
111
  def extract_skills(jd_text):
@@ -121,8 +53,8 @@ def split_sections(resume_text):
121
  if edu: sections["Education"] = edu.group(2).strip()
122
  if exp: sections["Experience"] = exp.group(2).strip()
123
  if skills: sections["Skills"] = skills.group(2).strip()
124
- except:
125
- pass
126
  return sections
127
 
128
  def compute_scores(resume_text, jd_text, required_skills):
@@ -140,7 +72,8 @@ def compute_scores(resume_text, jd_text, required_skills):
140
  final_score = 0.6*keyword_score + 0.4*semantic_score
141
  tips = [f"⚠️ Add '{skill}' to improve ATS match" for skill in required_skills if skill.lower() not in resume_text.lower()]
142
  return final_score, keyword_score, semantic_score, section_scores, tips
143
- except:
 
144
  return 0,0,0,{"Education":0,"Experience":0,"Skills":0},[]
145
 
146
  # ---------------------------
@@ -149,8 +82,8 @@ def compute_scores(resume_text, jd_text, required_skills):
149
  def export_csv(df, filename="ats_report.csv"):
150
  try:
151
  df.to_csv(filename, index=False)
152
- except:
153
- pass
154
  return filename
155
 
156
  def export_pdf(df, filename="ats_report.pdf"):
@@ -171,22 +104,23 @@ def export_pdf(df, filename="ats_report.pdf"):
171
  pdf.multi_cell(0, 10, row["Tips"])
172
  pdf.ln(5)
173
  pdf.output(filename)
174
- except:
175
- pass
176
  return filename
177
 
178
  # ---------------------------
179
- # AI Resume Rewriter (Simple)
180
  # ---------------------------
181
  def ai_resume_rewriter(resume_text, jd_text):
182
  try:
183
  required_skills = extract_skills(jd_text)
 
184
  rewritten = resume_text
185
- for skill in required_skills:
186
- if skill.lower() not in resume_text.lower():
187
- rewritten += f"\n- Experience with {skill}"
188
  return rewritten
189
- except:
 
190
  return resume_text
191
 
192
  # ---------------------------
@@ -229,73 +163,52 @@ def generate_feedback(resume_text, jd_text):
229
  feedback_text += "\n\n### Suggested Certifications:\n" + ("\n".join(cert_suggestions) if cert_suggestions else "No suggestions")
230
  feedback_text += "\n\n### Resume Optimization Tips:\n" + ("\n".join(resume_tips) if resume_tips else "Your resume looks well-structured.")
231
  return feedback_text
232
- except:
 
233
  return "Feedback unavailable."
234
 
235
  # ---------------------------
236
  # Multi-JD Analysis
237
  # ---------------------------
238
- def analyze_multi_jd(user_id, resume_file, jd_texts):
239
- try:
240
- if resume_file.name.endswith(".pdf"):
241
- resume_text = extract_text_from_pdf(resume_file)
242
- elif resume_file.name.endswith(".docx"):
243
- resume_text = extract_text_from_docx(resume_file)
244
- else:
245
- resume_text = ""
246
- jd_list = [jd.strip() for jd in jd_texts.split("\n\n") if jd.strip()]
247
- results = []
248
- for jd in jd_list:
249
- required_skills = extract_skills(jd)
250
- final_score, keyword_score, semantic_score, section_scores, tips = compute_scores(resume_text, jd, required_skills)
251
- section_scores_str = "\n".join([f"{k}: {v:.2%}" for k,v in section_scores.items()])
252
- tips_str = "\n".join(tips) if tips else "No suggestions"
253
- results.append({
254
- "JD": jd[:50]+"..." if len(jd)>50 else jd,
255
- "Final Score": f"{final_score:.2%}",
256
- "Keyword Score": f"{keyword_score:.2%}",
257
- "Semantic Score": f"{semantic_score:.2%}",
258
- "Section Scores": section_scores_str,
259
- "Tips": tips_str
260
- })
261
- cursor.execute("""
262
- INSERT INTO analyses (user_id, resume_text, jd_text, final_score, keyword_score, semantic_score, section_scores, tips)
263
- VALUES (?,?,?,?,?,?,?,?)""",
264
- (user_id, resume_text, jd, final_score, keyword_score, semantic_score, str(section_scores), tips_str))
265
- conn.commit()
266
- df = pd.DataFrame(results)
267
- export_csv(df)
268
- export_pdf(df)
269
- return df
270
- except:
271
- return pd.DataFrame()
272
 
273
  # ---------------------------
274
  # Gradio Interface
275
  # ---------------------------
276
- def analyze_gradio(resume_file, jd_text, username, password):
277
- login_msg, user_id = login(username, password)
278
- if not user_id:
279
- return None, None, login_msg, ""
280
- df = analyze_multi_jd(user_id, resume_file, jd_text)
281
- try:
282
- if resume_file.name.endswith(".pdf"):
283
- resume_text = extract_text_from_pdf(resume_file)
284
- elif resume_file.name.endswith(".docx"):
285
- resume_text = extract_text_from_docx(resume_file)
286
- except:
287
- resume_text = ""
288
- feedback = generate_feedback(resume_text, jd_text)
289
- rewritten_resume = ai_resume_rewriter(resume_text, jd_text)
290
- return "ats_report.csv", "ats_report.pdf", feedback, rewritten_resume
291
-
292
  iface = gr.Interface(
293
- fn=analyze_gradio,
294
  inputs=[
295
  gr.File(label="Upload Resume (PDF/DOCX)"),
296
- gr.Textbox(label="Paste Job Description(s) (Separate multiple JDs with double line breaks)", lines=10),
297
- gr.Textbox(label="Username"),
298
- gr.Textbox(label="Password", type="password")
299
  ],
300
  outputs=[
301
  gr.File(label="Download CSV Report"),
 
1
  import gradio as gr
 
2
  import re
3
  import numpy as np
4
  import pandas as pd
 
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
  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
  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
  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):
 
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
  # ---------------------------
 
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"):
 
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
  # ---------------------------
 
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:
185
+ required_skills = extract_skills(jd)
186
+ final_score, keyword_score, semantic_score, section_scores, tips = compute_scores(resume_text, jd, required_skills)
187
+ section_scores_str = "\n".join([f"{k}: {v:.2%}" for k,v in section_scores.items()])
188
+ tips_str = "\n".join(tips) if tips else "No suggestions"
189
+ results.append({
190
+ "JD": jd[:50]+"..." if len(jd)>50 else jd,
191
+ "Final Score": f"{final_score:.2%}",
192
+ "Keyword Score": f"{keyword_score:.2%}",
193
+ "Semantic Score": f"{semantic_score:.2%}",
194
+ "Section Scores": section_scores_str,
195
+ "Tips": tips_str
196
+ })
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"),