Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -4,7 +4,7 @@ from PyPDF2 import PdfReader
|
|
| 4 |
from docx import Document
|
| 5 |
|
| 6 |
# Set up API key for Google Generative Language
|
| 7 |
-
API_KEY = st.secrets["
|
| 8 |
|
| 9 |
def extract_text_from_pdf(pdf_file):
|
| 10 |
"""Extract text from PDF file."""
|
|
@@ -23,15 +23,15 @@ def extract_text_from_docx(docx_file):
|
|
| 23 |
return text
|
| 24 |
|
| 25 |
def analyze_documents(resume_text, job_description):
|
| 26 |
-
"""Analyze resume text against the job description."""
|
| 27 |
custom_prompt = f"""
|
| 28 |
-
Please analyze the following resume in the context of the job description provided.
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
|
|
|
| 35 |
Job Description: {job_description}
|
| 36 |
Resume: {resume_text}
|
| 37 |
"""
|
|
@@ -46,110 +46,69 @@ def analyze_documents(resume_text, job_description):
|
|
| 46 |
response = requests.post(url, headers=headers, json=data)
|
| 47 |
return response.json()
|
| 48 |
|
| 49 |
-
def display_resume(file
|
| 50 |
-
"""Display uploaded resume content."""
|
| 51 |
file_type = file.name.split('.')[-1].lower()
|
| 52 |
-
unique_key = f"{file.name}_{index}" # Ensure the key is unique by appending an index
|
| 53 |
if file_type == 'pdf':
|
| 54 |
-
|
| 55 |
-
|
|
|
|
|
|
|
|
|
|
| 56 |
elif file_type == 'docx':
|
| 57 |
-
|
| 58 |
-
|
|
|
|
|
|
|
|
|
|
| 59 |
else:
|
| 60 |
-
st.error(
|
| 61 |
|
| 62 |
-
|
| 63 |
-
"""Extract the full analysis (match percentage, missing keywords, etc.) from the API response."""
|
| 64 |
-
try:
|
| 65 |
-
# Get the analysis content from the API response
|
| 66 |
-
analysis_content = response.get("choices", [{}])[0].get("text", "")
|
| 67 |
-
|
| 68 |
-
# Regex to extract the match percentage, missing keywords, final thoughts, and recommendations
|
| 69 |
-
match_percentage = re.search(r"Match Percentage:.*?([a-zA-Z0-9\s\-\(\)<>\d]+%)", analysis_content)
|
| 70 |
-
missing_keywords = re.search(r"Missing Keywords:([\s\S]*?)(?=\n\n|Final Thoughts)", analysis_content)
|
| 71 |
-
final_thoughts = re.search(r"Final Thoughts:\n\n([\s\S]*?)(?=\n\n|Recommendations)", analysis_content)
|
| 72 |
-
recommendations = re.search(r"Recommendations:\n\n([\s\S]*?)(?=\n\n|$)", analysis_content)
|
| 73 |
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
missing_keywords = missing_keywords.group(1).strip() if missing_keywords else "No missing keywords identified."
|
| 77 |
-
final_thoughts = final_thoughts.group(1).strip() if final_thoughts else "No final thoughts provided."
|
| 78 |
-
recommendations = recommendations.group(1).strip() if recommendations else "No recommendations provided."
|
| 79 |
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
"final_thoughts": final_thoughts,
|
| 84 |
-
"recommendations": recommendations
|
| 85 |
-
}
|
| 86 |
-
|
| 87 |
-
except Exception as e:
|
| 88 |
-
st.error(f"Error extracting analysis: {str(e)}")
|
| 89 |
-
return {
|
| 90 |
-
"match_percentage": "Match Percentage: N/A",
|
| 91 |
-
"missing_keywords": "Error extracting missing keywords.",
|
| 92 |
-
"final_thoughts": "Error extracting final thoughts.",
|
| 93 |
-
"recommendations": "Error extracting recommendations."
|
| 94 |
-
}
|
| 95 |
|
| 96 |
-
|
| 97 |
-
"
|
| 98 |
-
|
| 99 |
-
for index, resume in enumerate(resumes):
|
| 100 |
-
resume.seek(0) # Reset file pointer
|
| 101 |
-
file_type = resume.name.split('.')[-1].lower()
|
| 102 |
-
|
| 103 |
-
# Extract resume text based on file type
|
| 104 |
-
if file_type == 'pdf':
|
| 105 |
-
text = extract_text_from_pdf(resume) # Use PyMuPDF
|
| 106 |
-
elif file_type == 'docx':
|
| 107 |
-
text = extract_text_from_docx(resume)
|
| 108 |
-
|
| 109 |
-
# Analyze the resume once
|
| 110 |
-
analysis = analyze_documents(text, job_description)
|
| 111 |
-
full_analysis = extract_full_analysis(analysis)
|
| 112 |
-
|
| 113 |
-
# Display the full analysis result
|
| 114 |
-
st.write(f"### Match Percentage: {full_analysis['match_percentage']}")
|
| 115 |
-
st.write(f"### Missing Keywords: {full_analysis['missing_keywords']}")
|
| 116 |
-
st.write(f"### Final Thoughts:\n{full_analysis['final_thoughts']}")
|
| 117 |
-
st.write(f"### Recommendations:\n{full_analysis['recommendations']}")
|
| 118 |
|
| 119 |
-
|
| 120 |
-
st.
|
|
|
|
|
|
|
|
|
|
| 121 |
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
""", unsafe_allow_html=True
|
| 130 |
-
)
|
| 131 |
-
st.markdown('<div class="title">📝🔍🌟 ATS Resume Evaluation System</div>', unsafe_allow_html=True)
|
| 132 |
-
st.markdown('<div class="subtitle">Upload up to 10 resumes and analyze them against the job description</div>', unsafe_allow_html=True)
|
| 133 |
|
| 134 |
-
#
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
|
|
|
|
| 138 |
|
| 139 |
-
#
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 145 |
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
if len(resumes) <= 10: # Limit to a maximum of 10 resumes
|
| 150 |
-
with st.spinner("Analyzing..."):
|
| 151 |
-
analyze_multiple_resumes(resumes, job_description)
|
| 152 |
else:
|
| 153 |
-
st.error("
|
| 154 |
-
else:
|
| 155 |
-
st.error("Please provide both a job description and at least one resume file.")
|
|
|
|
| 4 |
from docx import Document
|
| 5 |
|
| 6 |
# Set up API key for Google Generative Language
|
| 7 |
+
API_KEY = st.secrets["API_KEY"]
|
| 8 |
|
| 9 |
def extract_text_from_pdf(pdf_file):
|
| 10 |
"""Extract text from PDF file."""
|
|
|
|
| 23 |
return text
|
| 24 |
|
| 25 |
def analyze_documents(resume_text, job_description):
|
|
|
|
| 26 |
custom_prompt = f"""
|
| 27 |
+
Please analyze the following resume in the context of the job description provided. Strictly check every single line in the job description and analyze my resume whether there is a match exactly. Strictly maintain high ATS standards and give scores only to the correct ones. Focus on hard skills which are missing and also soft skills which are missing. Provide the following details.:
|
| 28 |
+
1. The match percentage of the resume to the job description. Display this.
|
| 29 |
+
2. A list of missing keywords accurate ones.
|
| 30 |
+
3. Final thoughts on the resume's overall match with the job description in 3 lines.
|
| 31 |
+
4. Recommendations on how to add the missing keywords and improve the resume in 3-4 points with examples.
|
| 32 |
+
Please display in the above order don't mention the numbers like 1. 2. etc and strictly follow ATS standards so that analysis will be accurate. Strictly follow the above templates omg. don't keep changing every time.
|
| 33 |
+
Strictly follow the above things and template which has to be displayed and don't keep changing again and again. Don't fucking change the template from above.
|
| 34 |
+
Title should be Resume analysis and maintain the same title for all. Also if someone uploads the same unchanged resume twice, keep in mind to give the same results. Display new ones only if they have changed their resume according to your suggestions or at least few changes.
|
| 35 |
Job Description: {job_description}
|
| 36 |
Resume: {resume_text}
|
| 37 |
"""
|
|
|
|
| 46 |
response = requests.post(url, headers=headers, json=data)
|
| 47 |
return response.json()
|
| 48 |
|
| 49 |
+
def display_resume(file):
|
|
|
|
| 50 |
file_type = file.name.split('.')[-1].lower()
|
|
|
|
| 51 |
if file_type == 'pdf':
|
| 52 |
+
reader = PdfReader(file)
|
| 53 |
+
text = ""
|
| 54 |
+
for page in reader.pages:
|
| 55 |
+
text += page.extract_text()
|
| 56 |
+
st.text_area("Parsed Resume Content", text, height=400)
|
| 57 |
elif file_type == 'docx':
|
| 58 |
+
doc = Document(file)
|
| 59 |
+
text = ""
|
| 60 |
+
for para in doc.paragraphs:
|
| 61 |
+
text += para.text + "\n"
|
| 62 |
+
st.text_area("Parsed Resume Content", text, height=400)
|
| 63 |
else:
|
| 64 |
+
st.error("Unsupported file type. Please upload a PDF or DOCX file.")
|
| 65 |
|
| 66 |
+
st.set_page_config(page_title="ATS Resume Evaluation System", layout="wide")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 67 |
|
| 68 |
+
st.sidebar.title("Navigation")
|
| 69 |
+
page = st.sidebar.radio("Go to", ["Resume Analyzer"])
|
|
|
|
|
|
|
|
|
|
| 70 |
|
| 71 |
+
if page == "Resume Analyzer":
|
| 72 |
+
st.title("📄🔍 ATS Resume Evaluation System")
|
| 73 |
+
st.write("Welcome to the ATS Resume Evaluation System! Upload your resume and enter the job description to get a detailed evaluation of your resume's match with the job requirements.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 74 |
|
| 75 |
+
job_description = st.text_area("Job Description:")
|
| 76 |
+
resumes = st.file_uploader("Upload your resumes (PDF or DOCX)", type=["pdf", "docx"], accept_multiple_files=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 77 |
|
| 78 |
+
if resumes:
|
| 79 |
+
st.write("Uploaded Resumes:")
|
| 80 |
+
for resume in resumes:
|
| 81 |
+
st.write(f"📄 {resume.name}")
|
| 82 |
+
display_resume(resume)
|
| 83 |
|
| 84 |
+
if st.button("Analyze Resumes"):
|
| 85 |
+
if job_description and resumes:
|
| 86 |
+
if len(resumes) <= 10: # Limit to a maximum of 10 resumes
|
| 87 |
+
with st.spinner("Analyzing..."):
|
| 88 |
+
for resume in resumes:
|
| 89 |
+
resume.seek(0) # Reset the file pointer
|
| 90 |
+
file_type = resume.name.split('.')[-1].lower()
|
|
|
|
|
|
|
|
|
|
|
|
|
| 91 |
|
| 92 |
+
# Extract resume text based on file type
|
| 93 |
+
if file_type == 'pdf':
|
| 94 |
+
resume_text = extract_text_from_pdf(resume)
|
| 95 |
+
elif file_type == 'docx':
|
| 96 |
+
resume_text = extract_text_from_docx(resume)
|
| 97 |
|
| 98 |
+
# Analyze the resume text
|
| 99 |
+
analysis = analyze_documents(resume_text, job_description)
|
| 100 |
+
|
| 101 |
+
if "candidates" in analysis:
|
| 102 |
+
for candidate in analysis["candidates"]:
|
| 103 |
+
if "content" in candidate and "parts" in candidate["content"]:
|
| 104 |
+
for part in candidate["content"]["parts"]:
|
| 105 |
+
response_text = part["text"]
|
| 106 |
+
|
| 107 |
+
# Display the analysis results
|
| 108 |
+
st.markdown(response_text)
|
| 109 |
|
| 110 |
+
st.success("Analysis Complete!")
|
| 111 |
+
else:
|
| 112 |
+
st.error("You can upload a maximum of 10 resumes.")
|
|
|
|
|
|
|
|
|
|
| 113 |
else:
|
| 114 |
+
st.error("Please enter the job description and upload at least one resume.")
|
|
|
|
|
|