Upload 3 files
Browse files- app.py +104 -0
- prompt.py +30 -0
- requirements.txt +10 -0
app.py
ADDED
@@ -0,0 +1,104 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
import time
|
3 |
+
import json
|
4 |
+
import pandas as pd
|
5 |
+
import streamlit as st
|
6 |
+
import docx
|
7 |
+
import fitz as pymupdf
|
8 |
+
from dotenv import load_dotenv
|
9 |
+
import google.generativeai as genai
|
10 |
+
from prompt import extract_skill, prompt_first_chunks
|
11 |
+
|
12 |
+
# Load environment variables
|
13 |
+
load_dotenv()
|
14 |
+
GOOGLE_API_KEY = os.getenv("GOOGLE_API_KEY")
|
15 |
+
|
16 |
+
genai.configure(api_key=GOOGLE_API_KEY)
|
17 |
+
model = genai.GenerativeModel('gemini-1.0-pro')
|
18 |
+
|
19 |
+
def extract_skills_from_job_description(job_description):
|
20 |
+
"""Extract skills from the provided job description."""
|
21 |
+
jd_prompt = extract_skill.format(job_description=job_description)
|
22 |
+
response = model.generate_content(jd_prompt, generation_config=genai.types.GenerationConfig(temperature=0.0))
|
23 |
+
if response._result.candidates[0].content:
|
24 |
+
generated_response = response._result.candidates[0].content.parts[0].text.replace("```json\n", "").rstrip("\n").replace("```", "")
|
25 |
+
json_array = json.loads(generated_response)
|
26 |
+
elements_to_prepend = ['Name', 'Address', 'EmailId', 'Mobile_number']
|
27 |
+
return elements_to_prepend + json_array
|
28 |
+
else:
|
29 |
+
st.error("Error with Generative AI Model.")
|
30 |
+
return []
|
31 |
+
|
32 |
+
def process_resume(file, jd_skills):
|
33 |
+
"""Extract skills and ratings from a single resume file."""
|
34 |
+
text = ""
|
35 |
+
if file.name.endswith('.pdf'):
|
36 |
+
# Process PDF files
|
37 |
+
document = pymupdf.open(stream=file.read(), filetype="pdf")
|
38 |
+
for page_num in range(len(document)):
|
39 |
+
page = document.load_page(page_num)
|
40 |
+
text += page.get_text()
|
41 |
+
document.close()
|
42 |
+
elif file.name.endswith('.docx'):
|
43 |
+
# Process DOCX files
|
44 |
+
document = docx.Document(file)
|
45 |
+
for paragraph in document.paragraphs:
|
46 |
+
text += paragraph.text + "\n"
|
47 |
+
for table in document.tables:
|
48 |
+
for row in table.rows:
|
49 |
+
for cell in row.cells:
|
50 |
+
text += cell.text + "\n"
|
51 |
+
|
52 |
+
# Generate response from model
|
53 |
+
resume_prompt = prompt_first_chunks.format(resume=text, jd_skill=jd_skills)
|
54 |
+
response = model.generate_content(resume_prompt, generation_config=genai.types.GenerationConfig(temperature=0.0))
|
55 |
+
try:
|
56 |
+
json_array = json.loads(response._result.candidates[0].content.parts[0].text)
|
57 |
+
return pd.DataFrame([json_array], columns=jd_skills)
|
58 |
+
except Exception as e:
|
59 |
+
st.error(f"Error processing file {file.name}: {e}")
|
60 |
+
return pd.DataFrame()
|
61 |
+
|
62 |
+
def main():
|
63 |
+
st.title("Resume Filtering Based on Job Description")
|
64 |
+
|
65 |
+
# Upload resumes
|
66 |
+
uploaded_files = st.file_uploader("Upload Resumes (PDF/DOCX)", type=["pdf", "docx"], accept_multiple_files=True)
|
67 |
+
|
68 |
+
# Input job description
|
69 |
+
job_description = st.text_area("Enter Job Description")
|
70 |
+
|
71 |
+
# Process resumes and display results
|
72 |
+
if st.button("Process Resumes"):
|
73 |
+
if not uploaded_files or not job_description:
|
74 |
+
st.warning("Please upload resumes and provide a job description.")
|
75 |
+
return
|
76 |
+
|
77 |
+
jd_skills = extract_skills_from_job_description(job_description)
|
78 |
+
if not jd_skills:
|
79 |
+
return
|
80 |
+
|
81 |
+
all_data = pd.DataFrame(columns=jd_skills)
|
82 |
+
|
83 |
+
for file in uploaded_files:
|
84 |
+
resume_data = process_resume(file, jd_skills)
|
85 |
+
if not resume_data.empty:
|
86 |
+
resume_data["resume_path"] = file.name
|
87 |
+
all_data = pd.concat([all_data, resume_data], ignore_index=True)
|
88 |
+
|
89 |
+
if not all_data.empty:
|
90 |
+
# Calculate total skill ratings
|
91 |
+
skills_columns = all_data.columns[4:-1]
|
92 |
+
all_data['total_skill_rating'] = round((all_data[skills_columns].sum(axis=1) / len(skills_columns)) * 100, 2)
|
93 |
+
|
94 |
+
# Display dataframe in Streamlit
|
95 |
+
st.write("### Processed Resume Data:", all_data)
|
96 |
+
|
97 |
+
# Save to CSV
|
98 |
+
csv_path = "processed_resumes.csv"
|
99 |
+
all_data.to_csv(csv_path, index=False)
|
100 |
+
st.success(f"Data saved to {csv_path}")
|
101 |
+
st.download_button(label="Download CSV", data=all_data.to_csv(index=False), file_name="processed_resumes.csv", mime="text/csv")
|
102 |
+
|
103 |
+
if __name__ == "__main__":
|
104 |
+
main()
|
prompt.py
ADDED
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from langchain_core.prompts import PromptTemplate
|
2 |
+
|
3 |
+
extract_skill = PromptTemplate.from_template("""`You are an experienced HR recruiter, your role involves receiving a ```{job_description}``` for a specific position.
|
4 |
+
Your primary responsibility is to compile a list of essential requirements and skills mentioned in the job description.
|
5 |
+
Kindly provide the main points in the form of a string list.
|
6 |
+
Please give the results as JSON array""")
|
7 |
+
|
8 |
+
|
9 |
+
|
10 |
+
|
11 |
+
prompt_first_chunks = PromptTemplate.from_template("""
|
12 |
+
Candidate Resume information is below.
|
13 |
+
----------------------------------
|
14 |
+
{resume}
|
15 |
+
----------------------------------
|
16 |
+
Given the provided resume cotext and no prior knowledge,
|
17 |
+
You are an experienced HR recruiter proficient in resume scanning. Your task is to verify if the candidate possesses a specific skill listed in jd_skills based on the resume above. Note that the skill may not be directly listed; you may need to analyze the resume to find the skill.
|
18 |
+
Jd_skills: {jd_skill}
|
19 |
+
Your response should be as a JSON array with the required Jd_skills as the key and its corresponding answer as the value.
|
20 |
+
Ensure that only the skill listed in jd_skills is matched, and do not assign 1 or 0 values to the name, address, email ID, and mobile number fields.
|
21 |
+
The format should be:
|
22 |
+
{{
|
23 |
+
"Name": "full name of candidate in string",
|
24 |
+
"Address": "full address in string",
|
25 |
+
"EmailId": "emailid in string",
|
26 |
+
"Mobile_number":"Candidate mobile number in string",
|
27 |
+
{jd_skill}: 1 (if skill present) or 0 (if skill not present)
|
28 |
+
}}
|
29 |
+
""")
|
30 |
+
|
requirements.txt
ADDED
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
langchain
|
2 |
+
openai
|
3 |
+
PyMuPDF
|
4 |
+
twilio
|
5 |
+
flask
|
6 |
+
python-docx
|
7 |
+
llama_index
|
8 |
+
google-generativeai
|
9 |
+
llama-index-llms-azure-openai
|
10 |
+
langchain-openai
|