File size: 4,948 Bytes
f833cab |
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 |
import io
import json
from typing import Optional
import gradio as gr
import PyPDF2
from resume_ai import score, improve
def extract_text_from_pdf(file_obj: io.IOBase) -> str:
"""Extract text from a PDF file-like object."""
try:
reader = PyPDF2.PdfReader(file_obj)
text_chunks = []
for page in reader.pages:
page_text = page.extract_text() or ""
text_chunks.append(page_text)
text = "\n".join(text_chunks).strip()
if not text:
raise ValueError("No extractable text found in PDF.")
return text
except Exception as e:
raise ValueError(f"Error reading PDF: {e}")
def read_resume_to_text(resume_file_path) -> str:
"""
Accepts a file path and returns text content.
Supports PDF and plain text files.
"""
if resume_file_path is None:
raise ValueError("Please upload a resume file.")
filename = str(resume_file_path).lower()
if filename.endswith(".pdf"):
with open(resume_file_path, "rb") as f:
return extract_text_from_pdf(f)
else:
with open(resume_file_path, "rb") as f:
data = f.read()
if not data:
raise ValueError("Uploaded file is empty.")
try:
return data.decode("utf-8").strip()
except UnicodeDecodeError:
return data.decode("latin-1").strip()
def score_fn(resume_file_path, job_desc: str) -> str:
try:
if not job_desc or not job_desc.strip():
raise ValueError("Please paste a job description.")
resume_text = read_resume_to_text(resume_file_path)
result = score(resume_text, job_desc)
return json.dumps(result, indent=2, ensure_ascii=False)
except Exception as e:
return f"Error: {e}"
def improve_fn(resume_file_path, job_desc: Optional[str]) -> str:
try:
resume_text = read_resume_to_text(resume_file_path)
jd_text = job_desc if job_desc and job_desc.strip() else None
suggestions = improve(resume_text, jd_text)
if isinstance(suggestions, (list, tuple)):
bullets = "\n".join(f"- {s}" for s in suggestions)
return f"### Suggestions\n{bullets}"
elif isinstance(suggestions, dict):
return "```json\n" + json.dumps(suggestions, indent=2, ensure_ascii=False) + "\n```"
else:
return str(suggestions)
except Exception as e:
return f"Error: {e}"
def format_score_display(result_json) -> str:
"""
Takes the result JSON (as dict or str), parses it, and returns a Markdown string for display.
"""
if isinstance(result_json, str):
try:
result = json.loads(result_json)
except Exception:
return f"```\n{result_json}\n```"
else:
result = result_json
md = f"## π ATS Compatibility Score: **{result.get('overall_score', 0)}%**\n\n"
md += "### Category Scores\n"
md += "| Skills | Experience | Education |\n"
md += "|--------|------------|-----------|\n"
cs = result.get("category_scores", {})
md += f"| {cs.get('skills',0)}% | {cs.get('experience',0)}% | {cs.get('education',0)}% |\n\n"
gaps = result.get("top_skill_gaps", [])
if gaps:
md += "### π© Top Skill Gaps\n"
for gap in gaps:
md += f"- {gap}\n"
return md
# ...existing code...
with gr.Blocks(title="Resume AI (Score & Improve)") as demo:
gr.Markdown(
"""
# π Resume AI β Score & Improve
Upload your resume (PDF or TXT), paste a Job Description, and get:
- **Score**: A formatted breakdown of your resume's ATS compatibility
- **Improve**: A healthy set of suggestions to enhance your resume
"""
)
with gr.Row():
resume = gr.File(label="Upload Resume (PDF or TXT)", file_types=[".pdf", ".txt"], type="filepath")
jd = gr.Textbox(label="Job Description (paste here)", lines=10, placeholder="Paste JD text...")
with gr.Row():
score_btn = gr.Button("βοΈ Score Resume", variant="primary")
improve_btn = gr.Button("β¨ Improve Resume")
score_out = gr.Markdown(label="Score (Formatted)")
improve_out = gr.Markdown(label="Improvement Suggestions")
def score_fn_display(resume_file_path, job_desc: str) -> str:
try:
if not job_desc or not job_desc.strip():
raise ValueError("Please paste a job description.")
resume_text = read_resume_to_text(resume_file_path)
result = score(resume_text, job_desc)
return format_score_display(result)
except Exception as e:
return f"Error: {e}"
score_btn.click(fn=score_fn_display, inputs=[resume, jd], outputs=score_out)
improve_btn.click(fn=improve_fn, inputs=[resume, jd], outputs=improve_out)
if __name__ == "__main__":
demo.launch(server_name="0.0.0.0",server_port=7860, pwa=True) |