mkhekare's picture
Update app.py
b5949d6 verified
# Ensure system dependencies are installed
import os
import tempfile
from utils.parsing_utils import extract_text_from_file, parse_cv_content
from utils.latex_utils import generate_latex, compile_latex_to_pdf
from utils.gemini_utils import generate_ats_optimized_content
from pygments import highlight
from pygments.lexers import TexLexer
from pygments.formatters import HtmlFormatter
import streamlit as st
import os
# Hugging Face Spaces specific configuration
if os.environ.get('IS_HF_SPACE'):
from streamlit.web import cli as stcli
import sys
def main():
sys.argv = [
"streamlit",
"run",
"app.py",
"--server.port=7860",
"--server.address=0.0.0.0",
"--server.headless=true",
"--browser.gatherUsageStats=false",
"--server.enableXsrfProtection=false"
]
sys.exit(stcli.main())
if __name__ == "__main__":
main()
# Set page config
st.set_page_config(
page_title="ATS-Friendly CV Builder",
page_icon="πŸ“„",
layout="centered",
initial_sidebar_state="expanded"
)
# Custom CSS
def local_css(file_name):
with open(file_name) as f:
st.markdown(f'<style>{f.read()}</style>', unsafe_allow_html=True)
local_css("style.css")
# Initialize session state
if 'original_cv_data' not in st.session_state:
st.session_state.original_cv_data = {}
if 'edited_cv_data' not in st.session_state:
st.session_state.edited_cv_data = {}
if 'job_description' not in st.session_state:
st.session_state.job_description = ""
if 'generated_latex' not in st.session_state:
st.session_state.generated_latex = ""
# App header
st.title("πŸ“„ ATS-Friendly CV Builder")
st.markdown("""
Upload your existing CV, customize it, and generate an optimized ATS-friendly resume tailored to any job description.
""")
# File upload section
st.header("1. Upload Your Existing CV")
uploaded_file = st.file_uploader(
"Upload your CV (PDF, DOCX, or TXT)",
type=["pdf", "docx", "txt"],
help="We'll extract the information to create your base resume"
)
if uploaded_file:
with st.spinner("Extracting information from your CV..."):
try:
# Extract text from uploaded file
file_content = extract_text_from_file(uploaded_file)
# Parse the CV content into structured data
cv_data = parse_cv_content(file_content)
# Store in session state
st.session_state.original_cv_data = cv_data
st.session_state.edited_cv_data = cv_data.copy()
st.success("CV information extracted successfully!")
# Show extracted data for verification
with st.expander("View extracted information"):
st.json(cv_data, expanded=False)
except Exception as e:
st.error(f"Error processing your file: {str(e)}")
# Edit CV section
if st.session_state.original_cv_data:
st.header("2. Edit Your Information")
# Personal Information
with st.expander("Personal Information", expanded=True):
cols = st.columns(2)
with cols[0]:
st.session_state.edited_cv_data['name'] = st.text_input(
"Full Name",
value=st.session_state.edited_cv_data.get('name', '')
)
st.session_state.edited_cv_data['email'] = st.text_input(
"Email",
value=st.session_state.edited_cv_data.get('email', '')
)
with cols[1]:
st.session_state.edited_cv_data['phone'] = st.text_input(
"Phone",
value=st.session_state.edited_cv_data.get('phone', '')
)
st.session_state.edited_cv_data['linkedin'] = st.text_input(
"LinkedIn URL",
value=st.session_state.edited_cv_data.get('linkedin', '')
)
# Professional Summary
st.session_state.edited_cv_data['summary'] = st.text_area(
"Professional Summary",
value=st.session_state.edited_cv_data.get('summary', ''),
height=100,
help="A brief overview of your professional background and skills"
)
# Skills
skills = st.text_area(
"Skills (comma separated)",
value=", ".join(st.session_state.edited_cv_data.get('skills', [])),
help="List your technical and soft skills"
)
st.session_state.edited_cv_data['skills'] = [s.strip() for s in skills.split(",") if s.strip()]
# Experience
st.subheader("Work Experience")
if 'experience' not in st.session_state.edited_cv_data:
st.session_state.edited_cv_data['experience'] = []
for i, exp in enumerate(st.session_state.edited_cv_data['experience']):
with st.expander(f"Experience {i+1}", expanded=i==0):
cols = st.columns(2)
with cols[0]:
exp['title'] = st.text_input(
"Job Title",
value=exp.get('title', ''),
key=f"exp_title_{i}"
)
exp['company'] = st.text_input(
"Company",
value=exp.get('company', ''),
key=f"exp_company_{i}"
)
with cols[1]:
exp['start_date'] = st.text_input(
"Start Date",
value=exp.get('start_date', ''),
key=f"exp_start_{i}"
)
exp['end_date'] = st.text_input(
"End Date",
value=exp.get('end_date', ''),
key=f"exp_end_{i}"
)
exp['description'] = st.text_area(
"Description",
value=exp.get('description', ''),
height=100,
key=f"exp_desc_{i}"
)
# Add new experience
if st.button("βž• Add Another Position"):
st.session_state.edited_cv_data['experience'].append({
'title': '',
'company': '',
'start_date': '',
'end_date': '',
'description': ''
})
st.experimental_rerun()
# Education
st.subheader("Education")
if 'education' not in st.session_state.edited_cv_data:
st.session_state.edited_cv_data['education'] = []
for i, edu in enumerate(st.session_state.edited_cv_data['education']):
with st.expander(f"Education {i+1}", expanded=i==0):
cols = st.columns(2)
with cols[0]:
edu['degree'] = st.text_input(
"Degree",
value=edu.get('degree', ''),
key=f"edu_degree_{i}"
)
edu['institution'] = st.text_input(
"Institution",
value=edu.get('institution', ''),
key=f"edu_institution_{i}"
)
with cols[1]:
edu['start_date'] = st.text_input(
"Start Date",
value=edu.get('start_date', ''),
key=f"edu_start_{i}"
)
edu['end_date'] = st.text_input(
"End Date",
value=edu.get('end_date', ''),
key=f"edu_end_{i}"
)
# Add new education
if st.button("βž• Add Another Education"):
st.session_state.edited_cv_data['education'].append({
'degree': '',
'institution': '',
'start_date': '',
'end_date': ''
})
st.experimental_rerun()
# Projects
st.subheader("Projects")
if 'projects' not in st.session_state.edited_cv_data:
st.session_state.edited_cv_data['projects'] = []
for i, proj in enumerate(st.session_state.edited_cv_data['projects']):
with st.expander(f"Project {i+1}", expanded=i==0):
proj['title'] = st.text_input(
"Project Title",
value=proj.get('title', ''),
key=f"proj_title_{i}"
)
proj['description'] = st.text_area(
"Description",
value=proj.get('description', ''),
height=100,
key=f"proj_desc_{i}"
)
# Add new project
if st.button("βž• Add Another Project"):
st.session_state.edited_cv_data['projects'].append({
'title': '',
'description': ''
})
st.experimental_rerun()
# Job Description and Optimization
if st.session_state.edited_cv_data:
st.header("3. Optimize for a Job")
st.session_state.job_description = st.text_area(
"Paste the Job Description",
value=st.session_state.job_description,
height=200,
help="Paste the job description you're applying for to get tailored recommendations"
)
# In your app.py where you have the rerun call
if st.button("✨ Optimize for This Job"):
if not st.session_state.job_description:
st.warning("Please enter a job description first")
else:
with st.spinner("Generating ATS-optimized content..."):
try:
optimized_data = generate_ats_optimized_content(
st.session_state.edited_cv_data,
st.session_state.job_description
)
st.session_state.edited_cv_data = optimized_data
st.success("Optimization complete! Review the changes below.")
st.experimental_rerun() # Remove this line
st.rerun() # Use this instead
except Exception as e:
st.error(f"Error during optimization: {str(e)}")
# Generate LaTeX and PDF
if st.session_state.edited_cv_data:
st.header("4. Generate Your ATS-Friendly Resume")
if st.button("πŸ”„ Generate LaTeX Code"):
with st.spinner("Generating LaTeX code..."):
try:
latex_code = generate_latex(st.session_state.edited_cv_data)
st.session_state.generated_latex = latex_code
except Exception as e:
st.error(f"Error generating LaTeX: {str(e)}")
if st.session_state.generated_latex:
# Display LaTeX code with syntax highlighting
st.subheader("Generated LaTeX Code")
st.markdown("""
Review and edit the LaTeX code below if needed before generating your PDF.
""")
# Syntax highlighting
formatter = HtmlFormatter(style="colorful")
styled_code = highlight(
st.session_state.generated_latex,
TexLexer(),
formatter
)
st.markdown(styled_code, unsafe_allow_html=True)
# Download LaTeX
st.download_button(
label="πŸ“₯ Download LaTeX File",
data=st.session_state.generated_latex,
file_name="resume.tex",
mime="text/plain"
)
# Generate and download PDF
if st.button("πŸ–¨οΈ Generate PDF"):
with st.spinner("Compiling PDF..."):
try:
with tempfile.TemporaryDirectory() as tempdir:
tex_file = os.path.join(tempdir, "resume.tex")
with open(tex_file, "w") as f:
f.write(st.session_state.generated_latex)
pdf_path = compile_latex_to_pdf(tex_file, tempdir)
if pdf_path and os.path.exists(pdf_path):
with open(pdf_path, "rb") as f:
pdf_bytes = f.read()
st.success("PDF generated successfully!")
st.download_button(
label="πŸ“₯ Download PDF",
data=pdf_bytes,
file_name="resume.pdf",
mime="application/pdf"
)
else:
st.error("Failed to generate PDF")
except Exception as e:
st.error(f"Error generating PDF: {str(e)}")
st.text(str(e))