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()