|
|
import gradio as gr |
|
|
import pdfplumber |
|
|
import spacy |
|
|
import os |
|
|
|
|
|
|
|
|
try: |
|
|
nlp = spacy.load("en_core_web_sm", disable=["ner", "lemmatizer"]) |
|
|
except Exception as e: |
|
|
raise RuntimeError(f"Failed to load spaCy model: {str(e)}") |
|
|
|
|
|
def generate_portfolio(resume_file): |
|
|
|
|
|
if resume_file is None: |
|
|
return "No file uploaded. Please upload a PDF resume." |
|
|
|
|
|
|
|
|
try: |
|
|
with pdfplumber.open(resume_file) as pdf: |
|
|
text = "".join(page.extract_text() or "" for page in pdf.pages) |
|
|
except Exception as e: |
|
|
return f"Error processing PDF: {str(e)}" |
|
|
|
|
|
|
|
|
doc = nlp(text) |
|
|
data = { |
|
|
"name": "", |
|
|
"summary": "", |
|
|
"experience": [], |
|
|
"skills": [], |
|
|
"contact": "" |
|
|
} |
|
|
for ent in doc.ents: |
|
|
if ent.label_ == "PERSON" and not data["name"]: |
|
|
data["name"] = ent.text |
|
|
elif ent.label_ == "ORG": |
|
|
data["experience"].append({"company": ent.text, "role": ""}) |
|
|
elif ent.label_ == "EMAIL": |
|
|
data["contact"] = ent.text |
|
|
|
|
|
|
|
|
skill_keywords = ["python", "javascript", "sql", "communication", "leadership"] |
|
|
data["skills"] = [token.text for token in doc if token.text.lower() in skill_keywords] |
|
|
data["summary"] = text[:200] + "..." |
|
|
|
|
|
|
|
|
os.remove(resume_file) |
|
|
|
|
|
|
|
|
portfolio_html = f""" |
|
|
<!DOCTYPE html> |
|
|
<html lang="en"> |
|
|
<head> |
|
|
<meta charset="UTF-8"> |
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
|
<title>{data['name']}'s Portfolio</title> |
|
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet"> |
|
|
</head> |
|
|
<body> |
|
|
<header class="bg-primary text-white text-center py-5"> |
|
|
<h1>{data['name']}</h1> |
|
|
<p>Professional Portfolio</p> |
|
|
</header> |
|
|
<div class="container my-5"> |
|
|
<section> |
|
|
<h2>About Me</h2> |
|
|
<p>{data['summary']}</p> |
|
|
</section> |
|
|
<section> |
|
|
<h2>Experience</h2> |
|
|
{''.join([f'<div class="card mb-3"><div class="card-body"><h5 class="card-title">{exp["company"]}</h5><p class="card-text">{exp["role"]}</p></div></div>' for exp in data['experience']])} |
|
|
</section> |
|
|
<section> |
|
|
<h2>Skills</h2> |
|
|
<ul class="list-group"> |
|
|
{''.join([f'<li class="list-group-item">{skill}</li>' for skill in data['skills']])} |
|
|
</ul> |
|
|
</section> |
|
|
<section> |
|
|
<h2>Contact</h2> |
|
|
<p>Email: {data['contact']}</p> |
|
|
</section> |
|
|
</div> |
|
|
<footer class="bg-dark text-white text-center py-3"> |
|
|
<p>© 2025 {data['name']}</p> |
|
|
<p>Generated by Resume-to-Portfolio AI. Your data was processed securely and not retained.</p> |
|
|
</footer> |
|
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script> |
|
|
</body> |
|
|
</html> |
|
|
""" |
|
|
return portfolio_html |
|
|
|
|
|
|
|
|
interface = gr.Interface( |
|
|
fn=generate_portfolio, |
|
|
inputs=gr.inputs.File(label="Upload Resume (PDF)"), |
|
|
outputs=gr.outputs.HTML(label="Your Portfolio"), |
|
|
title="Resume to Portfolio Generator", |
|
|
description="Upload your resume to generate a portfolio landing page. Your data is processed securely and not stored.", |
|
|
allow_flagging="never" |
|
|
) |
|
|
|
|
|
interface.launch() |