156 lines
6.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import os
import json
import logging
import requests
from PyPDF2 import PdfReader
from recruitment.models import Candidate
logger = logging.getLogger(__name__)
OPENROUTER_API_KEY ='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'
OPENROUTER_MODEL = 'qwen/qwen-2.5-72b-instruct:free'
if not OPENROUTER_API_KEY:
logger.warning("OPENROUTER_API_KEY not set. Resume scoring will be skipped.")
def extract_text_from_pdf(file_path):
print("text extraction")
text = ""
try:
with open(file_path, "rb") as f:
reader = PdfReader(f)
for page in reader.pages:
text += (page.extract_text() or "")
except Exception as e:
logger.error(f"PDF extraction failed: {e}")
raise
return text.strip()
def ai_handler(prompt):
print("model call")
response = requests.post(
url="https://openrouter.ai/api/v1/chat/completions",
headers={
"Authorization": f"Bearer {OPENROUTER_API_KEY}",
"Content-Type": "application/json",
},
data=json.dumps({
"model": OPENROUTER_MODEL,
"messages": [{"role": "user", "content": prompt}],
},
)
)
res = {}
print(response.status_code)
if response.status_code == 200:
res = response.json()
content = res["choices"][0]['message']['content']
try:
content = content.replace("```json","").replace("```","")
res = json.loads(content)
except Exception as e:
print(e)
# res = raw_output["choices"][0]["message"]["content"]
else:
print("error response")
return res
def handle_reume_parsing_and_scoring(pk):
logger.info(f"Scoring resume for candidate {pk}")
try:
instance = Candidate.objects.get(pk=pk)
file_path = instance.resume.path
if not os.path.exists(file_path):
logger.warning(f"Resume file not found: {file_path}")
return
resume_text = extract_text_from_pdf(file_path)
job_detail= f"{instance.job.description} {instance.job.qualifications}"
resume_parser_prompt = f"""
You are an expert resume parser and summarizer. Given a resume in plain text format, extract and organize the following key-value information into a clean, valid JSON object:
full_name: Full name of the candidate
current_title: Most recent or current job title
location: City and state (or country if outside the U.S.)
contact: Phone number and email (as a single string or separate fields)
linkedin: LinkedIn profile URL (if present)
github: GitHub or portfolio URL (if present)
summary: Brief professional profile or summary (12 sentences)
education: List of degrees, each with:
institution
degree
year
gpa (if provided)
relevant_courses (as a list, if mentioned)
skills: Grouped by category if possible (e.g., programming, big data, visualization), otherwise as a flat list of technologies/tools
experience: List of roles, each with:
company
job_title
location
start_date and end_date (or "Present" if applicable)
key_achievements (as a list of concise bullet points)
projects: List of notable projects (if clearly labeled), each with:
name
year
technologies_used
brief_description
Instructions:
Be concise but preserve key details.
Normalize formatting (e.g., “Jun. 2014” → “2014-06”).
Omit redundant or promotional language.
If a section is missing, omit the key or set it to null/empty list as appropriate.
Output only valid JSON—no markdown, no extra text.
Now, process the following resume text:
{resume_text}
"""
resume_parser_result = ai_handler(resume_parser_prompt)
resume_scoring_prompt = f"""
You are an expert technical recruiter. Your task is to score the following candidate for the role of a Senior Data Analyst based on the provided job criteria.
**Job Criteria:**
{job_detail}
**Candidate's Extracted Resume Json:**
\"\"\"
{resume_parser_result}
\"\"\"
**Your Task:**
Provide a response in strict JSON format with the following keys:
1. 'match_score': A score from 0 to 100 representing how well the candidate fits the role.
2. 'strengths': A brief summary of why the candidate is a strong fit, referencing specific criteria.
3. 'weaknesses': A brief summary of where the candidate falls short or what criteria are missing.
4. 'criteria_checklist': An object where you rate the candidate's match for each specific criterion (e.g., {{'Python': 'Met', 'AWS': 'Not Mentioned'}}).
Only output valid JSON. Do not include any other text.
"""
resume_scoring_result = ai_handler(resume_scoring_prompt)
instance.parsed_summary = str(resume_parser_result)
# Update candidate with scoring results
instance.match_score = resume_scoring_result.get('match_score')
instance.strengths = resume_scoring_result.get('strengths', '')
instance.weaknesses = resume_scoring_result.get('weaknesses', '')
instance.criteria_checklist = resume_scoring_result.get('criteria_checklist', {})
instance.is_resume_parsed = True
# Save only scoring-related fields to avoid recursion
instance.save(update_fields=[
'match_score', 'strengths', 'weaknesses',
'criteria_checklist','parsed_summary', 'is_resume_parsed'
])
logger.info(f"Successfully scored resume for candidate {instance.id}")
except Exception as e:
logger.error(f"Failed to score resume for candidate {instance.id}: {e}")