Job Role: {job_role if job_role else "Not specified"}
Analysis Date: {current_date}
AI Model: {model_used}
Overall Score: {resume_score}/100 - {"Excellent" if resume_score >= 80 else "Good" if resume_score >= 60 else "Needs Improvement"}
{f'
â Custom Job Description Used
' if st.session_state.get('used_custom_job_desc', False) else ''}
""", unsafe_allow_html=True)
# Add gauge charts for scores
import plotly.graph_objects as go
col1, col2 = st.columns(2)
with col1:
# Resume Score Gauge
fig1 = go.Figure(go.Indicator(
mode="gauge+number",
value=resume_score,
domain={'x': [0, 1], 'y': [0, 1]},
title={'text': "Resume Score", 'font': {'size': 16}},
gauge={
'axis': {'range': [0, 100], 'tickwidth': 1},
'bar': {'color': "#4CAF50" if resume_score >= 80 else "#FFA500" if resume_score >= 60 else "#FF4444"},
'bgcolor': "white",
'borderwidth': 2,
'bordercolor': "gray",
'steps': [
{'range': [0, 40], 'color': 'rgba(255, 68, 68, 0.2)'},
{'range': [40, 60], 'color': 'rgba(255, 165, 0, 0.2)'},
{'range': [60, 80], 'color': 'rgba(255, 214, 0, 0.2)'},
{'range': [80, 100], 'color': 'rgba(76, 175, 80, 0.2)'}
],
'threshold': {
'line': {'color': "red", 'width': 4},
'thickness': 0.75,
'value': 60
}
}
))
fig1.update_layout(
height=250,
margin=dict(l=20, r=20, t=50, b=20),
)
st.plotly_chart(fig1, use_container_width=True)
status = "Excellent" if resume_score >= 80 else "Good" if resume_score >= 60 else "Needs Improvement"
st.markdown(f"
{status}
", unsafe_allow_html=True)
with col2:
# ATS Score Gauge
fig2 = go.Figure(go.Indicator(
mode="gauge+number",
value=ats_score,
domain={'x': [0, 1], 'y': [0, 1]},
title={'text': "ATS Optimization Score", 'font': {'size': 16}},
gauge={
'axis': {'range': [0, 100], 'tickwidth': 1},
'bar': {'color': "#4CAF50" if ats_score >= 80 else "#FFA500" if ats_score >= 60 else "#FF4444"},
'bgcolor': "white",
'borderwidth': 2,
'bordercolor': "gray",
'steps': [
{'range': [0, 40], 'color': 'rgba(255, 68, 68, 0.2)'},
{'range': [40, 60], 'color': 'rgba(255, 165, 0, 0.2)'},
{'range': [60, 80], 'color': 'rgba(255, 214, 0, 0.2)'},
{'range': [80, 100], 'color': 'rgba(76, 175, 80, 0.2)'}
],
'threshold': {
'line': {'color': "red", 'width': 4},
'thickness': 0.75,
'value': 60
}
}
))
fig2.update_layout(
height=250,
margin=dict(l=20, r=20, t=50, b=20),
)
st.plotly_chart(fig2, use_container_width=True)
status = "Excellent" if ats_score >= 80 else "Good" if ats_score >= 60 else "Needs Improvement"
st.markdown(f"
{status}
", unsafe_allow_html=True)
# Add Job Description Match Score if custom job description was used
if st.session_state.get('used_custom_job_desc', False) and custom_job_description:
# Extract job match score from analysis result or calculate it
job_match_score = analysis_result.get("job_match_score", 0)
if not job_match_score and "job_match" in analysis_result:
job_match_score = analysis_result["job_match"].get("score", 0)
# If we have a job match score, display it
if job_match_score:
st.markdown("""
Job Description Match Analysis
""", unsafe_allow_html=True)
col1, col2 = st.columns(2)
with col1:
# Job Match Score Gauge
fig3 = go.Figure(go.Indicator(
mode="gauge+number",
value=job_match_score,
domain={'x': [0, 1], 'y': [0, 1]},
title={'text': "Job Match Score", 'font': {'size': 16}},
gauge={
'axis': {'range': [0, 100], 'tickwidth': 1},
'bar': {'color': "#4CAF50" if job_match_score >= 80 else "#FFA500" if job_match_score >= 60 else "#FF4444"},
'bgcolor': "white",
'borderwidth': 2,
'bordercolor': "gray",
'steps': [
{'range': [0, 40], 'color': 'rgba(255, 68, 68, 0.2)'},
{'range': [40, 60], 'color': 'rgba(255, 165, 0, 0.2)'},
{'range': [60, 80], 'color': 'rgba(255, 214, 0, 0.2)'},
{'range': [80, 100], 'color': 'rgba(76, 175, 80, 0.2)'}
],
'threshold': {
'line': {'color': "red", 'width': 4},
'thickness': 0.75,
'value': 60
}
}
))
fig3.update_layout(
height=250,
margin=dict(l=20, r=20, t=50, b=20),
)
st.plotly_chart(fig3, use_container_width=True)
match_status = "Excellent Match" if job_match_score >= 80 else "Good Match" if job_match_score >= 60 else "Low Match"
st.markdown(f"
{match_status}
", unsafe_allow_html=True)
with col2:
st.markdown("""
What This Means
This score represents how well your resume matches the specific job description you provided.
- 80-100: Excellent match - your resume is highly aligned with this job
- 60-79: Good match - your resume matches many requirements
- Below 60: Consider tailoring your resume more specifically to this job
""", unsafe_allow_html=True)
# Format the full response with better styling
formatted_analysis = full_response
# Replace section headers with styled headers
section_styles = {
"## Overall Assessment": """
Overall Assessment
""",
"## Professional Profile Analysis": """
Professional Profile Analysis
""",
"## Skills Analysis": """
Skills Analysis
""",
"## Experience Analysis": """
Experience Analysis
""",
"## Education Analysis": """
Education Analysis
""",
"## Key Strengths": """
Key Strengths
""",
"## Areas for Improvement": """
Areas for Improvement
""",
"## ATS Optimization Assessment": """
ATS Optimization Assessment
""",
"## Recommended Courses": """
Recommended Courses
""",
"## Resume Score": """
Resume Score
""",
"## Role Alignment Analysis": """
Role Alignment Analysis
""",
"## Job Match Analysis": """
Job Match Analysis
""",
}
# Apply the styling to each section
for section, style in section_styles.items():
if section in formatted_analysis:
formatted_analysis = formatted_analysis.replace(
section, style)
# Add closing div tags
next_section = False
for next_sec in section_styles.keys():
if next_sec != section and next_sec in formatted_analysis.split(style)[1]:
split_text = formatted_analysis.split(style)[1].split(next_sec)
formatted_analysis = formatted_analysis.split(style)[0] + style + split_text[0] + "
" + next_sec + "".join(split_text[1:])
next_section = True
break
if not next_section:
formatted_analysis = formatted_analysis + "
"
# Remove any extra closing div tags that might have been added
formatted_analysis = formatted_analysis.replace("
", "
")
# Ensure we don't have any orphaned closing tags at the end
if formatted_analysis.endswith("
"):
# Count opening and closing div tags
open_tags = formatted_analysis.count("
")
# If we have more closing than opening tags, remove the extras
if close_tags > open_tags:
excess = close_tags - open_tags
formatted_analysis = formatted_analysis[:-6 * excess]
# Clean up any visible HTML tags that might appear in the text
formatted_analysis = formatted_analysis.replace("</div>", "")
formatted_analysis = formatted_analysis.replace("<div>", "")
formatted_analysis = formatted_analysis.replace("
", "
") # Ensure proper opening
formatted_analysis = formatted_analysis.replace("
", "
") # Ensure proper closing
# Add CSS for the report
st.markdown("""
""", unsafe_allow_html=True)
# Display the formatted analysis
st.markdown(f"""
{formatted_analysis}
""", unsafe_allow_html=True)
# Create a PDF report
pdf_buffer = self.ai_analyzer.generate_pdf_report(
analysis_result={
"score": resume_score,
"ats_score": ats_score,
"model_used": model_used,
"full_response": full_response,
"strengths": analysis_result.get("strengths", []),
"weaknesses": analysis_result.get("weaknesses", []),
"used_custom_job_desc": st.session_state.get('used_custom_job_desc', False),
"custom_job_description": custom_job_description if st.session_state.get('used_custom_job_desc', False) else ""
},
candidate_name=st.session_state.get(
'candidate_name', 'Candidate'),
job_role=selected_role
)
# PDF download button
if pdf_buffer:
st.download_button(
label="đ Download PDF Report",
data=pdf_buffer,
file_name=f"resume_analysis_{datetime.now().strftime('%Y%m%d_%H%M')}.pdf",
mime="application/pdf",
use_container_width=True,
on_click=lambda: st.balloons()
)
else:
st.error("PDF generation failed. Please try again later.")
else:
st.error(f"Analysis failed: {analysis_result.get('error', 'Unknown error')}")
except Exception as ai_error:
st.error(f"Error during AI analysis: {str(ai_error)}")
import traceback as tb
st.code(tb.format_exc())
st.toast("Check out these repositories: [Awesome Java](https://github.com/Hunterdii/Awesome-Java)", icon="âšī¸")
def render_home(self):
apply_modern_styles()
# Hero Section
hero_section(
"Smart Resume AI",
"Transform your career with AI-powered resume analysis and building. Get personalized insights and create professional resumes that stand out."
)
# Features Section
st.markdown('
', unsafe_allow_html=True)
feature_card(
"fas fa-robot",
"AI-Powered Analysis",
"Get instant feedback on your resume with advanced AI analysis that identifies strengths and areas for improvement."
)
feature_card(
"fas fa-magic",
"Smart Resume Builder",
"Create professional resumes with our intelligent builder that suggests optimal content and formatting."
)
feature_card(
"fas fa-chart-line",
"Career Insights",
"Access detailed analytics and personalized recommendations to enhance your career prospects."
)
st.markdown('
', unsafe_allow_html=True)
st.toast("Check out these repositories: [AI-Nexus(AI/ML)](https://github.com/Hunterdii/AI-Nexus)", icon="âšī¸")
# Call-to-Action with Streamlit navigation
col1, col2, col3 = st.columns([1, 1, 1])
with col2:
if st.button("Get Started", key="get_started_btn",
help="Click to start analyzing your resume",
type="primary",
use_container_width=True):
cleaned_name = "đ RESUME ANALYZER".lower().replace(" ", "_").replace("đ", "").strip()
st.session_state.page = cleaned_name
st.rerun()
def render_job_search(self):
"""Render the job search page"""
render_job_search()
st.toast("Check out these repositories: [GeeksforGeeks-POTD](https://github.com/Hunterdii/GeeksforGeeks-POTD)", icon="âšī¸")
def render_feedback_page(self):
"""Render the feedback page"""
apply_modern_styles()
# Page Header
page_header(
"Feedback & Suggestions",
"Help us improve by sharing your thoughts"
)
# Initialize feedback manager
feedback_manager = FeedbackManager()
# Create tabs for form and stats
form_tab, stats_tab = st.tabs(["Submit Feedback", "Feedback Stats"])
with form_tab:
feedback_manager.render_feedback_form()
with stats_tab:
feedback_manager.render_feedback_stats()
st.toast("Check out these repositories: [TryHackMe Free Rooms](https://github.com/Hunterdii/tryhackme-free-rooms)", icon="âšī¸")
def show_repo_notification(self):
message = """
Check out these other repositories:
Hacking Resources:
Programming Languages:
Data Structures & Algorithms:
AI/ML Projects:
If you find this project helpful, please consider â starring the repo!
"""
st.sidebar.markdown(message, unsafe_allow_html=True)
def main(self):
"""Main application entry point"""
self.apply_global_styles()
# Admin login/logout in sidebar
with st.sidebar:
st_lottie(self.load_lottie_url("https://assets5.lottiefiles.com/packages/lf20_xyadoh9h.json"), height=200, key="sidebar_animation")
st.title("Smart Resume AI")
st.markdown("---")
# Navigation buttons
for page_name in self.pages.keys():
if st.button(page_name, use_container_width=True):
cleaned_name = page_name.lower().replace(" ", "_").replace("đ ", "").replace("đ", "").replace("đ", "").replace("đ", "").replace("đ¯", "").replace("đŦ", "").replace("âšī¸", "").strip()
st.session_state.page = cleaned_name
st.rerun()
# Add some space before admin login
st.markdown("
", unsafe_allow_html=True)
st.markdown("---")
# Admin Login/Logout section at bottom
if st.session_state.get('is_admin', False):
st.success(f"Logged in as: {st.session_state.get('current_admin_email')}")
if st.button("Logout", key="logout_button"):
try:
log_admin_action(st.session_state.get('current_admin_email'), "logout")
st.session_state.is_admin = False
st.session_state.current_admin_email = None
st.success("Logged out successfully!")
st.rerun()
except Exception as e:
st.error(f"Error during logout: {str(e)}")
else:
with st.expander("đ¤ Admin Login"):
admin_email_input = st.text_input("Email", key="admin_email_input")
admin_password = st.text_input("Password", type="password", key="admin_password_input")
if st.button("Login", key="login_button"):
try:
if verify_admin(admin_email_input, admin_password):
st.session_state.is_admin = True
st.session_state.current_admin_email = admin_email_input
log_admin_action(admin_email_input, "login")
st.success("Logged in successfully!")
st.rerun()
else:
st.error("Invalid credentials")
except Exception as e:
st.error(f"Error during login: {str(e)}")
# Display the repository notification in the sidebar
self.show_repo_notification()
# Force home page on first load
if 'initial_load' not in st.session_state:
st.session_state.initial_load = True
st.session_state.page = 'home'
st.rerun()
# Get current page and render it
current_page = st.session_state.get('page', 'home')
# Create a mapping of cleaned page names to original names
page_mapping = {name.lower().replace(" ", "_").replace("đ ", "").replace("đ", "").replace("đ", "").replace("đ", "").replace("đ¯", "").replace("đŦ", "").replace("âšī¸", "").strip(): name
for name in self.pages.keys()}
# Render the appropriate page
if current_page in page_mapping:
self.pages[page_mapping[current_page]]()
else:
# Default to home page if invalid page
self.render_home()
# Add footer to every page
self.add_footer()
if __name__ == "__main__":
app = ResumeApp()
app.main()