import streamlit as st
import base64
from io import BytesIO
import json
from datetime import datetime
import time
import re
import os
# Set page configuration and title
st.set_page_config(
page_title="ResumeBuilder Pro",
layout="wide",
initial_sidebar_state="collapsed"
)
# Define a flag to check if API functionality is available
if "gemini_available" not in st.session_state:
st.session_state.gemini_available = False
# Try to import Google Generative AI library, but make it optional
try:
import google.generativeai as genai
st.session_state.gemini_import_success = True
except ImportError:
st.session_state.gemini_import_success = False
# Initialize Gemini API (modified to handle missing API key gracefully)
def initialize_gemini_api():
if not st.session_state.gemini_import_success:
st.warning("Google Generative AI library not installed. Install with: `pip install google-generativeai`")
return False
try:
# Get API key from session state first (from text input)
api_key = st.session_state.get("api_key", "")
# Only try to configure if API key is provided
if api_key:
genai.configure(api_key=api_key)
# Test the API with a simple call to verify it works
model = genai.GenerativeModel(model_name="gemini-pro")
_ = model.generate_content("Hello")
return True
else:
# No API key provided yet, not an error
return False
except Exception as e:
st.error(f"Failed to initialize Gemini API: {str(e)}")
return False
# Function to get Gemini model response
def get_gemini_response(prompt, temperature=0.7):
if not st.session_state.gemini_available:
st.warning("Gemini API not available. Please set up your API key.")
return None
try:
# Create the model
generation_config = {
"temperature": temperature,
"top_p": 0.95,
"top_k": 64,
"max_output_tokens": 8192,
}
model = genai.GenerativeModel(
model_name="gemini-pro",
generation_config=generation_config,
)
response = model.generate_content(prompt)
return response.text
except Exception as e:
st.error(f"Error getting AI response: {str(e)}")
return None
# Initialize session state for resume data
if "resume_data" not in st.session_state:
st.session_state.resume_data = {
"fullName": "Alexander Johnson",
"title": "Senior Frontend Developer",
"email": "alex.johnson@example.com",
"phone": "(555) 123-4567",
"location": "San Francisco, CA",
"summary": "Experienced frontend developer with 6+ years specializing in React and modern JavaScript frameworks. Passionate about creating intuitive user interfaces and optimizing web performance.",
"experience": [
{
"id": 1,
"company": "Tech Innovations Inc.",
"position": "Senior Frontend Developer",
"duration": "2019 - Present",
"description": "Lead frontend development for enterprise SaaS platform. Improved performance by 40% through code optimization. Mentored junior developers."
},
{
"id": 2,
"company": "WebSolutions Co.",
"position": "Frontend Developer",
"duration": "2017 - 2019",
"description": "Developed responsive web applications using React. Collaborated with design team to implement UI/UX improvements."
}
],
"education": [
{
"id": 1,
"institution": "University of California, Berkeley",
"degree": "B.S. Computer Science",
"duration": "2013 - 2017"
}
],
"skills": ["React", "JavaScript", "TypeScript", "HTML/CSS", "Redux", "Next.js", "Tailwind CSS", "UI/UX Design"]
}
if "dark_mode" not in st.session_state:
st.session_state.dark_mode = False
if "show_preview" not in st.session_state:
st.session_state.show_preview = True
if "new_skill" not in st.session_state:
st.session_state.new_skill = ""
if "job_description" not in st.session_state:
st.session_state.job_description = ""
if "ai_suggestions" not in st.session_state:
st.session_state.ai_suggestions = {}
if "cover_letter" not in st.session_state:
st.session_state.cover_letter = ""
if "suggested_skills" not in st.session_state:
st.session_state.suggested_skills = []
# Apply custom styling based on dark/light mode
def apply_custom_styling():
dark_mode = st.session_state.dark_mode
primary_color = "#4F46E5" # Indigo
if dark_mode:
background_color = "#111827" # Dark gray
text_color = "#F9FAFB" # Almost white
card_bg = "#1F2937" # Medium gray
input_bg = "#374151" # Light gray
secondary_bg = "#1E3A8A" # Dark blue
accent_light = "#818CF8" # Lighter indigo
else:
background_color = "#F9FAFB" # Almost white
text_color = "#111827" # Dark gray
card_bg = "#FFFFFF" # White
input_bg = "#F3F4F6" # Light gray
secondary_bg = "#EEF2FF" # Light indigo
accent_light = "#C7D2FE" # Very light indigo
css = f"""
"""
st.markdown(css, unsafe_allow_html=True)
# Function to download resume as JSON
def download_resume_json():
json_str = json.dumps(st.session_state.resume_data, indent=2)
b64 = base64.b64encode(json_str.encode()).decode()
filename = f"resume_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json"
href = f'π₯ Download JSON'
return href
# Function to create a placeholder for PDF export
def download_resume_pdf():
# In a real app, you would generate an actual PDF here
# For this example, we'll just return a placeholder message
return ''
# Main application header
def render_header():
col1, col2 = st.columns([6, 4])
with col1:
ai_badge = 'AI-Ready' if st.session_state.gemini_import_success else ""
st.markdown(f'
π ResumeBuilder Pro {ai_badge}
', unsafe_allow_html=True)
with col2:
download_buttons = f"""
{download_resume_json()}
{download_resume_pdf()}
"""
st.markdown(download_buttons, unsafe_allow_html=True)
# Hidden buttons that are triggered by the custom buttons above
col2_1, col2_2 = st.columns(2)
with col2_1:
if st.button("Toggle Theme", key="baseButton-secondary", type="secondary"):
st.session_state.dark_mode = not st.session_state.dark_mode
st.rerun()
with col2_2:
if st.button("Toggle Preview", key="baseButton-primary"):
st.session_state.show_preview = not st.session_state.show_preview
st.rerun()
# API Key Management - Improved to be more user-friendly and handle errors better
def manage_api_key():
if "api_key" not in st.session_state:
st.session_state.api_key = ""
st.markdown('
', unsafe_allow_html=True)
# Show different message if the generative AI library is not installed
if not st.session_state.gemini_import_success:
st.markdown("### π§ Enable AI Features")
st.markdown("""
To use AI features, you need to install the Google Generative AI library:
```
pip install google-generativeai
```
After installation, restart the app to access AI-powered resume enhancements.
""")
st.markdown('
', unsafe_allow_html=True)
return
# If library is installed but API key not set
st.markdown("### π Gemini API Setup")
st.markdown("To use AI features, enter your Google Gemini API key below:")
api_key = st.text_input("API Key",
value=st.session_state.api_key,
type="password",
placeholder="Enter your Gemini API key here")
col1, col2 = st.columns([1, 3])
with col1:
if st.button("Save API Key"):
st.session_state.api_key = api_key
if initialize_gemini_api():
st.session_state.gemini_available = True
st.success("API key saved and verified successfully!")
else:
if api_key:
st.error("Invalid API key. Please check and try again.")
else:
st.warning("Please enter an API key to enable AI features.")
with col2:
st.markdown("Get your API key from [Google AI Studio](https://makersuite.google.com/app/apikey)")
# Show what AI features are available
if not st.session_state.gemini_available:
st.markdown('
', unsafe_allow_html=True)
st.markdown("### π Unlock AI Features")
st.markdown("""
By adding your API key, you'll unlock powerful AI features:
- **Professional Summary Generator**: Create compelling summaries automatically
- **Job Match Analysis**: Score your resume against job descriptions
- **Skills Recommendations**: Get tailored skill suggestions for your role
- **Experience Description Enhancement**: Make your work history more impactful
- **Cover Letter Generator**: Create customized cover letters in seconds
""")
st.markdown('
', unsafe_allow_html=True)
st.subheader("Personal Information")
col1, col2 = st.columns(2)
with col1:
new_name = st.text_input("Full Name", value=data["fullName"], key="fullName")
if new_name != data["fullName"]:
st.session_state.resume_data["fullName"] = new_name
new_email = st.text_input("Email", value=data["email"], key="email")
if new_email != data["email"]:
st.session_state.resume_data["email"] = new_email
new_location = st.text_input("Location", value=data["location"], key="location")
if new_location != data["location"]:
st.session_state.resume_data["location"] = new_location
with col2:
new_title = st.text_input("Professional Title", value=data["title"], key="title")
if new_title != data["title"]:
st.session_state.resume_data["title"] = new_title
new_phone = st.text_input("Phone", value=data["phone"], key="phone")
if new_phone != data["phone"]:
st.session_state.resume_data["phone"] = new_phone
st.markdown("### Professional Summary")
# Add AI summary generation if API key is set
if st.session_state.gemini_available:
col1, col2 = st.columns([3, 1])
with col2:
if st.button("β¨ Generate AI Summary"):
summary = generate_ai_summary()
if summary:
st.session_state.resume_data["summary"] = summary
st.rerun()
new_summary = st.text_area("", value=data["summary"], height=150, key="summary")
if new_summary != data["summary"]:
st.session_state.resume_data["summary"] = new_summary
st.markdown('
', unsafe_allow_html=True)
# Add Job Description Analysis section if API key is set
if st.session_state.gemini_available:
st.markdown('
', unsafe_allow_html=True)
st.markdown("### π Job Match Analysis")
st.markdown("Paste a job description to get AI-powered insights on how your resume matches the requirements.")
job_description = st.text_area("Job Description", height=100, key="job_desc_input",
value=st.session_state.job_description)
if job_description:
company_name = st.text_input("Company Name (for cover letter)", key="company_name")
col1, col2, col3 = st.columns([1,1,1])
with col1:
if st.button("Analyze Job Match"):
st.session_state.job_description = job_description
analysis = analyze_job_description(job_description)
if analysis:
st.session_state.ai_suggestions = analysis
st.rerun()
with col2:
if st.button("Generate Cover Letter") and company_name:
st.session_state.job_description = job_description
cover_letter = generate_cover_letter(job_description, company_name)
if cover_letter:
st.session_state.cover_letter = cover_letter
st.rerun()
with col3:
if st.button("Suggest Skills"):
suggested_skills = suggest_skills_for_role(data["title"])
if suggested_skills:
st.session_state.suggested_skills = suggested_skills
st.rerun()
# Display analysis results if available
if st.session_state.ai_suggestions:
analysis = st.session_state.ai_suggestions
st.markdown("### π Job Match Analysis Results")
# Match percentage
match_percentage = analysis.get("match_percentage", 0)
st.markdown(f"### Match Score: {match_percentage}%")
st.markdown('
'.format(match_percentage), unsafe_allow_html=True)
# Missing skills
if "missing_skills" in analysis and analysis["missing_skills"]:
st.markdown('
', unsafe_allow_html=True)
st.markdown("#### π΄ Missing Skills")
st.markdown("Consider adding these skills to your resume:")
missing_skills_html = ""
for skill in analysis["missing_skills"]:
missing_skills_html += f'{skill} '
st.markdown(missing_skills_html, unsafe_allow_html=True)
# Hidden buttons for adding missing skills
for skill in analysis["missing_skills"]:
if st.button("Add", key=f"add_skill_{skill.replace(' ', '_')}", type="primary"):
if skill not in data["skills"]:
data["skills"].append(skill)
st.rerun()
st.markdown('
', unsafe_allow_html=True)
# Highlight skills
if "highlight_skills" in analysis and analysis["highlight_skills"]:
st.markdown('
', unsafe_allow_html=True)
st.markdown("#### π’ Relevant Skills")
st.markdown("Highlight these skills prominently in your resume:")
highlight_skills_html = ""
for skill in analysis["highlight_skills"]:
highlight_skills_html += f'{skill}'
st.markdown(highlight_skills_html, unsafe_allow_html=True)
st.markdown('
', unsafe_allow_html=True)
# Emphasis suggestions
if "emphasis_suggestions" in analysis and analysis["emphasis_suggestions"]:
st.markdown('
', unsafe_allow_html=True)
st.markdown("#### π‘ Experience to Emphasize")
emphasis_html = "
"
for suggestion in analysis["emphasis_suggestions"]:
emphasis_html += f"
', unsafe_allow_html=True)
# Display suggested skills if available
if hasattr(st.session_state, 'suggested_skills') and st.session_state.suggested_skills:
st.markdown("### π·οΈ Suggested Skills for Your Role")
suggested_skills_html = ""
for skill in st.session_state.suggested_skills:
if skill not in data["skills"]:
suggested_skills_html += f'{skill} '
if suggested_skills_html:
st.markdown(suggested_skills_html, unsafe_allow_html=True)
# Hidden buttons for adding suggested skills
for skill in st.session_state.suggested_skills:
if skill not in data["skills"]:
if st.button("Add", key=f"add_suggested_skill_{skill.replace(' ', '_')}", type="primary"):
data["skills"].append(skill)
st.rerun()
else:
st.info("All suggested skills are already in your resume!")
# Display cover letter if available
if st.session_state.cover_letter:
st.markdown("### π Generated Cover Letter")
st.markdown('
', unsafe_allow_html=True)
# Replace newlines with tags for proper HTML display
formatted_letter = st.session_state.cover_letter.replace('\n', ' ')
st.markdown(formatted_letter, unsafe_allow_html=True)
st.markdown('
', unsafe_allow_html=True)
# Add button to copy cover letter to clipboard
if st.button("π Copy Cover Letter"):
st.toast("Cover letter copied to clipboard!", icon="π")
st.markdown('