Spaces:
Sleeping
Sleeping
import streamlit as st | |
from utils.utils import * | |
from agents import prompts | |
import pyperclip | |
from dotenv import load_dotenv | |
import pandas as pd | |
import hashlib | |
load_dotenv() | |
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("SmartHire-Assistant") | |
# Select Task | |
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 = 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) | |
if resume_file: | |
# Calculate a hash of the new file selection | |
new_file_hash = hash_inputs(str(resume_file), '', '', '') | |
if 'last_file_hash' not in st.session_state or st.session_state.last_file_hash != new_file_hash: | |
# Clear cached responses because a new file is uploaded | |
st.session_state.analysis = None | |
st.session_state.analysis_mc = None | |
st.session_state.input_hash = None | |
st.session_state.analysis_s = None | |
st.session_state.input_hash_sel = None | |
st.session_state.analysis_mc_s = None | |
st.session_state.analysis_mc_s_exp = None | |
# Update last file hash | |
st.session_state.last_file_hash = new_file_hash | |
#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 = prompts.prompt_template_modern | |
prompt_template = prompts.prompt_template_new | |
response = generate_analysis_new(resume_text, job_pref, job_title_text, must_have, prompt_template) | |
#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 = prompts.prompt_template_resumes_ | |
prompt_template = prompts.prompt_template_new | |
response = generate_individual_analysis(resume_text, job_pref, job_title_text, must_have, prompt_template) | |
#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']) | |
## Prioritize by overall_match_score, then education_certifications_score, and finally skills_keywords_score | |
response_anal = max( | |
response, | |
key=lambda x: ( | |
x.get('overall_match_score', 0), | |
x.get('education_certifications_score', 0), # Secondary criterion | |
x.get('skills_keywords_score', 0) # Tertiary criterion | |
) | |
) | |
# 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() | |