CR7CAD commited on
Commit
19a0df1
·
verified ·
1 Parent(s): ae778e2

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +80 -131
app.py CHANGED
@@ -167,10 +167,10 @@ def basic_summarize(text, max_length=100):
167
  summary = " ".join(summary_sentences)
168
  return summary
169
 
170
- # Custom classification function for comprehensive job fit assessment
171
  def evaluate_job_fit(resume_summary, job_requirements, models):
172
  """
173
- Use model to evaluate job fit with comprehensive analysis across multiple dimensions
174
  """
175
  start_time = time.time()
176
 
@@ -180,121 +180,41 @@ def evaluate_job_fit(resume_summary, job_requirements, models):
180
  job_title = job_requirements["title"]
181
  job_summary = job_requirements["summary"]
182
 
183
- # Create a comprehensive analysis prompt for the model to evaluate
184
- analysis_prompt = f"""
185
- RESUME SUMMARY:
186
- {resume_summary}
187
-
188
- JOB DESCRIPTION:
189
- Title: {job_title}
190
- Required experience: {years_required} years
191
- Required skills: {', '.join(required_skills)}
192
- Description: {job_summary}
193
-
194
- TASK: Analyze how well the candidate matches this job based on:
195
- 1. Technical skills match
196
- 2. Experience level match
197
- 3. Role/position alignment
198
- 4. Industry familiarity
199
- 5. Potential for success in this position
200
-
201
- Assign a score from 0-2 where:
202
- 0 = NOT FIT (major gaps in requirements)
203
- 1 = POTENTIAL FIT (meets some key requirements)
204
- 2 = GOOD FIT (meets most or all key requirements)
205
- """
206
-
207
- # Truncate prompt if needed to fit model's input limits
208
- max_prompt_length = 1024 # Set a reasonable limit
209
- if len(analysis_prompt) > max_prompt_length:
210
- analysis_prompt = analysis_prompt[:max_prompt_length]
211
-
212
- # Use sentiment analysis model for evaluation
213
- fit_score = 0 # Default score
214
-
215
- # Run multiple sub-analyses to build confidence in our result
216
- sub_analyses = []
217
-
218
- # Function to run model evaluation
219
- def run_model_evaluation(prompt_text):
220
- if has_pipeline and 'evaluator' in models:
221
- result = models['evaluator'](prompt_text)
222
- # Convert sentiment to score
223
- if result[0]['label'] == 'POSITIVE' and result[0]['score'] > 0.8:
224
- return 2 # Strong positive = good fit
225
- elif result[0]['label'] == 'NEUTRAL':
226
- return 1 # neutral fit = potential fit
227
- else:
228
- return 0 # Negative = not fit
229
- else:
230
- # Manual implementation if pipeline not available
231
- tokenizer = models['evaluator_tokenizer']
232
- model = models['evaluator_model']
233
-
234
- # Truncate to avoid exceeding model's max length
235
- max_length = tokenizer.model_max_length if hasattr(tokenizer, 'model_max_length') else 512
236
- truncated_text = " ".join(prompt_text.split()[:max_length])
237
-
238
- inputs = tokenizer(truncated_text, return_tensors="pt", truncation=True, max_length=max_length)
239
- with torch.no_grad():
240
- outputs = model(**inputs)
241
-
242
- probabilities = torch.nn.functional.softmax(outputs.logits, dim=-1)
243
- positive_prob = probabilities[0][1].item() # Positive class probability
244
-
245
- # Convert probability to score
246
- if positive_prob > 0.8:
247
- return 2
248
- elif positive_prob > 0.6:
249
- return 1
250
- else:
251
- return 0
252
 
253
- # Run skills analysis
254
- skills_prompt = f"""
255
- RESUME SKILLS: {resume_summary}
256
- JOB REQUIRED SKILLS: {', '.join(required_skills)}
257
-
258
- Does the candidate have most of the required technical skills for this position?
259
- """
260
- skills_score = run_model_evaluation(skills_prompt)
261
- sub_analyses.append(skills_score)
262
 
263
- # Run experience analysis
264
- experience_prompt = f"""
265
- RESUME EXPERIENCE: {resume_summary}
266
- JOB REQUIRED EXPERIENCE: {years_required} years in {job_title}
 
 
 
 
 
267
 
268
- Does the candidate have sufficient years of relevant experience for this position?
269
- """
270
- experience_score = run_model_evaluation(experience_prompt)
271
- sub_analyses.append(experience_score)
272
 
273
- # Run role alignment analysis
274
- role_prompt = f"""
275
- CANDIDATE PROFILE: {resume_summary}
276
- JOB ROLE: {job_title}, {job_summary}
277
 
278
- Is the candidate's background well-aligned with this job role and responsibilities?
279
- """
280
- role_score = run_model_evaluation(role_prompt)
281
- sub_analyses.append(role_score)
282
 
283
- # Calculate overall score (weighted average)
284
- # Skills: 40%, Experience: 30%, Role alignment: 30%
285
- weights = [0.4, 0.3, 0.3]
286
- weighted_score = sum(score * weight for score, weight in zip(sub_analyses, weights))
287
 
288
- # Convert to integer score (0-2)
289
- if weighted_score >= 1.5:
290
- fit_score = 2
291
- elif weighted_score >= 0.8:
292
- fit_score = 1
293
- else:
294
- fit_score = 0
295
-
296
- # Extract key information from resume for assessment
297
- # Parse name, age, industry from resume summary
298
  name_match = re.search(r'Name:\s*(.*?)(?=\n|\Z)', resume_summary)
299
  name = name_match.group(1).strip() if name_match else "The candidate"
300
 
@@ -304,19 +224,27 @@ def evaluate_job_fit(resume_summary, job_requirements, models):
304
  industry_match = re.search(r'Expected Industry:\s*(.*?)(?=\n|\Z)', resume_summary)
305
  industry = industry_match.group(1).strip() if industry_match else "unspecified industry"
306
 
307
- # Count matching skills but don't show the percentage in output
308
- resume_lower = resume_summary.lower()
309
- matching_skills = [skill for skill in required_skills if skill.lower() in resume_lower]
310
- missing_skills = [skill for skill in required_skills if skill.lower() not in resume_lower]
 
 
 
 
 
 
 
 
 
 
311
 
312
- # Generate assessment text based on score with more holistic evaluation
313
  if fit_score == 2:
314
- fit_assessment = f"{fit_score}: {name} demonstrates strong alignment with the {job_title} position. Their background in {industry} and professional experience appear well-suited for this role's requirements. The technical expertise matches what the position demands."
315
  elif fit_score == 1:
316
- fit_assessment = f"{fit_score}: {name} shows potential for the {job_title} role with some relevant experience, though there are gaps in certain technical areas. Their {industry} background provides partial alignment with the position requirements. Additional training might be needed in {', '.join(missing_skills[:2])} if pursuing this opportunity."
317
  else:
318
- # For score 0, be constructive but honest
319
- fit_assessment = f"{fit_score}: {name}'s current background shows limited alignment with this {job_title} position. Their experience level and technical background differ significantly from the role requirements. A position better matching their {industry} expertise might be more suitable."
320
 
321
  execution_time = time.time() - start_time
322
 
@@ -511,15 +439,15 @@ def extract_skills(text):
511
  """Extract key skills from the resume"""
512
  # Common skill categories - reduced keyword list for speed
513
  skill_categories = {
514
- "Programming": ["Python", "Java", "JavaScript", "HTML", "CSS", "SQL", "C++", "C#", "Go"],
515
- "Data Science": ["Machine Learning", "Data Analysis", "Statistics", "TensorFlow", "PyTorch", "AI", "Algorithms"],
516
- "Database": ["SQL", "MySQL", "MongoDB", "Database", "NoSQL", "PostgreSQL"],
517
- "Web Development": ["React", "Angular", "Node.js", "Frontend", "Backend", "Full-Stack"],
518
- "Software Development": ["Agile", "Scrum", "Git", "DevOps", "Docker", "System Design"],
519
- "Cloud": ["AWS", "Azure", "Google Cloud", "Cloud Computing"],
520
  "Security": ["Cybersecurity", "Network Security", "Encryption", "Security"],
521
- "Business": ["Project Management", "Business Analysis", "Leadership", "Teamwork"],
522
- "Design": ["UX/UI", "User Experience", "Design Thinking", "Adobe"]
523
  }
524
 
525
  # Process everything at once
@@ -589,13 +517,19 @@ def extract_job_requirements(job_description, models):
589
  """
590
  Extract key requirements from a job description
591
  """
592
- # Common technical skills to look for
593
  tech_skills = [
594
  "Python", "Java", "C++", "JavaScript", "TypeScript", "Go", "Rust", "SQL", "Ruby", "PHP", "Swift", "Kotlin",
595
  "React", "Angular", "Vue", "Node.js", "HTML", "CSS", "Django", "Flask", "Spring", "REST API", "GraphQL",
596
  "Machine Learning", "TensorFlow", "PyTorch", "Data Science", "AI", "Big Data", "Deep Learning", "NLP",
597
  "AWS", "Azure", "GCP", "Docker", "Kubernetes", "CI/CD", "Jenkins", "GitHub Actions", "Terraform",
598
- "MySQL", "PostgreSQL", "MongoDB", "Redis", "Elasticsearch", "DynamoDB", "Cassandra"
 
 
 
 
 
 
599
  ]
600
 
601
  # Clean the text for processing
@@ -636,6 +570,19 @@ def extract_job_requirements(job_description, models):
636
  # Extract required skills
637
  required_skills = [skill for skill in tech_skills if re.search(r'\b' + re.escape(skill.lower()) + r'\b', clean_job_text)]
638
 
 
 
 
 
 
 
 
 
 
 
 
 
 
639
  # Create a simple summary of the job using the summarize_text function
640
  job_summary = summarize_text(job_description, models, max_length=100)
641
 
@@ -729,8 +676,10 @@ def main():
729
  2: "GOOD FIT"
730
  }
731
 
732
- # Show the score prominently
733
- st.markdown(f"## {fit_labels[fit_score]}")
 
 
734
 
735
  # Display assessment
736
  st.markdown(assessment)
 
167
  summary = " ".join(summary_sentences)
168
  return summary
169
 
170
+ # Modified job fit evaluation function that uses a direct scoring approach
171
  def evaluate_job_fit(resume_summary, job_requirements, models):
172
  """
173
+ Use a more direct method to evaluate job fit, rather than relying solely on sentiment analysis
174
  """
175
  start_time = time.time()
176
 
 
180
  job_title = job_requirements["title"]
181
  job_summary = job_requirements["summary"]
182
 
183
+ # Extract skills from resume
184
+ skills_mentioned = extract_skills(resume_summary)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
185
 
186
+ # Calculate skill match percentage
187
+ matching_skills = [skill for skill in required_skills if skill in skills_mentioned]
188
+ skill_match_percentage = len(matching_skills) / len(required_skills) if required_skills else 0
 
 
 
 
 
 
189
 
190
+ # Extract experience level from resume
191
+ experience_pattern = r'(\d+)\+?\s*years?\s*(?:of)?\s*experience'
192
+ experience_match = re.search(experience_pattern, resume_summary, re.IGNORECASE)
193
+ years_experience = 0
194
+ if experience_match:
195
+ try:
196
+ years_experience = int(experience_match.group(1))
197
+ except:
198
+ years_experience = 0
199
 
200
+ # Experience match
201
+ exp_match_ratio = min(1.0, years_experience / max(1, years_required)) if years_required > 0 else 0.5
 
 
202
 
203
+ # Check job title match
204
+ job_title_lower = job_title.lower()
205
+ title_match = 0
 
206
 
207
+ # Look for job title words in resume
208
+ title_words = [word for word in job_title_lower.split() if len(word) > 3]
209
+ title_matches = sum(1 for word in title_words if word in resume_summary.lower())
210
+ title_match = title_matches / len(title_words) if title_words else 0
211
 
212
+ # Calculate scores for each dimension
213
+ skill_score = min(2, skill_match_percentage * 3) # 0-2 scale
214
+ exp_score = min(2, exp_match_ratio * 2) # 0-2 scale
215
+ title_score = min(2, title_match * 2) # 0-2 scale
216
 
217
+ # Extract name, age, industry from resume summary
 
 
 
 
 
 
 
 
 
218
  name_match = re.search(r'Name:\s*(.*?)(?=\n|\Z)', resume_summary)
219
  name = name_match.group(1).strip() if name_match else "The candidate"
220
 
 
224
  industry_match = re.search(r'Expected Industry:\s*(.*?)(?=\n|\Z)', resume_summary)
225
  industry = industry_match.group(1).strip() if industry_match else "unspecified industry"
226
 
227
+ # Calculate weighted final score
228
+ # Skills: 50%, Experience: 30%, Title match: 20%
229
+ weighted_score = (skill_score * 0.5) + (exp_score * 0.3) + (title_score * 0.2)
230
+
231
+ # Convert to integer score (0-2)
232
+ if weighted_score >= 1.5:
233
+ fit_score = 2 # Good fit
234
+ elif weighted_score >= 0.8:
235
+ fit_score = 1 # Potential fit
236
+ else:
237
+ fit_score = 0 # Not a fit
238
+
239
+ # Generate assessment text based on score
240
+ missing_skills = [skill for skill in required_skills if skill not in skills_mentioned]
241
 
 
242
  if fit_score == 2:
243
+ fit_assessment = f"{fit_score}: GOOD FIT - {name} demonstrates strong alignment with the {job_title} position. Their background in {industry} and professional experience appear well-suited for this role's requirements. The technical expertise matches what the position demands."
244
  elif fit_score == 1:
245
+ fit_assessment = f"{fit_score}: POTENTIAL FIT - {name} shows potential for the {job_title} role with some relevant experience, though there are gaps in certain technical areas. Their {industry} background provides partial alignment with the position requirements. Additional training might be needed in {', '.join(missing_skills[:2])} if pursuing this opportunity."
246
  else:
247
+ fit_assessment = f"{fit_score}: NOT FIT - {name}'s current background shows limited alignment with this {job_title} position. Their experience level and technical background differ significantly from the role requirements. A position better matching their {industry} expertise might be more suitable."
 
248
 
249
  execution_time = time.time() - start_time
250
 
 
439
  """Extract key skills from the resume"""
440
  # Common skill categories - reduced keyword list for speed
441
  skill_categories = {
442
+ "Programming": ["Python", "Java", "JavaScript", "HTML", "CSS", "SQL", "C++", "C#", "Go", "React", "Angular", "Vue", "Node.js"],
443
+ "Data Science": ["Machine Learning", "Data Analysis", "Statistics", "TensorFlow", "PyTorch", "AI", "Algorithms", "NLP", "Deep Learning"],
444
+ "Database": ["SQL", "MySQL", "MongoDB", "Database", "NoSQL", "PostgreSQL", "Oracle", "Redis"],
445
+ "Web Development": ["React", "Angular", "Node.js", "Frontend", "Backend", "Full-Stack", "REST API", "GraphQL"],
446
+ "Software Development": ["Agile", "Scrum", "Git", "DevOps", "Docker", "System Design", "CI/CD", "Jenkins"],
447
+ "Cloud": ["AWS", "Azure", "Google Cloud", "Cloud Computing", "Lambda", "S3", "EC2"],
448
  "Security": ["Cybersecurity", "Network Security", "Encryption", "Security"],
449
+ "Business": ["Project Management", "Business Analysis", "Leadership", "Teamwork", "Agile", "Scrum"],
450
+ "Design": ["UX/UI", "User Experience", "Design Thinking", "Adobe", "Figma"]
451
  }
452
 
453
  # Process everything at once
 
517
  """
518
  Extract key requirements from a job description
519
  """
520
+ # Common technical skills to look for - expanded list for better matching
521
  tech_skills = [
522
  "Python", "Java", "C++", "JavaScript", "TypeScript", "Go", "Rust", "SQL", "Ruby", "PHP", "Swift", "Kotlin",
523
  "React", "Angular", "Vue", "Node.js", "HTML", "CSS", "Django", "Flask", "Spring", "REST API", "GraphQL",
524
  "Machine Learning", "TensorFlow", "PyTorch", "Data Science", "AI", "Big Data", "Deep Learning", "NLP",
525
  "AWS", "Azure", "GCP", "Docker", "Kubernetes", "CI/CD", "Jenkins", "GitHub Actions", "Terraform",
526
+ "MySQL", "PostgreSQL", "MongoDB", "Redis", "Elasticsearch", "DynamoDB", "Cassandra", "Oracle",
527
+ "Project Management", "Agile", "Scrum", "UX/UI", "Design", "Leadership", "Team Management",
528
+ "Communication Skills", "Problem Solving", "Critical Thinking", "Blockchain", "Information Security",
529
+ "Networking", "Linux", "Windows Server", "Excel", "PowerPoint", "Word", "Tableau", "Power BI", "R",
530
+ "SPSS", "SAS", "Spark", "Hadoop", "JIRA", "Confluence", "Git", "SVN", "Testing", "QA", "DevOps",
531
+ "Full Stack", "Mobile Development", "Android", "iOS", "React Native", "Flutter", "SEO", "Marketing",
532
+ "Sales", "Customer Service", "Business Analysis", "Data Analysis", "Accounting", "Finance"
533
  ]
534
 
535
  # Clean the text for processing
 
570
  # Extract required skills
571
  required_skills = [skill for skill in tech_skills if re.search(r'\b' + re.escape(skill.lower()) + r'\b', clean_job_text)]
572
 
573
+ # If no skills found, use some default important ones to avoid empty lists
574
+ if not required_skills:
575
+ # Extract some common words that might be skills
576
+ words = re.findall(r'\b\w{4,}\b', clean_job_text)
577
+ word_counts = {}
578
+ for word in words:
579
+ if word not in ["with", "that", "this", "have", "from", "they", "will", "what", "your", "their", "about"]:
580
+ word_counts[word] = word_counts.get(word, 0) + 1
581
+
582
+ # Get the top 5 most common words as potential skills
583
+ sorted_words = sorted(word_counts.items(), key=lambda x: x[1], reverse=True)
584
+ required_skills = [word.capitalize() for word, _ in sorted_words[:5]]
585
+
586
  # Create a simple summary of the job using the summarize_text function
587
  job_summary = summarize_text(job_description, models, max_length=100)
588
 
 
676
  2: "GOOD FIT"
677
  }
678
 
679
+ # Show the score prominently with appropriate coloring
680
+ score_label = fit_labels[fit_score]
681
+ score_colors = {0: "red", 1: "orange", 2: "green"}
682
+ st.markdown(f"<h2 style='color: {score_colors[fit_score]};'>{score_label}</h2>", unsafe_allow_html=True)
683
 
684
  # Display assessment
685
  st.markdown(assessment)