Untitled
Anonymous
plain_text
03/17/2025 9:11 PM
8.9 KB
29
Indexable
import os
import json
import openai
import spacy
import streamlit as st
import pdfkit
from docx import Document
from zipfile import ZipFile
import tempfile
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
from dotenv import load_dotenv
# Initialize
load_dotenv()
openai.api_key = os.getenv("OPENAI_API_KEY")
nlp = spacy.load("en_core_web_sm")
# ======================
# Core Functions
# ======================
def parse_linkedin_data(zip_path):
"""Extract LinkedIn profile data from ZIP export"""
with ZipFile(zip_path, 'r') as zip_ref:
with zip_ref.open('Profile.json') as file:
data = json.load(file)
profile = data['profile']
return {
'name': f"{profile['firstName']} {profile['lastName']}",
'contact': f"{profile['email']} | {profile['phone']}",
'summary': profile['summary'],
'experience': "\n".join([
f"- {exp['title']} at {exp['companyName']} ({exp['dates']})"
for exp in profile['positionGroups']
]),
'skills': [skill['name'] for skill in profile['skills']],
'education': "\n".join([
f"- {edu['degreeName']} at {edu['schoolName']}"
for edu in profile['education']
])
}
def create_ats_docx(resume_data):
"""Generate ATS-friendly Word document"""
doc = Document()
# Header
doc.add_heading(resume_data['name'], 0)
doc.add_paragraph(resume_data['contact'] + "\n")
# Sections
sections = [
('Professional Summary', resume_data['summary']),
('Work Experience', resume_data['experience']),
('Skills', ', '.join(resume_data['skills'])),
('Education', resume_data['education'])
]
for title, content in sections:
doc.add_heading(title, level=1)
doc.add_paragraph(content)
# Formatting
for paragraph in doc.paragraphs:
paragraph.style.font.name = 'Arial'
paragraph.style.font.size = Pt(11)
return doc
def create_ats_pdf(resume_data):
"""Generate ATS-friendly PDF"""
html_content = f"""
<!DOCTYPE html>
<html>
<head>
<style>
body {{ font-family: Arial; font-size: 11pt; line-height: 1.6; }}
h1 {{ color: #2B3467; border-bottom: 2px solid #2B3467; }}
h2 {{ color: #2B3467; margin-top: 1.5em; }}
</style>
</head>
<body>
<h1>{resume_data['name']}</h1>
<p>{resume_data['contact']}</p>
<h2>Professional Summary</h2>
<p>{resume_data['summary']}</p>
<h2>Work Experience</h2>
<p>{resume_data['experience']}</p>
<h2>Skills</h2>
<p>{', '.join(resume_data['skills'])}</p>
<h2>Education</h2>
<p>{resume_data['education']}</p>
</body>
</html>
"""
config = pdfkit.configuration(wkhtmltopdf='/usr/local/bin/wkhtmltopdf')
pdfkit.from_string(html_content, "resume.pdf", configuration=config)
def calculate_ats_score(resume_text, job_desc):
"""Calculate ATS compatibility score"""
def extract_keywords(text):
doc = nlp(text)
return [token.lemma_.lower() for token in doc
if not token.is_stop and token.pos_ in ['NOUN', 'VERB']]
vectorizer = TfidfVectorizer(tokenizer=extract_keywords)
tfidf_matrix = vectorizer.fit_transform([resume_text, job_desc])
return round(cosine_similarity(tfidf_matrix[0:1], tfidf_matrix[1:2])[0][0] * 100, 1)
def enhance_with_ai(resume_data, job_desc):
"""Improve resume using GPT-3.5"""
prompt = f"""
Optimize this resume for the following job description:
{job_desc}
Current resume data:
{json.dumps(resume_data, indent=2)}
Return ONLY valid JSON with these keys:
- summary (improved professional summary)
- experience (bullet points)
- skills (prioritized list)
- education (formatted)
"""
response = openai.ChatCompletion.create(
model="gpt-3.5-turbo",
messages=[{"role": "user", "content": prompt}],
max_tokens=700
)
return json.loads(response.choices[0].message.content)
# ======================
# Streamlit UI
# ======================
st.set_page_config(page_title="AI Resume Builder", layout="wide")
st.title("🚀 AI-Powered Resume Builder")
# Input Section
col1, col2 = st.columns(2)
with col1:
linkedin_zip = st.file_uploader("Upload LinkedIn Export (ZIP):", type='zip')
with col2:
job_desc = st.text_area("Paste Job Description:", height=150)
# Manual Inputs
if not linkedin_zip:
name = st.text_input("Full Name:")
contact = st.text_input("Contact Info (Email/Phone):")
experience = st.text_area("Work Experience (Bullet Points):")
skills = st.text_input("Skills (comma-separated):")
education = st.text_area("Education:")
if st.button("Generate Resume"):
resume_data = {}
# Process Inputs
if linkedin_zip:
with tempfile.NamedTemporaryFile(delete=False) as tmp:
tmp.write(linkedin_zip.getvalue())
resume_data = parse_linkedin_data(tmp.name)
else:
resume_data = {
'name': name,
'contact': contact,
'summary': "",
'experience': experience,
'skills': [s.strip() for s in skills.split(',')],
'education': education
}
# AI Enhancement
with st.spinner("Optimizing with AI..."):
ai_enhanced = enhance_with_ai(resume_data, job_desc)
resume_data.update(ai_enhanced)
# Generate Documents
with st.spinner("Creating files..."):
# DOCX
doc = create_ats_docx(resume_data)
doc.save("resume.docx")
# PDF
create_ats_pdf(resume_data)
# ATS Score
resume_text = " ".join([
resume_data['summary'],
resume_data['experience'],
" ".join(resume_data['skills']),
resume_data['education']
])
score = calculate_ats_score(resume_text, job_desc)
# Display Results
st.success("Resume Generated!")
st.subheader(f"ATS Compatibility Score: {score}/100")
if score < 70:
st.warning("Improvement needed! Add more keywords from the job description.")
# Download Buttons
col1, col2 = st.columns(2)
with col1:
st.download_button("Download DOCX", open("resume.docx", 'rb'), file_name="resume.docx")
with col2:
st.download_button("Download PDF", open("resume.pdf", 'rb'), file_name="resume.pdf")Editor is loading...
Leave a Comment