diff --git a/evaluator.py b/evaluator.py index 1f9e91f..d3690b4 100644 --- a/evaluator.py +++ b/evaluator.py @@ -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( diff --git a/job_description.txt b/job_description.txt new file mode 100644 index 0000000..42fff25 --- /dev/null +++ b/job_description.txt @@ -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. diff --git a/models.py b/models.py index e83779e..3f51ca1 100644 --- a/models.py +++ b/models.py @@ -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) diff --git a/prompts/templates/job_description_analysis.jinja b/prompts/templates/job_description_analysis.jinja new file mode 100644 index 0000000..faa849d --- /dev/null +++ b/prompts/templates/job_description_analysis.jinja @@ -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.** diff --git a/prompts/templates/resume_evaluation_criteria.jinja b/prompts/templates/resume_evaluation_criteria.jinja index 45c0daf..214a8ce 100644 --- a/prompts/templates/resume_evaluation_criteria.jinja +++ b/prompts/templates/resume_evaluation_criteria.jinja @@ -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) @@ -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"], diff --git a/resume.txt b/resume.txt new file mode 100644 index 0000000..0b50af7 --- /dev/null +++ b/resume.txt @@ -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 diff --git a/score.py b/score.py index b0944dd..e0548e7 100644 --- a/score.py +++ b/score.py @@ -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 @@ -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) @@ -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.""" @@ -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) @@ -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" @@ -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}") @@ -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 @@ -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", "") @@ -297,13 +319,21 @@ def main(pdf_path): if __name__ == "__main__": - if len(sys.argv) < 2: - print("Usage: python score.py ") - 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)