from . import models from django.urls import reverse from django.db import transaction from django.dispatch import receiver from django.db.models.signals import post_save from .models import FormField,FormStage,FormTemplate # @receiver(post_save, sender=models.Candidate) # def parse_resume(sender, instance, created, **kwargs): # if instance.resume and not instance.summary: # from .utils import extract_summary_from_pdf,match_resume_with_job_description # summary = extract_summary_from_pdf(instance.resume.path) # if 'error' not in summary: # instance.summary = summary # instance.save() # match_resume_with_job_description import logging logger = logging.getLogger(__name__) import os from .utils import extract_text_from_pdf,score_resume_with_openrouter import asyncio @receiver(post_save, sender=models.Candidate) def score_candidate_resume(sender, instance, created, **kwargs): try: # Get absolute file path 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) # if not resume_text: # instance.scoring_error = "Could not extract text from resume." # instance.save(update_fields=['scoring_error']) # return job_detail=str(instance.job.description)+str(instance.job.qualifications) prompt1 = 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 (1–2 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} """ result = score_resume_with_openrouter(prompt1) 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:** \"\"\" {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. """ result1 = score_resume_with_openrouter(prompt) instance.parsed_summary = str(result) # Update candidate with scoring results instance.match_score = result1.get('match_score') instance.strengths = result1.get('strengths', '') instance.weaknesses = result1.get('weaknesses', '') instance.criteria_checklist = result1.get('criteria_checklist', {}) # Save only scoring-related fields to avoid recursion instance.save(update_fields=[ 'match_score', 'strengths', 'weaknesses', 'criteria_checklist','parsed_summary' ]) logger.info(f"Successfully scored resume for candidate {instance.id}") except Exception as e: # error_msg = str(e)[:500] # Truncate to fit TextField # instance.scoring_error = error_msg # instance.save(update_fields=['scoring_error']) logger.error(f"Failed to score resume for candidate {instance.id}: {e}") # @receiver(post_save,sender=models.Candidate) # def trigger_scoring(sender,intance,created,**kwargs): @receiver(post_save, sender=FormTemplate) def create_default_stages(sender, instance, created, **kwargs): """ Create default resume stages when a new FormTemplate is created """ if created: with transaction.atomic(): # Stage 1: Contact Information contact_stage = FormStage.objects.create( template=instance, name='Contact Information', order=0, is_predefined=True ) FormField.objects.create( stage=contact_stage, label='First Name', field_type='text', required=True, order=0, is_predefined=True ) FormField.objects.create( stage=contact_stage, label='Last Name', field_type='text', required=True, order=1, is_predefined=True ) FormField.objects.create( stage=contact_stage, label='Email Address', field_type='email', required=True, order=2, is_predefined=True ) FormField.objects.create( stage=contact_stage, label='Phone Number', field_type='phone', required=True, order=3, is_predefined=True ) FormField.objects.create( stage=contact_stage, label='Address', field_type='text', required=False, order=4, is_predefined=True ) FormField.objects.create( stage=contact_stage, label='Resume Upload', field_type='file', required=True, order=5, is_predefined=True, file_types='.pdf,.doc,.docx', max_file_size=1 ) # Stage 2: Resume Objective objective_stage = FormStage.objects.create( template=instance, name='Resume Objective', order=1, is_predefined=True ) FormField.objects.create( stage=objective_stage, label='Career Objective', field_type='textarea', required=False, order=0, is_predefined=True ) # Stage 3: Education education_stage = FormStage.objects.create( template=instance, name='Education', order=2, is_predefined=True ) FormField.objects.create( stage=education_stage, label='Degree', field_type='text', required=True, order=0, is_predefined=True ) FormField.objects.create( stage=education_stage, label='Institution', field_type='text', required=True, order=1, is_predefined=True ) FormField.objects.create( stage=education_stage, label='Location', field_type='text', required=False, order=2, is_predefined=True ) FormField.objects.create( stage=education_stage, label='Graduation Date', field_type='date', required=False, order=3, is_predefined=True ) # Stage 4: Experience experience_stage = FormStage.objects.create( template=instance, name='Experience', order=3, is_predefined=True ) FormField.objects.create( stage=experience_stage, label='Position Title', field_type='text', required=True, order=0, is_predefined=True ) FormField.objects.create( stage=experience_stage, label='Company Name', field_type='text', required=True, order=1, is_predefined=True ) FormField.objects.create( stage=experience_stage, label='Location', field_type='text', required=False, order=2, is_predefined=True ) FormField.objects.create( stage=experience_stage, label='Start Date', field_type='date', required=True, order=3, is_predefined=True ) FormField.objects.create( stage=experience_stage, label='End Date', field_type='date', required=True, order=4, is_predefined=True ) FormField.objects.create( stage=experience_stage, label='Responsibilities & Achievements', field_type='textarea', required=False, order=5, is_predefined=True ) # Stage 5: Skills skills_stage = FormStage.objects.create( template=instance, name='Skills', order=4, is_predefined=True ) FormField.objects.create( stage=skills_stage, label='Technical Skills', field_type='checkbox', required=False, order=0, is_predefined=True, options=['Programming Languages', 'Frameworks', 'Tools & Technologies'] ) # Stage 6: Summary summary_stage = FormStage.objects.create( template=instance, name='Summary', order=5, is_predefined=True ) FormField.objects.create( stage=summary_stage, label='Professional Summary', field_type='textarea', required=False, order=0, is_predefined=True ) # Stage 7: Certifications certifications_stage = FormStage.objects.create( template=instance, name='Certifications', order=6, is_predefined=True ) FormField.objects.create( stage=certifications_stage, label='Certification Name', field_type='text', required=False, order=0, is_predefined=True ) FormField.objects.create( stage=certifications_stage, label='Issuing Organization', field_type='text', required=False, order=1, is_predefined=True ) FormField.objects.create( stage=certifications_stage, label='Issue Date', field_type='date', required=False, order=2, is_predefined=True ) FormField.objects.create( stage=certifications_stage, label='Expiration Date', field_type='date', required=False, order=3, is_predefined=True ) # Stage 8: Awards and Recognitions awards_stage = FormStage.objects.create( template=instance, name='Awards and Recognitions', order=7, is_predefined=True ) FormField.objects.create( stage=awards_stage, label='Award Name', field_type='text', required=False, order=0, is_predefined=True ) FormField.objects.create( stage=awards_stage, label='Issuing Organization', field_type='text', required=False, order=1, is_predefined=True ) FormField.objects.create( stage=awards_stage, label='Date Received', field_type='date', required=False, order=2, is_predefined=True ) FormField.objects.create( stage=awards_stage, label='Description', field_type='textarea', required=False, order=3, is_predefined=True )