datasciencedojo commited on
Commit
cc6dcbe
1 Parent(s): be9d639

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +287 -0
app.py ADDED
@@ -0,0 +1,287 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ from utils.utilss import *
3
+ from prompts import cover_letter_prompts
4
+ import pyperclip
5
+ from dotenv import load_dotenv
6
+ import pandas as pd
7
+ import hashlib
8
+
9
+ load_dotenv()
10
+ def copytoclipboard():
11
+ if st.button("Copy to Clipboard"):
12
+ pyperclip.copy(st.session_state.analysis)
13
+ st.success("Cover letter copied to clipboard!")
14
+
15
+ def hash_inputs(resume_text, job_title, must_have, job_pref):
16
+ # Generate a hash based on the inputs
17
+ input_str = resume_text + job_title + must_have + job_pref
18
+ return hashlib.md5(input_str.encode()).hexdigest()
19
+
20
+ def hash_sel_inputs(resume_text, job_title, must_have, job_pref):
21
+ # Generate a hash based on the inputs
22
+ input_str = str(resume_text) + job_title + must_have + job_pref
23
+ return hashlib.md5(input_str.encode()).hexdigest()
24
+ def table_resp(lists):
25
+ d={}
26
+ sc=[[],[],[],[],[],[],[]]
27
+ for i in lists:
28
+ sc[0].append(i['candidate_name'])
29
+ sc[1].append(str(i['overall_match_score'])+'%')
30
+ sc[2].append(str(i['skills_keywords_score'])+'%')
31
+ sc[3].append(str(i['experience_score'])+'%')
32
+ sc[4].append(str(i['education_certifications_score'])+'%')
33
+ sc[5].append(str(i['preferred_qualifications_score'])+'%')
34
+ sc[6].append(i['score_interpretation'])
35
+ cols=['Candidate Name','Match Score','Skills & Keywords (40%)','Experience & Responsibilities (30%)','Education & Certifications (20%)','Preferred Qualifications (10%)','Score Interpretation']
36
+ for i in range(len(sc)):
37
+ d[cols[i]]=sc[i]
38
+ df = pd.DataFrame(d)
39
+ return df
40
+
41
+ def table_resp_exp(lists):
42
+ d={}
43
+ sc=[[],[],[],[],[],[],[]]
44
+ for i in lists:
45
+ sc[0].append(i['candidate_name'])
46
+ sc[1].append(i['overall_match_score'])
47
+ sc[2].append('('+str(i['skills_keywords_score'])+' out of 40) '+i['skills_keywords_explanation'])
48
+ sc[3].append('('+str(i['experience_score'])+' out of 30) '+i['experience_explanation'])
49
+ sc[4].append('('+str(i['education_certifications_score'])+' out of 20) '+i['education_certifications_explanation'])
50
+ sc[5].append('('+str(i['preferred_qualifications_score'])+' out of 10) '+i['preferred_qualifications_explanation'])
51
+ sc[6].append(i['score_interpretation'])
52
+ cols=['Candidate Name','Match Score','Skills & Keywords (40%)','Experience & Responsibilities (30%)','Education & Certifications (20%)','Preferred Qualifications (10%)','Score Interpretation']
53
+ for i in range(len(sc)):
54
+ d[cols[i]]=sc[i]
55
+ df = pd.DataFrame(d)
56
+ return df
57
+
58
+ def expand(ext_res):
59
+ formatted_resp=f"""
60
+ **Candidate:** {ext_res['candidate_name']}
61
+
62
+ **Match Score**: {ext_res['overall_match_score']}%
63
+
64
+ **Skills & Keywords** ({ext_res['skills_keywords_score']}% out of 40%):
65
+ {ext_res['skills_keywords_explanation']}
66
+
67
+ **Experience & Responsibilities** ({ext_res['experience_score']}% out of 30%):
68
+ {ext_res['experience_explanation']}
69
+
70
+ **Education & Certifications** ({ext_res['education_certifications_score']}% out of 20%):
71
+ {ext_res['education_certifications_explanation']}
72
+
73
+ **Preferred Qualifications** ({ext_res['preferred_qualifications_score']}% out of 10%):
74
+ {ext_res['preferred_qualifications_explanation']}
75
+
76
+ **Score Interpretation**: {ext_res['score_interpretation']}
77
+ """
78
+ return formatted_resp
79
+
80
+ def concise_resp(ext_res):
81
+ formatted_resp=f"""
82
+ **Candidate:** {ext_res['candidate_name']}
83
+
84
+ **Match Score**: {ext_res['overall_match_score']}%
85
+
86
+ **Skills & Keywords**: {ext_res['skills_keywords_score']}% out of 40%
87
+
88
+ **Experience**: {ext_res['experience_score']}% out of 30%
89
+
90
+ **Education & Certifications**: {ext_res['education_certifications_score']}% out of 20%
91
+
92
+ **Preferred Qualifications**: {ext_res['preferred_qualifications_score']}% out of 10%
93
+
94
+ **Score Interpretation**: {ext_res['score_interpretation']}
95
+ """
96
+
97
+ return formatted_resp
98
+ def filecheck(resume_file):
99
+ if len(resume_file)==1 and resume_file is not None:
100
+ resume_text = parse_resume(resume_file[0])
101
+ return resume_text
102
+ def filecheck_error(resume_file):
103
+ if len(resume_file)==0:
104
+ st.warning("Please upload a Resume.")
105
+ else:
106
+ st.warning("Please upload only one Resume.")
107
+
108
+ def filescheck(resume_file):
109
+ if len(resume_file)>1 and resume_file is not None:
110
+ resume_text = parse_resumes(resume_file)
111
+ return resume_text
112
+
113
+
114
+ def filescheck_error(resume_file):
115
+ if len(resume_file)==0:
116
+ st.warning("Please upload Resumes.")
117
+ else:
118
+ st.warning("Please upload more than 1 Resume for selection.")
119
+
120
+ def main():
121
+ if 'analysis' not in st.session_state:
122
+ st.session_state.analysis = None
123
+ if 'jobadv' not in st.session_state:
124
+ st.session_state.jobadv = None
125
+ if 'analysis_mc' not in st.session_state:
126
+ st.session_state.analysis_mc = None
127
+ if 'analysis' not in st.session_state:
128
+ st.session_state.analysis = None
129
+ if 'input_hash' not in st.session_state:
130
+ st.session_state.input_hash = None
131
+ if 'analysis_mc_s' not in st.session_state:
132
+ st.session_state.analysis_mc_s = None
133
+ if 'analysis_s' not in st.session_state:
134
+ st.session_state.analysis_s = None
135
+ if 'input_hash_sel' not in st.session_state:
136
+ st.session_state.input_hash_sel = None
137
+ if 'analysis_mc_s_exp' not in st.session_state:
138
+ st.session_state.analysis_mc_s_exp = None
139
+
140
+ st.title("Job Hiring Assistant")
141
+
142
+
143
+ # Select Cover Letter Style
144
+ st.sidebar.header("Select Task")
145
+ selection = st.sidebar.radio("Select option", ("Generate Job Adverstisment", "Resume Analysis","Resume Selection"))
146
+
147
+ # Generate Cover Letter
148
+ if selection == "Generate Job Adverstisment":
149
+
150
+ st.header("Job Details")
151
+ st.subheader('Job Title')
152
+ job_title_text = st.text_input("Enter job title here",max_chars=30)
153
+ st.subheader('Job Requirement')
154
+ job_requirement = st.text_area("Enter job requirement here")
155
+ if st.button("Generate Job Adverstisment"):
156
+ if job_requirement is not None:
157
+ prompt_template = cover_letter_prompts.prompt_template_classic
158
+ jobadv = generate_adv(job_requirement,job_title_text, prompt_template)
159
+ st.subheader("Job Adverstisment:")
160
+ st.markdown(jobadv)
161
+ st.session_state.jobadv = jobadv
162
+ #copytoclipboard()
163
+ else:
164
+ st.warning("Please provide a job requirement.")
165
+ else:
166
+ st.sidebar.header("Resume Analysis Criteria")
167
+ scoretext='''**80-100**: Good match
168
+
169
+ **50-79**: Medium match
170
+
171
+ **0-49**: Poor match '''
172
+ criteriatext='''**40%**: Skills and Keywords
173
+
174
+ **30%**: Experience & Responsibilities
175
+
176
+ **20%**: Education & Certifications
177
+
178
+ **10%**: Preferred Qualifications '''
179
+ #st.session_state.dropdown_open= False # Close the dropdown on click
180
+
181
+ #Match Score Range
182
+ # st.sidebar.button("Match Score Range",icon=":material/arrow_drop_down:")
183
+ #st.session_state.dropdown_open= False
184
+ #st.sidebar.button("Match Score Range",icon=":material/arrow_drop_up:")
185
+ st.sidebar.subheader("Match Score Range")
186
+ scorecontainer=st.sidebar.container(height=130)
187
+ scorecontainer.markdown(scoretext)
188
+
189
+ #Criteria weight
190
+ st.sidebar.subheader("Criteria weight")
191
+ criteriacontainer=st.sidebar.container(height=130)
192
+ criteriacontainer.markdown(criteriatext)
193
+
194
+ st.subheader("Upload Resume")
195
+ resume_file = st.file_uploader("Choose a file or drag and drop", type=["pdf"],accept_multiple_files=True)
196
+
197
+ #st.header("Job Details")
198
+ st.subheader('Job Title')
199
+ job_title_text = st.text_input("Enter job title here", "",max_chars=30)
200
+ st.subheader('Job Requirements')
201
+ must_have = st.text_area("Enter job must-have requirements here", "")
202
+ st.subheader('Preferred Qualification')
203
+ job_pref = st.text_area("Enter any preferred skills or qualifications here", "")
204
+ resume_text = None
205
+
206
+ if selection == "Resume Analysis":
207
+ btn1=st.button("Generate Resume Analysis")
208
+ if btn1:
209
+ #Only show Scores
210
+ #if st.button("Match Score"):
211
+ resume_text=filecheck(resume_file)
212
+ if resume_text is not None:
213
+ if job_pref is not None and must_have is not None :
214
+ current_input_hash = hash_inputs(resume_text, job_title_text, must_have, job_pref)
215
+
216
+ # Check if the inputs have changed
217
+ if st.session_state.input_hash != current_input_hash:
218
+ # Inputs have changed, generate new analysis
219
+ st.session_state.input_hash = current_input_hash
220
+ prompt_template = cover_letter_prompts.prompt_template_modern
221
+ response = generate_analysis(resume_text, job_pref, job_title_text, must_have, prompt_template)
222
+
223
+ # Cache the result
224
+ st.session_state.analysis = expand(response)
225
+ st.session_state.analysis_mc = concise_resp(response)
226
+
227
+ # Display the cached response based on the button clicked
228
+ # Match Score button clicked
229
+ st.subheader("Resume Analysis (Match Score)")
230
+ st.markdown(st.session_state.analysis_mc)
231
+ with st.expander("Detailed Analysis"):
232
+ st.markdown(st.session_state.analysis)
233
+
234
+ else:
235
+ st.warning("Please provide all job details.")
236
+ else:
237
+ filecheck_error(resume_file)
238
+
239
+
240
+
241
+ else:
242
+ btn1=st.button("Generate Match Score")
243
+ btn2=st.button("Generate Analysis")
244
+ if btn1 or btn2:
245
+ #Only show Scores
246
+ #if st.button("Match Score"):
247
+ resume_text=filescheck(resume_file)
248
+ if resume_text is not None:
249
+ if job_pref is not None and must_have is not None :
250
+ current_input_hash = hash_sel_inputs(resume_text, job_title_text, must_have, job_pref)
251
+
252
+ # Check if the inputs have changed
253
+ if st.session_state.input_hash_sel != current_input_hash:
254
+ # Inputs have changed, generate new analysis
255
+ st.session_state.input_hash_sel = current_input_hash
256
+ prompt_template = cover_letter_prompts.prompt_template_resumes_
257
+ response = generate_sel_analysis(resume_text, job_pref, job_title_text, must_have, prompt_template)
258
+ print('response:',response)
259
+ response_anal=max(response, key=lambda x: x['overall_match_score'])
260
+ # Cache the result
261
+ st.session_state.analysis_s = expand(response_anal)
262
+ st.session_state.analysis_mc_s = table_resp(response)
263
+ st.session_state.analysis_mc_s_exp = table_resp_exp(response)
264
+
265
+ # Display the cached response based on the button clicked
266
+ if btn1:
267
+ # Match Score button clicked
268
+ st.subheader("Match Scores")
269
+ st.dataframe(st.session_state.analysis_mc_s,hide_index=True)
270
+ elif btn2:
271
+ # Generate Analysis button clicked
272
+ print("expands")
273
+ st.subheader("Resume Analysis (Top Scored)")
274
+ # Candidate selection
275
+ st.markdown(st.session_state.analysis_s)
276
+ with st.expander("Detailed Analysis - All Candidates"):
277
+ st.dataframe(st.session_state.analysis_mc_s_exp,hide_index=True)
278
+
279
+ else:
280
+ st.warning("Please provide all job details.")
281
+ else:
282
+ filescheck_error(resume_file)
283
+
284
+
285
+
286
+ if __name__ == "__main__":
287
+ main()