Spaces:
Sleeping
Sleeping
File size: 5,554 Bytes
b599927 e8ef9d3 b599927 e8ef9d3 ff761e7 b599927 4aa5719 b599927 e8ef9d3 b599927 f64ab2b ff761e7 f64ab2b 1f5134c ff761e7 b599927 1f5134c ff761e7 596c61d b599927 ff761e7 b599927 1f5134c b599927 3f73843 dfa9f62 5b412c3 dfa9f62 5b412c3 dfa9f62 5b412c3 dfa9f62 5b412c3 dfa9f62 5b412c3 dfa9f62 5b412c3 dfa9f62 5b412c3 b599927 1f5134c b599927 1f5134c b599927 1f5134c b599927 ff761e7 4aa5719 ff761e7 b599927 dfa9f62 3f73843 b599927 1f5134c b599927 4aa5719 b599927 ff761e7 b599927 ff761e7 b599927 ff761e7 1f5134c | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 | # app.py
import gradio as gr
import pdfplumber
import spacy
import os
import json
import matplotlib.pyplot as plt
import tempfile
import re
from wordcloud import WordCloud
# Load NLP model
try:
nlp = spacy.load("en_core_web_sm")
except:
from spacy.cli import download
download("en_core_web_sm")
nlp = spacy.load("en_core_web_sm")
# --- Helper functions ---
def extract_text_from_pdf(file):
with pdfplumber.open(file) as pdf:
text = "\n".join([page.extract_text() or "" for page in pdf.pages])
return clean_text(text)
def clean_text(text):
import re
text = re.sub(r'\n+', '\n', text)
text = re.sub(r'[^\x00-\x7F]+', ' ', text) # remove non-ASCII
text = re.sub(r'\s+', ' ', text) # collapse multiple spaces
return text.strip()
def extract_contact_info(text):
email = re.search(r"[\w.+-]+@[\w-]+\.[\w.-]+", text)
phone = re.search(r"\b\d{4}[-.\s]?\d{7}\b|\b\d{11}\b", text)
return email.group(0) if email else None, phone.group(0) if phone else None
def extract_skills(text):
words = set(re.findall(r"\b\w+\b", text.lower()))
custom_keywords = re.findall(r"(?i)(?<=skills[:\-\n]).+?(?=education|experience|projects|certification|$)", text, re.DOTALL)
skill_keywords = [
"python", "sql", "ml", "ai", "keras", "tensorflow", "html", "css", "nltk",
"pandas", "numpy", "power bi", "excel", "scikit-learn", "seaborn", "flask", "django"
]
found = {k for k in skill_keywords if k in words}
for chunk in custom_keywords:
found.update([s.strip().lower() for s in re.split(r",|\n|\||\s{2,}", chunk) if len(s.strip()) >= 2])
return sorted(set(found))
def extract_section(text, section_titles):
lines = text.split("\n")
section_data = []
capture = False
for line in lines:
clean_line = line.strip()
if not clean_line:
continue
# Detect section start
if any(title.lower() in clean_line.lower() for title in section_titles):
capture = True
continue
# Stop if a new main section starts
if capture and re.match(r"^(skills|certifications|projects|summary|languages|contact|references|achievements|publications)\s*[:\-]?$", clean_line, re.IGNORECASE):
break
# Collect section lines
if capture:
section_data.append(clean_line)
return section_data
def match_job_description(skills, job_desc):
job_desc = job_desc.lower()
matched_skills = [s for s in skills if s.lower() in job_desc]
return matched_skills, len(matched_skills), len(skills)
def visualize_skills(skills):
if not skills:
return None
plt.figure(figsize=(6, 4))
freq = {s: skills.count(s) for s in skills}
plt.bar(freq.keys(), freq.values(), color="skyblue")
plt.xticks(rotation=45)
plt.title("Skills Frequency")
plt.tight_layout()
temp_chart = tempfile.NamedTemporaryFile(delete=False, suffix=".png")
plt.savefig(temp_chart.name)
return temp_chart.name
def parse_resume(file):
text = extract_text_from_pdf(file.name)
doc = nlp(text)
name = next((ent.text for ent in doc.ents if ent.label_ == "PERSON"), "Not Found")
email, phone = extract_contact_info(text)
skills = extract_skills(text)
education = extract_section(text, ["education", "academic background", "qualification", "university", "degree", "bachelor", "master"])
experience = extract_section(text, ["experience", "professional experience", "work history", "employment", "internship", "projects"])
print("=== DEBUG TEXT ===")
print(text)
parsed = {
"Name": name,
"Email": email,
"Phone": phone,
"Skills": skills,
"Education": education,
"Experience": experience,
}
chart = visualize_skills(skills)
return parsed, chart
def download_json(data):
with tempfile.NamedTemporaryFile(delete=False, suffix=".json", mode="w") as f:
json.dump(data, f, indent=4)
return f.name
def job_matcher(resume_json, job_desc):
skills = resume_json.get("Skills", [])
matched_skills, matched_count, total_skills = match_job_description(skills, job_desc)
return f"β
Matched Skills: {matched_skills}\nπ― Match Score: {matched_count} / {total_skills}"
# --- Gradio Interface ---
with gr.Blocks(title="π§ AI Resume Parser") as demo:
gr.Markdown("""
# π§ AI Resume Parser (with Skills Chart, JSON Download, and Job Match)
Upload a resume and extract structured data with NLP
""")
with gr.Row():
resume_input = gr.File(label="Upload Resume (PDF)", file_types=[".pdf"])
parse_btn = gr.Button("π Parse Resume")
parsed_output = gr.JSON(label="π§Ύ Parsed Resume")
chart_output = gr.Image(label="π Skills Chart")
download_btn = gr.File(label="β¬οΈ Download Resume JSON")
with gr.Accordion("π Match with Job Description", open=False):
job_desc_input = gr.Textbox(label="Paste Job Description", lines=5)
match_btn = gr.Button("π Match Skills")
match_output = gr.Text(label="Match Result")
def full_pipeline(file):
parsed, chart_path = parse_resume(file)
json_path = download_json(parsed)
return parsed, chart_path, json_path
parse_btn.click(fn=full_pipeline, inputs=[resume_input], outputs=[parsed_output, chart_output, download_btn])
match_btn.click(fn=job_matcher, inputs=[parsed_output, job_desc_input], outputs=[match_output])
if __name__ == "__main__":
demo.launch() |