Spaces:
Sleeping
Sleeping
import streamlit as st | |
from docx import Document | |
import pdfplumber | |
import re | |
from collections import Counter | |
# Custom CSS for UI enhancements | |
def apply_custom_css(): | |
st.markdown(""" | |
<style> | |
.title { | |
font-size: 2.5rem; | |
color: #4CAF50; | |
text-align: center; | |
margin-bottom: 20px; | |
} | |
.subtitle { | |
font-size: 1.5rem; | |
color: #FF5722; | |
margin-top: 20px; | |
margin-bottom: 10px; | |
} | |
.metric { | |
font-size: 1.2rem; | |
text-align: center; | |
} | |
.separator { | |
border-top: 2px solid #eee; | |
margin-top: 30px; | |
margin-bottom: 30px; | |
} | |
.suggestions { | |
background-color: #f9f9f9; | |
border-left: 4px solid #2196F3; | |
padding: 10px; | |
margin: 10px 0; | |
} | |
</style> | |
""", unsafe_allow_html=True) | |
# Function to extract text from PDF | |
def extract_text_from_pdf(pdf_file): | |
with pdfplumber.open(pdf_file) as pdf: | |
text = "" | |
for page in pdf.pages: | |
text += page.extract_text() + "\n" | |
return text | |
# Function to extract text from Word Document | |
def extract_text_from_docx(docx_file): | |
doc = Document(docx_file) | |
text = "" | |
for paragraph in doc.paragraphs: | |
text += paragraph.text + "\n" | |
return text | |
# Function to check ATS-friendliness | |
def check_ats_friendly(text): | |
score = 100 | |
suggestions = [] | |
# Keywords check | |
keywords = ["experience", "skills", "education", "certification", "achievements"] | |
for keyword in keywords: | |
if keyword not in text.lower(): | |
score -= 10 | |
suggestions.append(f"Add more emphasis on '{keyword}' in your resume.") | |
# Formatting check | |
if re.search(r"[\t]", text): # Tabs are not ATS-friendly | |
score -= 10 | |
suggestions.append("Avoid using tabs for formatting; use spaces or standard bullet points.") | |
# Font styles check | |
if re.search(r"[{}<>]", text): # Symbols like curly braces and angled brackets can confuse ATS | |
score -= 10 | |
suggestions.append("Remove any special symbols like {}, <>, etc., which might confuse ATS.") | |
# Contact info check | |
if not re.search(r"\b\d{10}\b", text): # Check for a 10-digit phone number | |
score -= 10 | |
suggestions.append("Ensure your resume includes a valid phone number.") | |
if not re.search(r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}", text): # Check for an email | |
score -= 10 | |
suggestions.append("Include a valid professional email address.") | |
# Suggestion for simplicity | |
if len(text.split()) > 1000: # Resume too long | |
score -= 10 | |
suggestions.append("Try to keep your resume concise and limit it to one or two pages.") | |
return score, suggestions | |
# Function to calculate job fit score and recommend missing keywords | |
def calculate_job_fit_score(resume_text, job_description): | |
resume_words = Counter(resume_text.lower().split()) | |
job_words = Counter(job_description.lower().split()) | |
# Find overlap between resume and job description keywords | |
common_words = resume_words & job_words | |
total_job_words = sum(job_words.values()) | |
missing_keywords = set(job_words.keys()) - set(resume_words.keys()) | |
# Calculate match percentage | |
if total_job_words == 0: | |
match_percentage = 0 | |
else: | |
match_percentage = sum(common_words.values()) / total_job_words * 100 | |
suggestions = [] | |
if match_percentage < 100: | |
suggestions.append( | |
"Consider including the missing keywords in your resume to improve matching." | |
) | |
return match_percentage, suggestions, missing_keywords | |
# Main App | |
def main(): | |
apply_custom_css() | |
st.markdown("<div class='title'>ATS Resume Checker</div>", unsafe_allow_html=True) | |
st.write( | |
"Upload your resume to check its ATS-friendliness and compare it with a job description for keyword matching." | |
) | |
st.markdown("<hr class='separator'>", unsafe_allow_html=True) | |
uploaded_resume = st.file_uploader( | |
"π Upload your Resume (PDF or Word):", type=["pdf", "docx"] | |
) | |
job_description = st.text_area("π Paste the Job Description:") | |
if uploaded_resume: | |
# Extract text based on file type | |
if uploaded_resume.name.endswith(".pdf"): | |
resume_text = extract_text_from_pdf(uploaded_resume) | |
elif uploaded_resume.name.endswith(".docx"): | |
resume_text = extract_text_from_docx(uploaded_resume) | |
else: | |
st.error("Unsupported file format. Please upload a PDF or Word document.") | |
return | |
# Analyze ATS-friendliness | |
st.markdown("<div class='subtitle'>π οΈ ATS Analysis</div>", unsafe_allow_html=True) | |
ats_score, ats_suggestions = check_ats_friendly(resume_text) | |
st.metric("ATS Score", f"{ats_score}/100") | |
if ats_score >= 80: | |
st.success("β Your resume is ATS-friendly!") | |
else: | |
st.warning("β οΈ Your resume needs improvement to be ATS-friendly.") | |
# Display ATS suggestions | |
st.markdown("<div class='subtitle'>π Suggestions for ATS Improvement</div>", unsafe_allow_html=True) | |
if ats_suggestions: | |
for idx, suggestion in enumerate(ats_suggestions, 1): | |
st.markdown(f"<div class='suggestions'>{idx}. {suggestion}</div>", unsafe_allow_html=True) | |
else: | |
st.write("Your resume is well-optimized for ATS!") | |
st.markdown("<hr class='separator'>", unsafe_allow_html=True) | |
# Analyze Job Fit | |
if job_description.strip(): | |
st.markdown("<div class='subtitle'>π Job Description Matching</div>", unsafe_allow_html=True) | |
job_fit_score, job_fit_suggestions, missing_keywords = calculate_job_fit_score( | |
resume_text, job_description | |
) | |
st.metric("Job Fit Score", f"{job_fit_score:.2f}%") | |
if job_fit_score >= 70: | |
st.success("β Your resume matches well with the job description!") | |
else: | |
st.warning("β οΈ Your resume could be tailored better to match the job description.") | |
# Display job fit suggestions | |
st.markdown("<div class='subtitle'>π Missing Keywords</div>", unsafe_allow_html=True) | |
if missing_keywords: | |
st.write("To improve matching, consider including these keywords:") | |
st.markdown(f"<div class='suggestions'>{', '.join(missing_keywords)}</div>", unsafe_allow_html=True) | |
else: | |
st.write("Your resume already includes all the required keywords!") | |
if __name__ == "__main__": | |
main() | |