import tempfile import pandas as pd import streamlit as st from pathlib import Path from src.extractor import extract_text, get_pdf_preview from src.matcher import compute_match from config import APP_ICON, APP_TITLE from core.logger import get_logger logger = get_logger(__name__) st.set_page_config(page_title = APP_TITLE, page_icon = APP_ICON, layout = "wide" ) #initalising the session state if "resumes" not in st.session_state: st.session_state.resume = {} if "selected_resume" not in st.session_state: st.session_state.selected_resume = None if "jd_text" not in st.session_state: st.session_state.jd_text = "" if "results" not in st.session_state: st.session_state.results = {} if "previews" not in st.session_state: st.session_state.previews = {} st.title(f"{APP_ICON} {APP_TITLE}") st.caption("Upload resume · Paste a job description · Get instant match analysis") st.divider() col_left, col_right = st.columns(2) with col_left: st.subheader("Resumes") upload_files = st.file_uploader("Upload one or more resumes", type = ["pdf"], accept_multiple_files = True) #Process the upload files if upload_files: for file in upload_files: name = file.name if name not in st.session_state.resume: with st.spinner(f"Extracting {name}..."): try: pdf_bytes = file.read() preview_bytes = get_pdf_preview(pdf_bytes) st.session_state.previews[name] = preview_bytes with tempfile.NamedTemporaryFile(suffix = ".pdf", delete = False, dir="/tmp" ) as temp: temp.write(pdf_bytes) temp_path = Path(temp.name) text = extract_text(temp_path) temp_path.unlink() st.session_state.resume[name] = text logger.info(f"Resume stored: {name}") st.success(f"{name} extracted") except Exception as e: logger.error(f"Failed to extract: {name} : {e}") st.error(f"Failed to rad: {name}") if st.session_state.resume: st.divider() resume_names = list(st.session_state.resume.keys()) selected = st.selectbox("Select resume to preview", options = resume_names, key = "selected_resume") if selected: st.markdown("**Fist page Preview: **") if selected in st.session_state.previews: st.image( st.session_state.previews[selected], caption = selected, use_container_width = True ) st.caption(f"Extracted: {len(st.session_state.resume[selected])} chars") with col_right: st.subheader("Job Discription") jd_text = st.text_area( "Paste the job discription here", height = 400, placeholder = "e.g. We are looking for AI Engineer ...", key = "jd_text" ) if len(jd_text) < 500: st.warning(f"JD seems too short — add more details for better matching") elif len(jd_text) > 5000: st.warning(f"JD is very long — consider pasting key requirements only") else: st.caption(f"{len(jd_text.split())} words — good length") st.divider() col_empty_left, col_btn, ccol_empty_right = st.columns([3, 4, 3]) with col_btn: analyze_clicked = st.button( "Analyze Match", type = "primary", use_container_width = True ) if analyze_clicked: if not st.session_state.resume: st.warning("Please upload at least one Resume") elif not jd_text.strip(): st.warning("Please paste a job description") else: selected = st.session_state.selected_resume resume_text = st.session_state.resume[selected] with st.spinner(f"Analysing {selected}..."): try: result = compute_match(resume_text, jd_text) st.session_state.results[selected] = result logger.info(f"Analysis complete: {selected}") except Exception as e: logger.error(f"Analysis failed for {selected}: {e}") st.error("Analysis failed - please try again") if st.session_state.results: selected = st.session_state.selected_resume if selected in st.session_state.results: results = st.session_state.results[selected] st.divider() st.subheader(f"Results - {selected}") col1, col2, col3 = st.columns(3) with col1: st.metric( label = "match Score", value = f"{result['match_percentage']}%" ) with col2: st.metric( label = "Missing Skills", value = len(result['missing_skills']) ) with col3: st.metric( label="Matched Skills", value=len(result['match_skills']) ) verdict = result['verdict'] if verdict == "Strong Match": st.success(f"{verdict}") elif verdict == "Good Match": st.info(f"{verdict}") elif verdict == "Weak Match": st.warning(f"{verdict}") else: st.error(f"{verdict}") col_match, col_miss = st.columns(2) with col_match: st.markdown("**Matched Skills**") for skill in result['match_skills']: st.markdown(f"- {skill}") with col_miss: st.markdown("**Missing Skills**") for skill in result["missing_skills"]: st.markdown(f"- {skill}") st.divider() st.subheader("Summary - All Resumes") if not st.session_state.results: st.info("Analyse resume to see summary here") else: row = [] for resume_name, result in st.session_state.results.items(): row.append({ "Resume": resume_name, "Match%": result["match_percentage"], "Matched Skills": len(result["match_skills"]), "Missing Skills": len(result["missing_skills"]), "Verdict": result["verdict"] }) df = pd.DataFrame(row) df = df.sort_values("Match%", ascending = False) st.dataframe( df, use_container_width = True, hide_index = True )