Spaces:
Build error
Build error
| 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() | |