|
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): |
|
|
|
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): |
|
|
|
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") |
|
|
|
|
|
|
|
st.sidebar.header("Select Task") |
|
selection = st.sidebar.radio("Select option", ("Generate Job Adverstisment", "Resume Analysis","Resume Selection")) |
|
|
|
|
|
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 |
|
|
|
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.sidebar.subheader("Match Score Range") |
|
scorecontainer=st.sidebar.container(height=130) |
|
scorecontainer.markdown(scoretext) |
|
|
|
|
|
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.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: |
|
|
|
|
|
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) |
|
|
|
|
|
if st.session_state.input_hash != current_input_hash: |
|
|
|
st.session_state.input_hash = current_input_hash |
|
prompt_template = prompts.prompt_template_modern |
|
response = generate_analysis(resume_text, job_pref, job_title_text, must_have, prompt_template) |
|
|
|
|
|
st.session_state.analysis = expand(response) |
|
st.session_state.analysis_mc = concise_resp(response) |
|
|
|
|
|
|
|
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: |
|
|
|
|
|
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) |
|
|
|
|
|
if st.session_state.input_hash_sel != current_input_hash: |
|
|
|
st.session_state.input_hash_sel = current_input_hash |
|
prompt_template = 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']) |
|
|
|
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) |
|
|
|
|
|
if btn1: |
|
|
|
st.subheader("Match Scores") |
|
st.dataframe(st.session_state.analysis_mc_s,hide_index=True) |
|
elif btn2: |
|
|
|
print("expands") |
|
st.subheader("Resume Analysis (Top Scored)") |
|
|
|
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() |
|
|