import gradio as gr from sentence_transformers import SentenceTransformer, util import docx import os from PyPDF2 import PdfReader import re from datetime import datetime # Load pre-trained model for sentence embedding model = SentenceTransformer('paraphrase-MiniLM-L6-v2') # Define maximum number of resumes MAX_RESUMES = 10 # Function to load job description from file path def load_job_description(job_desc_file): if not os.path.exists(job_desc_file): return "Job description file not found." with open(job_desc_file, 'r') as file: job_description = file.read() if not job_description.strip(): return "Job description is empty." return job_description # Function to load offer letter template def load_offer_letter_template(template_file): return docx.Document(template_file) # Function to check similarity between resumes and job description def check_similarity(job_description, resume_files): results = [] job_emb = model.encode(job_description, convert_to_tensor=True) for resume_file in resume_files: resume_text = extract_text_from_resume(resume_file) if not resume_text: results.append((resume_file.name, 0, "Not Eligible", None)) continue resume_emb = model.encode(resume_text, convert_to_tensor=True) similarity_score = util.pytorch_cos_sim(job_emb, resume_emb)[0][0].item() # Set a higher similarity threshold for eligibility if similarity_score >= 0.50: candidate_name = extract_candidate_name(resume_text) results.append((resume_file.name, similarity_score, "Eligible", candidate_name)) else: results.append((resume_file.name, similarity_score, "Not Eligible", None)) return results # Extract text from resume (handles .txt, .pdf, .docx) def extract_text_from_resume(resume_file): file_extension = os.path.splitext(resume_file)[1].lower() if file_extension not in ['.txt', '.pdf', '.docx']: return "Unsupported file format" if file_extension == '.txt': return read_text_file(resume_file) elif file_extension == '.pdf': return read_pdf_file(resume_file) elif file_extension == '.docx': return read_docx_file(resume_file) return "Failed to read the resume text." def read_text_file(file_path): with open(file_path, 'r') as file: return file.read() def read_pdf_file(file_path): reader = PdfReader(file_path) text = "" for page in reader.pages: text += page.extract_text() return text def read_docx_file(file_path): doc = docx.Document(file_path) text = "" for para in doc.paragraphs: text += para.text return text # Extract candidate name from resume text def extract_candidate_name(resume_text): name_pattern = re.compile(r'\b([A-Z][a-z]+ [A-Z][a-z]+)\b') matches = name_pattern.findall(resume_text) if matches: return matches[0] # Returns the first match return "Unknown Candidate" # Create an offer letter def create_offer_letter(candidate_name, job_title, company_name, joining_date, template_doc): new_doc = docx.Document() for paragraph in template_doc.paragraphs: new_doc.add_paragraph(paragraph.text) # Replace placeholders in the template for paragraph in new_doc.paragraphs: paragraph.text = paragraph.text.replace('{{ name }}', candidate_name) paragraph.text = paragraph.text.replace('{{ role }}', job_title) paragraph.text = paragraph.text.replace('{{ joining date }}', joining_date) timestamp = datetime.now().strftime("%Y%m%d%H%M%S") output_filename = f"/tmp/{candidate_name}_{timestamp}_Offer_Letter.docx" new_doc.save(output_filename) return output_filename # Schedule interview with AM/PM format def schedule_interview(candidate_name, interview_date, interview_time): try: interview_datetime = datetime.strptime(f"{interview_date} {interview_time}", '%Y-%m-%d %I:%M %p') message = f"Interview scheduled for {candidate_name} on {interview_datetime.strftime('%Y-%m-%d at %I:%M %p')}" return message except Exception as e: return f"Error in scheduling interview: {str(e)}" # Validate date and time with AM/PM def validate_date_time(date_str, time_str): try: date_obj = datetime.strptime(date_str, "%Y-%m-%d") time_obj = datetime.strptime(time_str, "%I:%M %p") return True, date_obj, time_obj except ValueError: return False, None, None # Main processing function def process_files(job_desc, template, resumes, interview_choice, interview_date, interview_time, generate_offer_choice, role, joining_date, candidate_name): try: # Check if the number of resumes is within the allowed limit if len(resumes) > MAX_RESUMES: return "Please upload no more than 10 resumes." # Check if all necessary files are provided if not job_desc or not template or not resumes: return "Please provide all necessary files." # Load the job description and offer letter template job_desc_text = load_job_description(job_desc) offer_template_doc = load_offer_letter_template(template) # Check similarity results = check_similarity(job_desc_text, resumes) # Initialize lists for the output analysis_results = ["Analysis Results:"] interview_messages = [] offer_files = [] # Process each resume's similarity for idx, (filename, similarity, eligibility, extracted_name) in enumerate(results, start=1): candidate_label = f"Candidate {idx}" similarity_percentage = similarity * 100 analysis_results.append(f"{candidate_label}, Similarity Percentage: {similarity_percentage:.2f}%") # If interview is scheduled and "Yes" is selected if interview_choice == "Yes" and eligibility == "Eligible" and extracted_name: is_valid, date_obj, time_obj = validate_date_time(interview_date, interview_time) if is_valid: interview_msg = schedule_interview(candidate_label, interview_date, interview_time) interview_messages.append(interview_msg) # Ask the user if they want to generate the offer letter if generate_offer_choice == "Yes": offer_file = create_offer_letter(candidate_name, role, "AI Company", joining_date, offer_template_doc) offer_files.append(offer_file) else: interview_messages.append(f"Offer letter not generated for {candidate_label}.") else: interview_messages.append(f"Invalid date or time format for {candidate_label}. Use YYYY-MM-DD for date and HH:MM AM/PM for time.") # Prepare interview schedule output if interview_messages: interview_messages.insert(0, "Interview Schedule:") interview_output = "\n".join(interview_messages) else: interview_output = "No interviews scheduled." # Prepare the offer letters output if offer_files: analysis_results.append("\nGenerated Offer Letters:") for idx, offer_file in enumerate(offer_files, start=1): analysis_results.append(f"- Candidate {idx} Offer Letter") # Join and return the results as formatted text analysis_output = "\n".join(analysis_results) interview_output = "\n".join(interview_messages) return analysis_output, interview_output, offer_files except Exception as e: # Return any errors encountered during processing return f"Error processing files: {str(e)}", None # Gradio Interface Components job_desc_input = gr.File(label="Upload Job Description (TXT)", type="filepath") template_input = gr.File(label="Upload Offer Letter Template (DOCX)", type="filepath") resumes_input = gr.Files(label="Upload Resumes (TXT, DOCX, PDF)", type="filepath") interview_choice_input = gr.Radio(["Yes", "No"], label="Schedule Interview?") interview_date_input = gr.Textbox(label="Interview Date (YYYY-MM-DD)", placeholder="Enter date in YYYY-MM-DD format") interview_time_input = gr.Textbox(label="Interview Time (HH:MM AM/PM)", placeholder="Enter time in HH:MM AM/PM format") generate_offer_choice_input = gr.Radio(["Yes", "No"], label="Generate Offer Letter?") role_input = gr.Textbox(label="Enter Role") joining_date_input = gr.Textbox(label="Enter Joining Date (YYYY-MM-DD)", placeholder="Enter joining date in YYYY-MM-DD format") candidate_name_input = gr.Textbox(label="Enter Candidate Name", placeholder="Enter candidate's name") # Gradio Outputs results_output = gr.Markdown(label="Analysis Results") interview_output = gr.Markdown(label="Interview Schedule") offer_letters_output = gr.Files(label="Generated Offer Letters") # Gradio Interface interface = gr.Interface( fn=process_files, inputs=[job_desc_input, template_input, resumes_input, interview_choice_input, interview_date_input, interview_time_input, generate_offer_choice_input, role_input, joining_date_input, candidate_name_input], outputs=[results_output, interview_output, offer_letters_output], title="HR Assistant - Resume Screening & Interview Scheduling", description="Upload job description, template, and resumes to screen candidates, schedule interviews, and generate offer letters." ) interface.launch()