Untitled

 avatar
unknown
plain_text
a month ago
6.8 kB
2
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