ResumeIQ / core /analyzer.py
pranav8tripathi@gmail.com
updated
4ede186
raw
history blame
5.06 kB
"""Core resume analysis logic"""
import streamlit as st
import re
from models.llm_client import LLMClient
from agents.resume_extractor import ResumeExtractor
from agents.jd_summarizer import JobDescriptionSummarizer
from agents.matcher import ResumeJDMatcher
from agents.shortlister import Shortlister
from db.database import ResumeMatchDB
def extract_candidate_info(resume_text):
"""Extract candidate name and email from resume text"""
email_pattern = r'[\w\.-]+@[\w\.-]+\.\w+'
email = re.search(email_pattern, resume_text)
email = email.group(0) if email else "Not found"
lines = resume_text.split('\n')
name = "Not found"
name_patterns = [
r'^[A-Z][a-z]+\s+[A-Z][a-z]+$',
r'^[A-Z][a-z]+\s+[A-Z]\.\s+[A-Z][a-z]+$',
r'^[A-Z][a-z]+\s+[A-Z][a-z]+\s+[A-Z][a-z]+$'
]
for line in lines:
line = line.strip()
if line and '@' not in line:
for pattern in name_patterns:
if re.match(pattern, line):
name = line
break
if name != "Not found":
break
return name, email
def analyze_resumes(uploaded_files, job_descriptions, api_key, model_name, base_url):
"""Analyze resumes and store results in session state"""
progress_bar = st.progress(0)
status_text = st.empty()
with st.spinner("Analyzing resumes..."):
results = []
total_steps = len(uploaded_files) * len(job_descriptions)
current_step = 0
db = ResumeMatchDB()
for uploaded_file in uploaded_files:
status_text.text(f"πŸ“„ Processing {uploaded_file.name}...")
extractor = ResumeExtractor(uploaded_file)
resume_text = extractor.get_resume_text()
if not resume_text or len(resume_text.strip()) < 10:
st.error(f"❌ Could not extract text from {uploaded_file.name}. File may be corrupted or empty.")
continue
candidate_name, candidate_email = extract_candidate_info(resume_text)
candidate_id = db.insert_candidate(
name=candidate_name,
email=candidate_email,
resume_path=uploaded_file.name
)
resume_results = []
for jd in job_descriptions:
current_step += 1
progress = current_step / total_steps
progress_bar.progress(progress)
status_text.text(f"πŸ” Matching with {jd['title']}...")
jd_agent = JobDescriptionSummarizer(jd['content'])
jd_summary = jd_agent.get_summary()
if not jd_summary or len(jd_summary.strip()) < 10:
st.error(f"❌ Could not process job description: {jd['title']}")
continue
llm = LLMClient(api_key=api_key, model_name=model_name, base_url=base_url)
matcher = ResumeJDMatcher(llm)
shortlister = Shortlister(threshold=70.0)
match_result = matcher.match_resume_to_job(resume_text, jd_summary)
match_percent = shortlister.compute_final_score(match_result)
is_shortlisted = shortlister.is_shortlisted(match_percent)
job_id = db.insert_job_description(
title=jd['title'],
description=jd['content']
)
match_data = {
'match_score': match_percent,
'skills_match': match_result['skills_match'],
'experience_match': match_result['experience_match'],
'education_match': match_result['education_match'],
'certifications_match': match_result['certifications_match'],
'summary': match_result['summary'],
'is_shortlisted': is_shortlisted
}
db.insert_match_result(candidate_id, job_id, match_data)
resume_results.append({
"job_title": jd['title'],
"match_score": match_percent,
"is_shortlisted": is_shortlisted,
"details": match_result,
"job_id": job_id
})
if resume_results:
best_match = max(resume_results, key=lambda x: x['match_score'])
results.append({
"candidate_name": candidate_name,
"candidate_email": candidate_email,
"resume_name": uploaded_file.name,
"best_match": best_match,
"candidate_id": candidate_id
})
progress_bar.empty()
status_text.empty()
st.session_state.results = results