Untitled
unknown
plain_text
9 months ago
6.8 kB
16
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