Update app.py
Browse files
app.py
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
|
|
| 1 |
import gradio as gr
|
|
|
|
| 2 |
from sentence_transformers import SentenceTransformer, util
|
| 3 |
-
import os
|
| 4 |
from PyPDF2 import PdfReader
|
| 5 |
import docx
|
| 6 |
import re
|
| 7 |
import google.generativeai as genai
|
| 8 |
-
import pandas as pd
|
| 9 |
import time
|
| 10 |
import concurrent.futures
|
| 11 |
|
|
@@ -133,23 +133,50 @@ def extract_candidate_details(gemini_response):
|
|
| 133 |
|
| 134 |
return name, email, contact
|
| 135 |
|
| 136 |
-
def
|
| 137 |
-
#
|
| 138 |
-
|
| 139 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 140 |
skills_weight = 0.2 # 20% weight to skills
|
|
|
|
| 141 |
|
| 142 |
-
# Normalize years of experience to 100% scale
|
| 143 |
leadership_score = min(leadership_years / max_leadership_exp, 1.0) * 100
|
| 144 |
management_score = min(management_years / max_management_exp, 1.0) * 100
|
| 145 |
|
| 146 |
-
#
|
| 147 |
-
|
|
|
|
| 148 |
|
| 149 |
-
|
|
|
|
|
|
|
|
|
|
| 150 |
|
| 151 |
# Calculate the overall match score
|
| 152 |
-
overall_match = (leadership_score * leadership_weight) +
|
|
|
|
|
|
|
|
|
|
| 153 |
return round(overall_match, 2)
|
| 154 |
|
| 155 |
def process_resume(resume, job_desc, progress_callback):
|
|
@@ -170,8 +197,9 @@ def process_resume(resume, job_desc, progress_callback):
|
|
| 170 |
gemini_analysis = analyze_with_gemini(resume_text, job_desc)
|
| 171 |
# Extract leadership and management details
|
| 172 |
leadership_years, management_years, skills = extract_management_details(gemini_analysis)
|
| 173 |
-
# Calculate overall match percentage
|
| 174 |
-
|
|
|
|
| 175 |
# Extract candidate details
|
| 176 |
name, email, contact = extract_candidate_details(gemini_analysis)
|
| 177 |
except Exception as e:
|
|
@@ -190,48 +218,36 @@ def process_resume(resume, job_desc, progress_callback):
|
|
| 190 |
"Gemini Analysis": gemini_analysis
|
| 191 |
}
|
| 192 |
|
| 193 |
-
|
| 194 |
-
|
| 195 |
-
|
| 196 |
-
|
| 197 |
-
if len(resumes) > MAX_RESUMES:
|
| 198 |
-
return f"Please upload no more than {MAX_RESUMES} resumes."
|
| 199 |
-
|
| 200 |
-
# Load job description text
|
| 201 |
-
job_desc = extract_text_from_file(job_desc_file)
|
| 202 |
-
|
| 203 |
results = []
|
| 204 |
-
total_resumes = len(resumes)
|
| 205 |
-
|
| 206 |
-
with concurrent.futures.ThreadPoolExecutor() as executor:
|
| 207 |
-
future_to_resume = {
|
| 208 |
-
executor.submit(process_resume, resume, job_desc, lambda p: None): resume for resume in resumes
|
| 209 |
-
}
|
| 210 |
-
|
| 211 |
-
for future in concurrent.futures.as_completed(future_to_resume):
|
| 212 |
-
result = future.result()
|
| 213 |
-
results.append(result)
|
| 214 |
|
| 215 |
-
#
|
| 216 |
-
|
| 217 |
-
|
| 218 |
-
|
| 219 |
-
|
| 220 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 221 |
|
| 222 |
-
|
|
|
|
| 223 |
|
| 224 |
# Gradio Interface
|
| 225 |
-
|
| 226 |
-
|
| 227 |
-
|
| 228 |
-
|
| 229 |
-
|
| 230 |
-
|
| 231 |
-
|
| 232 |
-
|
| 233 |
-
title="Resume Analysis with Gemini API",
|
| 234 |
-
description="Upload a job description and resumes to evaluate candidates' leadership and management match."
|
| 235 |
)
|
| 236 |
|
| 237 |
-
|
|
|
|
| 1 |
+
import os
|
| 2 |
import gradio as gr
|
| 3 |
+
import pandas as pd
|
| 4 |
from sentence_transformers import SentenceTransformer, util
|
|
|
|
| 5 |
from PyPDF2 import PdfReader
|
| 6 |
import docx
|
| 7 |
import re
|
| 8 |
import google.generativeai as genai
|
|
|
|
| 9 |
import time
|
| 10 |
import concurrent.futures
|
| 11 |
|
|
|
|
| 133 |
|
| 134 |
return name, email, contact
|
| 135 |
|
| 136 |
+
def calculate_role_score(role_keywords):
|
| 137 |
+
# Score based on the seniority of the leadership role
|
| 138 |
+
seniority_score = 0
|
| 139 |
+
role_hierarchy = {
|
| 140 |
+
"CEO": 5,
|
| 141 |
+
"CIO": 5,
|
| 142 |
+
"Director": 4,
|
| 143 |
+
"VP": 4,
|
| 144 |
+
"Manager": 3,
|
| 145 |
+
"Team Lead": 2,
|
| 146 |
+
"Junior": 1
|
| 147 |
+
}
|
| 148 |
+
|
| 149 |
+
for keyword, score in role_hierarchy.items():
|
| 150 |
+
if keyword.lower() in role_keywords.lower():
|
| 151 |
+
seniority_score = max(seniority_score, score) # Highest score from role hierarchy
|
| 152 |
+
|
| 153 |
+
return seniority_score
|
| 154 |
+
|
| 155 |
+
def calculate_advanced_match(leadership_years, management_years, skills, required_skills, role_keywords, max_leadership_exp=10, max_management_exp=10):
|
| 156 |
+
# Assign weights for leadership, management, and skills
|
| 157 |
+
leadership_weight = 0.35 # 35% weight to leadership
|
| 158 |
+
management_weight = 0.35 # 35% weight to management
|
| 159 |
skills_weight = 0.2 # 20% weight to skills
|
| 160 |
+
role_weight = 0.1 # 10% weight to role seniority
|
| 161 |
|
| 162 |
+
# Normalize years of experience to a 100% scale
|
| 163 |
leadership_score = min(leadership_years / max_leadership_exp, 1.0) * 100
|
| 164 |
management_score = min(management_years / max_management_exp, 1.0) * 100
|
| 165 |
|
| 166 |
+
# Adjust for role seniority
|
| 167 |
+
role_score = calculate_role_score(role_keywords) # Role score based on seniority of role
|
| 168 |
+
role_score = role_score * 100 # Normalize role score to 100 scale
|
| 169 |
|
| 170 |
+
# Calculate skill match: percentage of required skills found in the resume
|
| 171 |
+
skills_matched = sum(1 for skill in required_skills if skill.lower() in skills.lower())
|
| 172 |
+
total_skills = len(required_skills)
|
| 173 |
+
skill_match_score = (skills_matched / total_skills) * 100
|
| 174 |
|
| 175 |
# Calculate the overall match score
|
| 176 |
+
overall_match = (leadership_score * leadership_weight) + \
|
| 177 |
+
(management_score * management_weight) + \
|
| 178 |
+
(skill_match_score * skills_weight) + \
|
| 179 |
+
(role_score * role_weight)
|
| 180 |
return round(overall_match, 2)
|
| 181 |
|
| 182 |
def process_resume(resume, job_desc, progress_callback):
|
|
|
|
| 197 |
gemini_analysis = analyze_with_gemini(resume_text, job_desc)
|
| 198 |
# Extract leadership and management details
|
| 199 |
leadership_years, management_years, skills = extract_management_details(gemini_analysis)
|
| 200 |
+
# Calculate overall match percentage using enhanced calculation
|
| 201 |
+
role_keywords = gemini_analysis.lower()
|
| 202 |
+
overall_match = calculate_advanced_match(leadership_years, management_years, skills, required_skills, role_keywords)
|
| 203 |
# Extract candidate details
|
| 204 |
name, email, contact = extract_candidate_details(gemini_analysis)
|
| 205 |
except Exception as e:
|
|
|
|
| 218 |
"Gemini Analysis": gemini_analysis
|
| 219 |
}
|
| 220 |
|
| 221 |
+
# Main Gradio UI
|
| 222 |
+
def analyze_resumes(resumes, job_desc):
|
| 223 |
+
progress = gr.Progress()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 224 |
results = []
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 225 |
|
| 226 |
+
# Check for maximum number of resumes
|
| 227 |
+
if len(resumes) > MAX_RESUMES:
|
| 228 |
+
return "Error: Cannot upload more than 10 resumes."
|
| 229 |
+
|
| 230 |
+
# Process resumes concurrently
|
| 231 |
+
with concurrent.futures.ThreadPoolExecutor() as executor:
|
| 232 |
+
futures = []
|
| 233 |
+
for resume in resumes:
|
| 234 |
+
futures.append(executor.submit(process_resume, resume, job_desc, progress.update))
|
| 235 |
+
|
| 236 |
+
for future in concurrent.futures.as_completed(futures):
|
| 237 |
+
results.append(future.result())
|
| 238 |
|
| 239 |
+
resume_count_message = f"{len(resumes)} resume(s) uploaded."
|
| 240 |
+
return pd.DataFrame(results), resume_count_message
|
| 241 |
|
| 242 |
# Gradio Interface
|
| 243 |
+
iface = gr.Interface(
|
| 244 |
+
fn=analyze_resumes,
|
| 245 |
+
inputs=[
|
| 246 |
+
gr.File(label="Upload Resumes (PDF, DOCX, TXT)", file_count="multiple"),
|
| 247 |
+
gr.Textbox(label="Job Description", lines=5)
|
| 248 |
+
],
|
| 249 |
+
outputs=["dataframe", "text"],
|
| 250 |
+
live=True
|
|
|
|
|
|
|
| 251 |
)
|
| 252 |
|
| 253 |
+
iface.launch()
|