|
|
import gradio as gr |
|
|
import fitz |
|
|
import docx |
|
|
from sentence_transformers import SentenceTransformer, util |
|
|
from gtts import gTTS |
|
|
import tempfile |
|
|
|
|
|
|
|
|
model = SentenceTransformer("all-MiniLM-L6-v2") |
|
|
|
|
|
|
|
|
top_skills = [ |
|
|
|
|
|
"Python", "JavaScript", "SQL", "Machine Learning", "AWS", "Docker", "React", |
|
|
|
|
|
"SEO", "Content Creation", "Brand Management", "Google Analytics", "Social Media Marketing", |
|
|
|
|
|
"Financial Analysis", "Accounting", "Risk Management", |
|
|
|
|
|
"Patient Care", "Diagnosis", "Treatment Planning", |
|
|
|
|
|
"Lesson Planning", "Curriculum Design", "Classroom Management", |
|
|
|
|
|
"Communication", "Leadership", "Teamwork", "Problem Solving", "Critical Thinking" |
|
|
] |
|
|
|
|
|
|
|
|
job_roles = [ |
|
|
("Data Analyst", {"Python", "SQL", "Data Analysis"}), |
|
|
("Frontend Developer", {"JavaScript", "React"}), |
|
|
("Backend Developer", {"Python", "Docker", "AWS"}), |
|
|
("Machine Learning Engineer", {"Machine Learning", "Python"}), |
|
|
("Marketing Manager", {"SEO", "Content Creation", "Brand Management"}), |
|
|
("Digital Marketer", {"Google Analytics", "Social Media Marketing", "Content Creation"}), |
|
|
("Financial Analyst", {"Financial Analysis", "Accounting"}), |
|
|
("Risk Manager", {"Risk Management", "Accounting"}), |
|
|
("Nurse", {"Patient Care", "Diagnosis"}), |
|
|
("Healthcare Administrator", {"Treatment Planning", "Leadership"}), |
|
|
("Teacher", {"Lesson Planning", "Classroom Management"}), |
|
|
("Project Manager", {"Leadership", "Project Management"}) |
|
|
] |
|
|
|
|
|
|
|
|
def extract_text_from_pdf(pdf_file): |
|
|
doc = fitz.open(pdf_file) |
|
|
text = "" |
|
|
for page in doc: |
|
|
text += page.get_text() |
|
|
return text |
|
|
|
|
|
|
|
|
def extract_text_from_docx(docx_file): |
|
|
doc = docx.Document(docx_file) |
|
|
return "\n".join([p.text for p in doc.paragraphs]) |
|
|
|
|
|
|
|
|
def detect_skills(text): |
|
|
sentences = [s.strip() for s in text.split(".") if len(s.strip()) > 5] |
|
|
if not sentences: |
|
|
return [], [] |
|
|
|
|
|
sentence_embeddings = model.encode(sentences, convert_to_tensor=True) |
|
|
skill_embeddings = model.encode(top_skills, convert_to_tensor=True) |
|
|
|
|
|
cosine_scores = util.cos_sim(skill_embeddings, sentence_embeddings) |
|
|
|
|
|
found_skills = [] |
|
|
for idx, skill in enumerate(top_skills): |
|
|
score = max(cosine_scores[idx]) |
|
|
if score > 0.4: |
|
|
found_skills.append(skill) |
|
|
|
|
|
missing_skills = [s for s in top_skills if s not in found_skills] |
|
|
return found_skills, missing_skills |
|
|
|
|
|
|
|
|
def suggest_jobs(found_skills): |
|
|
matched_jobs = [] |
|
|
skill_set = set(found_skills) |
|
|
|
|
|
scored_jobs = [] |
|
|
for title, required_skills in job_roles: |
|
|
matches = skill_set & required_skills |
|
|
score = len(matches) / len(required_skills) |
|
|
if score > 0: |
|
|
scored_jobs.append((title, score)) |
|
|
|
|
|
if scored_jobs: |
|
|
scored_jobs.sort(key=lambda x: x[1], reverse=True) |
|
|
matched_jobs = [title for title, _ in scored_jobs[:3]] |
|
|
else: |
|
|
matched_jobs = ["General Roles"] |
|
|
|
|
|
return matched_jobs |
|
|
|
|
|
|
|
|
def recommend_skills(found_skills, matched_jobs): |
|
|
skill_set = set(found_skills) |
|
|
|
|
|
if matched_jobs == ["General Roles"]: |
|
|
return [s for s in top_skills if s not in skill_set][:5] |
|
|
|
|
|
relevant_skills = set() |
|
|
for job, required_skills in job_roles: |
|
|
if job in matched_jobs: |
|
|
relevant_skills |= required_skills |
|
|
|
|
|
missing_in_domain = [s for s in relevant_skills if s not in skill_set] |
|
|
|
|
|
if len(missing_in_domain) < 5: |
|
|
others = [s for s in top_skills if s not in skill_set and s not in missing_in_domain] |
|
|
missing_in_domain.extend(others[:5 - len(missing_in_domain)]) |
|
|
|
|
|
return missing_in_domain[:5] |
|
|
|
|
|
|
|
|
def summarize_resume(found_skills): |
|
|
if not found_skills: |
|
|
return "No clear skills detected in resume." |
|
|
return "This resume highlights skills in " + ", ".join(found_skills) + "." |
|
|
|
|
|
|
|
|
def score_resume(found_skills): |
|
|
score = int(len(found_skills) / len(top_skills) * 100) |
|
|
return min(score, 100) |
|
|
|
|
|
|
|
|
def save_report(text, skills, jobs, missing, summary, score): |
|
|
report = f""" |
|
|
--- Resume Analysis Report --- |
|
|
|
|
|
Summary: |
|
|
{summary} |
|
|
|
|
|
Resume Text: |
|
|
{text} |
|
|
|
|
|
Detected Skills: |
|
|
{skills} |
|
|
|
|
|
Suggested Job Titles: |
|
|
{jobs} |
|
|
|
|
|
Recommended Skills to Learn: |
|
|
{missing} |
|
|
|
|
|
Resume Score: {score}/100 |
|
|
""" |
|
|
tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".txt", mode='w', encoding='utf-8') |
|
|
tmp.write(report) |
|
|
tmp.close() |
|
|
return tmp.name |
|
|
|
|
|
|
|
|
def text_to_speech(jobs): |
|
|
tts = gTTS(f"Based on your resume, recommended job titles are: {jobs}") |
|
|
tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") |
|
|
tts.save(tmp.name) |
|
|
return tmp.name |
|
|
|
|
|
|
|
|
def process_resume(file_obj): |
|
|
filename = file_obj.name |
|
|
ext = filename.split(".")[-1].lower() |
|
|
|
|
|
if ext == "pdf": |
|
|
resume_text = extract_text_from_pdf(filename) |
|
|
elif ext == "txt": |
|
|
resume_text = file_obj.read().decode("utf-8") |
|
|
elif ext == "docx": |
|
|
resume_text = extract_text_from_docx(filename) |
|
|
else: |
|
|
return "Unsupported file format", "", "", "", "", "", None, None |
|
|
|
|
|
found_skills, _ = detect_skills(resume_text) |
|
|
matched_jobs = suggest_jobs(found_skills) |
|
|
recommended = recommend_skills(found_skills, matched_jobs) |
|
|
|
|
|
summary = summarize_resume(found_skills) |
|
|
skills_str = ", ".join(found_skills) |
|
|
jobs_str = ", ".join(matched_jobs) |
|
|
missing_str = ", ".join(recommended) |
|
|
score = score_resume(found_skills) |
|
|
report_path = save_report(resume_text, skills_str, jobs_str, missing_str, summary, score) |
|
|
audio_path = text_to_speech(jobs_str) |
|
|
|
|
|
return resume_text, summary, skills_str, jobs_str, missing_str, f"{score}/100", report_path, audio_path |
|
|
|
|
|
|
|
|
|
|
|
with gr.Blocks(theme="soft") as demo: |
|
|
gr.Markdown(""" |
|
|
# <span style='color:#2c3e50'>πβ¨ Resume Analyzer & Job Matcher</span> |
|
|
Upload your **resume** and let <span style='color:#1abc9c'>AI</span> analyze it for: |
|
|
- π§ **Summary of your profile** |
|
|
- β
Detected **skills** |
|
|
- πΌ Suggested **job titles** |
|
|
- π Recommended **skills to learn** |
|
|
- π― **Resume score** |
|
|
- π Audio feedback |
|
|
- π Downloadable report |
|
|
|
|
|
Supported formats: `.pdf`, `.docx`, `.txt` |
|
|
""") |
|
|
|
|
|
with gr.Row(): |
|
|
with gr.Column(): |
|
|
file = gr.File(label="π€ Upload your resume here") |
|
|
btn = gr.Button("π Analyze Resume", elem_classes="analyze-btn") |
|
|
|
|
|
with gr.Tabs(): |
|
|
with gr.Tab("π Resume Text"): |
|
|
resume_text = gr.Textbox(label="Your Resume Text", lines=15, interactive=False) |
|
|
|
|
|
with gr.Tab("π§ Summary"): |
|
|
summary = gr.Textbox(label="Resume Summary", interactive=False) |
|
|
|
|
|
with gr.Tab("π§° Skills & Jobs"): |
|
|
skills = gr.Textbox(label="Detected Skills", interactive=False) |
|
|
jobs = gr.Textbox(label="Suggested Job Titles", interactive=False) |
|
|
missing = gr.Textbox(label="Recommended Skills to Learn", interactive=False) |
|
|
|
|
|
with gr.Tab("π Score"): |
|
|
score = gr.Textbox(label="Resume Score", interactive=False) |
|
|
|
|
|
with gr.Tab("π Download & π Audio"): |
|
|
report = gr.File(label="π Download Analysis Report") |
|
|
audio = gr.Audio(label="π Job Titles Audio") |
|
|
|
|
|
btn.click( |
|
|
fn=process_resume, |
|
|
inputs=file, |
|
|
outputs=[ |
|
|
resume_text, summary, skills, jobs, missing, score, report, audio |
|
|
] |
|
|
) |
|
|
|
|
|
if __name__ == "__main__": |
|
|
demo.launch() |
|
|
|