Spaces:
Sleeping
Sleeping
File size: 9,568 Bytes
82e8360 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 |
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()
|