import os # ---------------- SAFE CACHE ---------------- os.environ["HF_HOME"] = "/tmp/hf" os.environ["TRANSFORMERS_CACHE"] = "/tmp/hf" os.environ["TORCH_HOME"] = "/tmp/torch" # ---------------- IMPORTS ---------------- import streamlit as st import pdfplumber import re import pandas as pd import spacy import json from keybert import KeyBERT from sentence_transformers import SentenceTransformer, util from streamlit_lottie import st_lottie # ========================================================= # PAGE CONFIG # ========================================================= st.set_page_config( page_title="AI Resume ATS Matcher", page_icon="📄", layout="wide" ) # ========================================================= # CUSTOM CSS (LIGHT MODERN UI) # ========================================================= st.markdown(""" """, unsafe_allow_html=True) # ========================================================= # LOAD MODELS # ========================================================= @st.cache_resource def load_models(): model = SentenceTransformer("all-MiniLM-L6-v2") nlp = spacy.load("en_core_web_sm") kw_model = KeyBERT(model=model) return model, nlp, kw_model model, nlp, kw_model = load_models() # ========================================================= # LOAD ANIMATION # ========================================================= def load_lottiefile(filepath): try: with open(filepath, "r") as f: return json.load(f) except: return None lottie_animation = load_lottiefile("Animation.json") # ========================================================= # HEADER # ========================================================= if lottie_animation: st_lottie(lottie_animation, height=180) st.markdown( '
🤖 AI Resume ATS
', unsafe_allow_html=True ) st.markdown( '
Upload multiple resumes and instantly rank the best candidates
', unsafe_allow_html=True ) # ========================================================= # HELPERS # ========================================================= def extract_text_from_pdf(pdf_file): text = "" with pdfplumber.open(pdf_file) as pdf: for page in pdf.pages: page_text = page.extract_text() if page_text: text += page_text + "\n" return text.strip() def clean_text(text): return re.sub(r"\s+", " ", text).strip() def extract_skills_auto(text, top_n=15): text = text[:3000] keywords = kw_model.extract_keywords( text, keyphrase_ngram_range=(1, 3), stop_words='english', top_n=top_n ) return [kw[0] for kw in keywords] def calculate_similarity(text1, text2): emb1 = model.encode(text1, convert_to_tensor=True) emb2 = model.encode(text2, convert_to_tensor=True) score = util.pytorch_cos_sim( emb1, emb2 ).item() return round(score * 100, 2) # ========================================================= # INPUT SECTION # ========================================================= #st.markdown('
', unsafe_allow_html=True) st.markdown("### 📄 Upload Resumes") resume_files = st.file_uploader( "Upload Multiple PDF Resumes", type=["pdf"], accept_multiple_files=True ) st.markdown("### 📝 Paste Job Description") job_description = st.text_area( "Enter Job Description", height=220, placeholder="Paste the job description here..." ) st.markdown('
', unsafe_allow_html=True) # ========================================================= # MATCH BUTTON # ========================================================= if st.button("🚀 Match Resumes"): if resume_files and job_description: with st.spinner("Analyzing resumes..."): jd_skills = extract_skills_auto(job_description) results = [] progress_bar = st.progress(0) total_files = len(resume_files) # ========================================================= # PROCESS RESUMES # ========================================================= for idx, resume_file in enumerate(resume_files): try: resume_text = clean_text( extract_text_from_pdf(resume_file) ) resume_skills = extract_skills_auto( resume_text ) score = calculate_similarity( " ".join(resume_skills), " ".join(jd_skills) ) results.append({ "Resume": resume_file.name, "ATS Score": score, "Skills": resume_skills[:8] }) except Exception as e: results.append({ "Resume": resume_file.name, "ATS Score": 0, "Skills": [f"Error: {str(e)}"] }) progress_bar.progress((idx + 1) / total_files) # ========================================================= # SORT RESULTS # ========================================================= results = sorted( results, key=lambda x: x["ATS Score"], reverse=True ) # ========================================================= # TOP STATS # ========================================================= st.markdown("## 📊 ATS Analytics") col1, col2, col3 = st.columns(3) with col1: st.metric( "Total Resumes", len(results) ) with col2: st.metric( "Top ATS Score", f"{results[0]['ATS Score']}%" ) with col3: avg_score = round( sum(r["ATS Score"] for r in results) / len(results), 2 ) st.metric( "Average Score", f"{avg_score}%" ) st.divider() # ========================================================= # TABLE # ========================================================= st.markdown("## 🏆 Candidate Rankings") table_data = [] for idx, item in enumerate(results, start=1): table_data.append({ "Rank": idx, "Resume": item["Resume"], "ATS Score": f"{item['ATS Score']}%" }) df = pd.DataFrame(table_data) st.dataframe( df, use_container_width=True ) # ========================================================= # TOP CANDIDATES # ========================================================= st.markdown("## ⭐ Best Candidates") top_candidates = results[:5] for idx, candidate in enumerate(top_candidates, start=1): score = candidate["ATS Score"] # Status if score >= 85: status = "Excellent Match ✅" elif score >= 65: status = "Good Match 👍" else: status = "Average Match ⚠️" # CARD st.markdown( '
', unsafe_allow_html=True ) col1, col2 = st.columns([1, 5]) # LEFT with col1: st.metric( label=f"🏅 Rank #{idx}", value=f"{score}%" ) # RIGHT with col2: st.markdown( f"### 📄 {candidate['Resume']}" ) st.progress(score / 100) st.markdown( f"**Status:** {status}" ) st.markdown("#### 🛠 Matching Skills") skill_html = "" for skill in candidate["Skills"]: skill_html += f""" {skill} """ st.markdown( skill_html, unsafe_allow_html=True ) st.markdown( '
', unsafe_allow_html=True ) # ========================================================= # DOWNLOAD CSV # ========================================================= csv = pd.DataFrame(results).to_csv(index=False).encode("utf-8") st.download_button( label="📥 Download ATS Report CSV", data=csv, file_name="ats_report.csv", mime="text/csv" ) else: st.warning( "⚠️ Please upload resumes and enter a job description." ) # ========================================================= # FOOTER # ========================================================= st.markdown("""


Developed by Ashvinkumar Bari • AI Resume Screening Platform

""", unsafe_allow_html=True)