import streamlit as st from utils.utilss import * from prompts import cover_letter_prompts import pyperclip from dotenv import load_dotenv import pandas as pd import hashlib load_dotenv() def copytoclipboard(): if st.button("Copy to Clipboard"): pyperclip.copy(st.session_state.analysis) st.success("Cover letter copied to clipboard!") def hash_inputs(resume_text, job_title, must_have, job_pref): # Generate a hash based on the inputs input_str = resume_text + job_title + must_have + job_pref return hashlib.md5(input_str.encode()).hexdigest() def hash_sel_inputs(resume_text, job_title, must_have, job_pref): # Generate a hash based on the inputs input_str = str(resume_text) + job_title + must_have + job_pref return hashlib.md5(input_str.encode()).hexdigest() def table_resp(lists): d={} sc=[[],[],[],[],[],[],[]] for i in lists: sc[0].append(i['candidate_name']) sc[1].append(str(i['overall_match_score'])+'%') sc[2].append(str(i['skills_keywords_score'])+'%') sc[3].append(str(i['experience_score'])+'%') sc[4].append(str(i['education_certifications_score'])+'%') sc[5].append(str(i['preferred_qualifications_score'])+'%') sc[6].append(i['score_interpretation']) cols=['Candidate Name','Match Score','Skills & Keywords (40%)','Experience & Responsibilities (30%)','Education & Certifications (20%)','Preferred Qualifications (10%)','Score Interpretation'] for i in range(len(sc)): d[cols[i]]=sc[i] df = pd.DataFrame(d) return df def table_resp_exp(lists): d={} sc=[[],[],[],[],[],[],[]] for i in lists: sc[0].append(i['candidate_name']) sc[1].append(i['overall_match_score']) sc[2].append('('+str(i['skills_keywords_score'])+' out of 40) '+i['skills_keywords_explanation']) sc[3].append('('+str(i['experience_score'])+' out of 30) '+i['experience_explanation']) sc[4].append('('+str(i['education_certifications_score'])+' out of 20) '+i['education_certifications_explanation']) sc[5].append('('+str(i['preferred_qualifications_score'])+' out of 10) '+i['preferred_qualifications_explanation']) sc[6].append(i['score_interpretation']) cols=['Candidate Name','Match Score','Skills & Keywords (40%)','Experience & Responsibilities (30%)','Education & Certifications (20%)','Preferred Qualifications (10%)','Score Interpretation'] for i in range(len(sc)): d[cols[i]]=sc[i] df = pd.DataFrame(d) return df def expand(ext_res): formatted_resp=f""" **Candidate:** {ext_res['candidate_name']} **Match Score**: {ext_res['overall_match_score']}% **Skills & Keywords** ({ext_res['skills_keywords_score']}% out of 40%): {ext_res['skills_keywords_explanation']} **Experience & Responsibilities** ({ext_res['experience_score']}% out of 30%): {ext_res['experience_explanation']} **Education & Certifications** ({ext_res['education_certifications_score']}% out of 20%): {ext_res['education_certifications_explanation']} **Preferred Qualifications** ({ext_res['preferred_qualifications_score']}% out of 10%): {ext_res['preferred_qualifications_explanation']} **Score Interpretation**: {ext_res['score_interpretation']} """ return formatted_resp def concise_resp(ext_res): formatted_resp=f""" **Candidate:** {ext_res['candidate_name']} **Match Score**: {ext_res['overall_match_score']}% **Skills & Keywords**: {ext_res['skills_keywords_score']}% out of 40% **Experience**: {ext_res['experience_score']}% out of 30% **Education & Certifications**: {ext_res['education_certifications_score']}% out of 20% **Preferred Qualifications**: {ext_res['preferred_qualifications_score']}% out of 10% **Score Interpretation**: {ext_res['score_interpretation']} """ return formatted_resp def filecheck(resume_file): if len(resume_file)==1 and resume_file is not None: resume_text = parse_resume(resume_file[0]) return resume_text def filecheck_error(resume_file): if len(resume_file)==0: st.warning("Please upload a Resume.") else: st.warning("Please upload only one Resume.") def filescheck(resume_file): if len(resume_file)>1 and resume_file is not None: resume_text = parse_resumes(resume_file) return resume_text def filescheck_error(resume_file): if len(resume_file)==0: st.warning("Please upload Resumes.") else: st.warning("Please upload more than 1 Resume for selection.") def main(): if 'analysis' not in st.session_state: st.session_state.analysis = None if 'jobadv' not in st.session_state: st.session_state.jobadv = None if 'analysis_mc' not in st.session_state: st.session_state.analysis_mc = None if 'analysis' not in st.session_state: st.session_state.analysis = None if 'input_hash' not in st.session_state: st.session_state.input_hash = None if 'analysis_mc_s' not in st.session_state: st.session_state.analysis_mc_s = None if 'analysis_s' not in st.session_state: st.session_state.analysis_s = None if 'input_hash_sel' not in st.session_state: st.session_state.input_hash_sel = None if 'analysis_mc_s_exp' not in st.session_state: st.session_state.analysis_mc_s_exp = None st.title("Job Hiring Assistant") # Select Cover Letter Style st.sidebar.header("Select Task") selection = st.sidebar.radio("Select option", ("Generate Job Adverstisment", "Resume Analysis","Resume Selection")) # Generate Cover Letter if selection == "Generate Job Adverstisment": st.header("Job Details") st.subheader('Job Title') job_title_text = st.text_input("Enter job title here",max_chars=30) st.subheader('Job Requirement') job_requirement = st.text_area("Enter job requirement here") if st.button("Generate Job Adverstisment"): if job_requirement is not None: prompt_template = cover_letter_prompts.prompt_template_classic jobadv = generate_adv(job_requirement,job_title_text, prompt_template) st.subheader("Job Adverstisment:") st.markdown(jobadv) st.session_state.jobadv = jobadv #copytoclipboard() else: st.warning("Please provide a job requirement.") else: st.sidebar.header("Resume Analysis Criteria") scoretext='''**80-100**: Good match **50-79**: Medium match **0-49**: Poor match ''' criteriatext='''**40%**: Skills and Keywords **30%**: Experience & Responsibilities **20%**: Education & Certifications **10%**: Preferred Qualifications ''' #st.session_state.dropdown_open= False # Close the dropdown on click #Match Score Range # st.sidebar.button("Match Score Range",icon=":material/arrow_drop_down:") #st.session_state.dropdown_open= False #st.sidebar.button("Match Score Range",icon=":material/arrow_drop_up:") st.sidebar.subheader("Match Score Range") scorecontainer=st.sidebar.container(height=130) scorecontainer.markdown(scoretext) #Criteria weight st.sidebar.subheader("Criteria weight") criteriacontainer=st.sidebar.container(height=130) criteriacontainer.markdown(criteriatext) st.subheader("Upload Resume") resume_file = st.file_uploader("Choose a file or drag and drop", type=["pdf"],accept_multiple_files=True) #st.header("Job Details") st.subheader('Job Title') job_title_text = st.text_input("Enter job title here", "",max_chars=30) st.subheader('Job Requirements') must_have = st.text_area("Enter job must-have requirements here", "") st.subheader('Preferred Qualification') job_pref = st.text_area("Enter any preferred skills or qualifications here", "") resume_text = None if selection == "Resume Analysis": btn1=st.button("Generate Resume Analysis") if btn1: #Only show Scores #if st.button("Match Score"): resume_text=filecheck(resume_file) if resume_text is not None: if job_pref is not None and must_have is not None : current_input_hash = hash_inputs(resume_text, job_title_text, must_have, job_pref) # Check if the inputs have changed if st.session_state.input_hash != current_input_hash: # Inputs have changed, generate new analysis st.session_state.input_hash = current_input_hash prompt_template = cover_letter_prompts.prompt_template_modern response = generate_analysis(resume_text, job_pref, job_title_text, must_have, prompt_template) # Cache the result st.session_state.analysis = expand(response) st.session_state.analysis_mc = concise_resp(response) # Display the cached response based on the button clicked # Match Score button clicked st.subheader("Resume Analysis (Match Score)") st.markdown(st.session_state.analysis_mc) with st.expander("Detailed Analysis"): st.markdown(st.session_state.analysis) else: st.warning("Please provide all job details.") else: filecheck_error(resume_file) else: btn1=st.button("Generate Match Score") btn2=st.button("Generate Analysis") if btn1 or btn2: #Only show Scores #if st.button("Match Score"): resume_text=filescheck(resume_file) if resume_text is not None: if job_pref is not None and must_have is not None : current_input_hash = hash_sel_inputs(resume_text, job_title_text, must_have, job_pref) # Check if the inputs have changed if st.session_state.input_hash_sel != current_input_hash: # Inputs have changed, generate new analysis st.session_state.input_hash_sel = current_input_hash prompt_template = cover_letter_prompts.prompt_template_resumes_ response = generate_sel_analysis(resume_text, job_pref, job_title_text, must_have, prompt_template) print('response:',response) response_anal=max(response, key=lambda x: x['overall_match_score']) # Cache the result st.session_state.analysis_s = expand(response_anal) st.session_state.analysis_mc_s = table_resp(response) st.session_state.analysis_mc_s_exp = table_resp_exp(response) # Display the cached response based on the button clicked if btn1: # Match Score button clicked st.subheader("Match Scores") st.dataframe(st.session_state.analysis_mc_s,hide_index=True) elif btn2: # Generate Analysis button clicked print("expands") st.subheader("Resume Analysis (Top Scored)") # Candidate selection st.markdown(st.session_state.analysis_s) with st.expander("Detailed Analysis - All Candidates"): st.dataframe(st.session_state.analysis_mc_s_exp,hide_index=True) else: st.warning("Please provide all job details.") else: filescheck_error(resume_file) if __name__ == "__main__": main()