Spaces:
Sleeping
Sleeping
| # 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)) |