Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 37 additions & 4 deletions evaluator.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,17 +37,50 @@ def _initialize_llm_provider(self):
"""Initialize the appropriate LLM provider based on the model."""
self.provider = initialize_llm_provider(self.model_name)

def _load_evaluation_prompt(self, resume_text: str) -> str:
def _load_evaluation_prompt(self, resume_text: str, job_requirements: dict = None) -> str:
criteria_template = self.template_manager.render_template(
"resume_evaluation_criteria", text_content=resume_text
"resume_evaluation_criteria",
text_content=resume_text,
job_requirements=job_requirements,
)
if criteria_template is None:
raise ValueError("Failed to load resume evaluation criteria template")
return criteria_template

def evaluate_resume(self, resume_text: str) -> EvaluationData:
def _analyze_job_description(self, job_description: str) -> dict:
"""Analyze the job description to extract key requirements."""
try:
prompt = self.template_manager.render_template(
"job_description_analysis", text_content=job_description
)
if prompt is None:
raise ValueError("Failed to load job description analysis template")

response = self.provider.chat(
model=self.model_name,
messages=[{"role": "user", "content": prompt}],
options={
"stream": False,
"temperature": 0.2,
},
)
response_text = extract_json_from_response(response["message"]["content"])
return json.loads(response_text)
except Exception as e:
logger.error(f"Error analyzing job description: {str(e)}")
return {}

def evaluate_resume(
self, resume_text: str, job_description: str = None
) -> EvaluationData:
self._last_resume_text = resume_text
full_prompt = self._load_evaluation_prompt(resume_text)
job_requirements = None
if job_description:
job_requirements = self._analyze_job_description(job_description)

full_prompt = self._load_evaluation_prompt(
resume_text, job_requirements=job_requirements
)
# logger.info(f"🔤 Evaluation prompt being sent: {full_prompt}")
try:
system_message = self.template_manager.render_template(
Expand Down
13 changes: 13 additions & 0 deletions job_description.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
**Software Engineer Intern**

**Responsibilities:**
- Develop and maintain web applications using Python and JavaScript.
- Collaborate with the team to design and implement new features.
- Write clean, efficient, and well-documented code.
- Troubleshoot and debug issues.

**Requirements:**
- Experience with Python and Django.
- Familiarity with JavaScript, HTML, and CSS.
- Strong problem-solving skills.
- Excellent communication and teamwork skills.
1 change: 1 addition & 0 deletions models.py
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ class Deductions(BaseModel):

class EvaluationData(BaseModel):
scores: Scores
job_match_score: Optional[CategoryScore] = None
bonus_points: BonusPoints
deductions: Deductions
key_strengths: List[str] = Field(min_items=1, max_items=5)
Expand Down
29 changes: 29 additions & 0 deletions prompts/templates/job_description_analysis.jinja
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
You are an expert in analyzing job descriptions. Your task is to extract the key skills and requirements from the provided job description and categorize them.

**Instructions:**

1. Read the job description carefully.
2. Identify the key technical skills, software, and qualifications required for the role.
3. Categorize the extracted information into a structured JSON format.

**JSON Output Structure:**

```json
{
"key_skills": [
"skill1",
"skill2",
"skill3"
],
"required_experience": [
"experience1",
"experience2"
]
}
```

**Job Description to Analyze:**

{{ text_content }}

**IMPORTANT: Respond with ONLY the JSON structure specified above. Do not include any other text or explanations.**
13 changes: 13 additions & 0 deletions prompts/templates/resume_evaluation_criteria.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,18 @@ You are evaluating a resume for a Software Intern position at HackerRank. Analyz
- Use GitHub data (if provided in === GITHUB DATA === section) as additional context
- Use blog data (if provided in === BLOG DATA === section) for technical communication assessment

{% if job_requirements %}
## JOB DESCRIPTION ALIGNMENT
- You will be provided with a set of key skills and required experience from a job description.
- Compare the candidate's resume against these requirements to assess their suitability for the role.
- Provide a `job_match_score` from 0 to 100, where 100 is a perfect match.
- The `job_match_score` should be included in the final JSON output.

**Job Requirements:**
- Key Skills: {{ job_requirements.key_skills | join(', ') }}
- Required Experience: {{ job_requirements.required_experience | join(', ') }}
{% endif %}

## SCORING CRITERIA

### Open Source (0-35 points)
Expand Down Expand Up @@ -174,6 +186,7 @@ Analyze the following resume and provide a JSON response with this EXACT structu
"production": {"score": 0, "max": 25, "evidence": "string"},
"technical_skills": {"score": 0, "max": 10, "evidence": "string"}
},
"job_match_score": {"score": 0, "max": 100, "evidence": "string"},
"bonus_points": {"total": 0, "breakdown": "string"},
"deductions": {"total": 0, "reasons": "string"},
"key_strengths": ["strength1", "strength2", "strength3", "strength4", "strength5"],
Expand Down
19 changes: 19 additions & 0 deletions resume.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
**John Doe**
john.doe@email.com | (123) 456-7890 | linkedin.com/in/johndoe | github.com/johndoe

**Summary**
Aspiring Software Engineer with a passion for web development and a strong foundation in Python and JavaScript.

**Experience**
**Software Developer Intern**, ABC Corp - Anytown, USA (May 2023 - Aug 2023)
- Developed and maintained a web application using Python, Django, and JavaScript.
- Collaborated with the team to design and implement new features.

**Education**
**B.S. in Computer Science**, University of Example - Exampleton, USA (2020 - 2024)

**Skills**
- **Programming Languages:** Python, JavaScript, HTML, CSS
- **Frameworks:** Django, React
- **Databases:** PostgreSQL, MongoDB
- **Tools:** Git, Docker
56 changes: 43 additions & 13 deletions score.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import json
import logging
import csv
import argparse
from pdf import PDFHandler
from github import fetch_and_display_github_info
from models import JSONResume, EvaluationData
Expand Down Expand Up @@ -71,6 +72,15 @@ def print_evaluation_results(
# Overall Score
print(f"\n🎯 OVERALL SCORE: {total_score:.1f}/{max_score}")

# Job Match Score
if hasattr(evaluation, "job_match_score") and evaluation.job_match_score:
print("\n" + "-" * 80)
print("🤝 JOB DESCRIPTION ALIGNMENT:")
js_score = evaluation.job_match_score
print(f" Match Score: {js_score.score}/{js_score.max}")
print(f" Evidence: {js_score.evidence}")
print("-" * 80)

# Detailed Scores
print("\n📈 DETAILED SCORES:")
print("-" * 60)
Expand Down Expand Up @@ -160,7 +170,10 @@ def print_evaluation_results(


def _evaluate_resume(
resume_data: JSONResume, github_data: dict = None, blog_data: dict = None
resume_data: JSONResume,
github_data: dict = None,
blog_data: dict = None,
job_description: str = None,
) -> Optional[EvaluationData]:
"""Evaluate the resume using AI and display results."""

Expand All @@ -181,7 +194,9 @@ def _evaluate_resume(
resume_text += blog_text

# Evaluate the enhanced resume
evaluation_result = evaluator.evaluate_resume(resume_text)
evaluation_result = evaluator.evaluate_resume(
resume_text, job_description=job_description
)

# print(evaluation_result)

Expand All @@ -197,7 +212,7 @@ def find_profile(profiles, network):
)


def main(pdf_path):
def main(pdf_path, job_description_path=None):
# Create cache filename based on PDF path
cache_filename = (
f"cache/resumecache_{os.path.basename(pdf_path).replace('.pdf', '')}.json"
Expand All @@ -206,6 +221,13 @@ def main(pdf_path):
f"cache/githubcache_{os.path.basename(pdf_path).replace('.pdf', '')}.json"
)

job_description = None
if job_description_path:
if os.path.exists(job_description_path):
job_description = Path(job_description_path).read_text()
else:
print(f"Warning: Job description file not found at '{job_description_path}'")

# Check if cache exists and we're in development mode
if DEVELOPMENT_MODE and os.path.exists(cache_filename):
print(f"Loading cached data from {cache_filename}")
Expand All @@ -226,7 +248,7 @@ def main(pdf_path):
os.makedirs(os.path.dirname(cache_filename), exist_ok=True)
Path(cache_filename).write_text(
json.dumps(resume_data.model_dump(), indent=2, ensure_ascii=False),
encoding='utf-8'
encoding="utf-8",
)

# Check if cache exists and we're in development mode
Expand All @@ -252,10 +274,10 @@ def main(pdf_path):
os.makedirs(os.path.dirname(github_cache_filename), exist_ok=True)
Path(github_cache_filename).write_text(
json.dumps(github_data, indent=2, ensure_ascii=False),
encoding='utf-8'
encoding="utf-8",
)

score = _evaluate_resume(resume_data, github_data)
score = _evaluate_resume(resume_data, github_data, job_description=job_description)

# Get candidate name for display
candidate_name = os.path.basename(pdf_path).replace(".pdf", "")
Expand Down Expand Up @@ -297,13 +319,21 @@ def main(pdf_path):


if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: python score.py <pdf_path>")
exit(1)
pdf_path = sys.argv[1]
parser = argparse.ArgumentParser(
description="Evaluate a resume PDF and optionally compare it against a job description."
)
parser.add_argument("pdf_path", help="Path to the resume PDF file.")
parser.add_argument(
"-j",
"--job-description",
dest="job_description_path",
help="Path to a text file containing the job description.",
required=False,
)
args = parser.parse_args()

if not os.path.exists(pdf_path):
print(f"Error: File '{pdf_path}' does not exist.")
if not os.path.exists(args.pdf_path):
print(f"Error: File '{args.pdf_path}' does not exist.")
exit(1)

main(pdf_path)
main(args.pdf_path, args.job_description_path)