"""Resume-to-job-description skill matcher powered by spaCy EntityRuler.""" from pathlib import Path import gradio as gr import jsonlines import spacy from spacy.cli import download DATA_DIR = Path(__file__).parent SKILL_PATTERNS = DATA_DIR / "skill_patterns.jsonl" # Ensure the small English model exists before the Space starts serving traffic. try: nlp = spacy.load("en_core_web_sm") except OSError: download("en_core_web_sm") nlp = spacy.load("en_core_web_sm") # Load custom skill patterns into the pipeline once at startup. ruler = nlp.add_pipe("entity_ruler", after="parser") ruler.from_disk(str(SKILL_PATTERNS)) def create_skill_set(doc): """Return upper-cased skill labels found in a spaCy Doc.""" return { ent.label_.upper()[6:] for ent in doc.ents if "skill" in ent.label_.lower() } def match_skills(job_skills, resume_skills): """Return the percent of job skills also present in the resume.""" if not job_skills: return None return round(len(job_skills.intersection(resume_skills)) / len(job_skills) * 100, 0) def match(resume_text, job_description): """Compare extracted skills and return a match score.""" resume_text = (resume_text or "").strip() job_description = (job_description or "").strip() if not resume_text or not job_description: return "Paste both a resume and a job description." resume_skills = create_skill_set(nlp(resume_text.lower())) job_skills = create_skill_set(nlp(job_description.lower())) score = match_skills(job_skills, resume_skills) if score is None: return "No matching skill set." return score demo = gr.Interface( fn=match, inputs=[ gr.Textbox(lines=10, label="Resume details"), gr.Textbox(lines=10, label="Job description"), ], outputs=gr.Textbox(label="Match score"), title="Resume Matcher", description=( "A spaCy-based resume matcher that compares resume skills with a job " "description and returns a match percentage." ), examples=[ [ "Experienced Python developer with machine learning, SQL, and Docker.", "Looking for a Python engineer with machine learning and cloud experience.", ] ], ) if __name__ == "__main__": demo.queue().launch()