-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathapp.py
344 lines (308 loc) · 13 KB
/
app.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
import json
import streamlit as st
from phi.utils.log import logger
from streamlit_pdf_viewer import pdf_viewer
from tasks import (
init_session_state,
schedule_interview,
extract_text_from_pdf,
analyze_resume,
send_selection_email,
send_rejection_email,
add_job_details,
)
from agents import (
create_resume_analyzer_agent,
create_scheduler_agent,
create_email_agent,
)
def main() -> None:
st.title("AI Recruitment System")
init_session_state()
with st.sidebar:
st.header("Configuration")
# API Configuration
st.subheader("LLM Models")
model_provider = st.selectbox("Model Provider", ["OpenAI", "Claude", "Mistral"])
api_key = st.text_input(
"API Key",
type="password",
value=st.session_state.api_key,
help="Get your API key",
)
if model_provider:
st.session_state.model_provider = model_provider
if api_key:
st.session_state.api_key = api_key
st.subheader("Zoom Settings")
zoom_account_id = st.text_input(
"Zoom Account ID", type="password", value=st.session_state.zoom_account_id
)
zoom_client_id = st.text_input(
"Zoom Client ID", type="password", value=st.session_state.zoom_client_id
)
zoom_client_secret = st.text_input(
"Zoom Client Secret",
type="password",
value=st.session_state.zoom_client_secret,
)
st.subheader("Email Settings")
email_sender = st.text_input(
"Sender Email",
value=st.session_state.email_sender,
help="Email address to send from",
)
email_passkey = st.text_input(
"Email App Password",
type="password",
value=st.session_state.email_passkey,
help="App-specific password for email",
)
company_name = st.text_input(
"Company Name",
value=st.session_state.company_name,
help="Name to use in email communications",
)
if zoom_account_id:
st.session_state.zoom_account_id = zoom_account_id
if zoom_client_id:
st.session_state.zoom_client_id = zoom_client_id
if zoom_client_secret:
st.session_state.zoom_client_secret = zoom_client_secret
if email_sender:
st.session_state.email_sender = email_sender
if email_passkey:
st.session_state.email_passkey = email_passkey
if company_name:
st.session_state.company_name = company_name
required_configs = {
"Model Provide": st.session_state.model_provider,
"API Key": st.session_state.api_key,
"Zoom Account ID": st.session_state.zoom_account_id,
"Zoom Client ID": st.session_state.zoom_client_id,
"Zoom Client Secret": st.session_state.zoom_client_secret,
"Email Sender": st.session_state.email_sender,
"Email Password": st.session_state.email_passkey,
"Company Name": st.session_state.company_name,
}
missing_configs = [k for k, v in required_configs.items() if not v]
if missing_configs:
st.warning(
f"Please configure the following in the sidebar: {', '.join(missing_configs)}"
)
return
if not st.session_state.api_key:
st.warning("Please enter your API Key in the sidebar to continue.")
return
with st.expander("Add Job Role & Descriptions", expanded=False):
job_role = st.text_input("Job Role")
job_description = st.text_area("Job Description (Use Markdown)")
additional_instructions = st.text_area(
"Additional Instructions (Optional)",
placeholder="Add additional instructions for candidate selection for this particular job role.",
)
button = st.button("Add details")
if button:
resonse = add_job_details(
job_role, job_description, additional_instructions
)
if resonse:
st.success("Data Saved Successfully.!!")
else:
st.error(
"Error occurred while storing the data. Make sure to provide Job Role and Job Description."
)
with open("data/job_descriptions.json", "r") as f:
json_descriptions_data = json.load(f)
role = st.selectbox(
"Select the role you're applying for:",
json_descriptions_data.keys(),
)
with st.expander("Job Description", expanded=True):
text_markdown = """
{job_descriptions}
"""
st.markdown(
text_markdown.format(
job_descriptions=json_descriptions_data[role]["job_description"],
)
)
# Add a "New Application" button before the resume upload
if st.button("📝 New Application"):
# Clear only the application-related states
keys_to_clear = [
"resume_text",
"analysis_complete",
"is_selected",
"candidate_email",
"current_pdf",
]
for key in keys_to_clear:
if key in st.session_state:
st.session_state[key] = None if key == "current_pdf" else ""
st.rerun()
resume_file = st.file_uploader(
"Upload your resume (PDF)", type=["pdf"], key="resume_uploader"
)
if resume_file is not None and resume_file != st.session_state.get("current_pdf"):
st.session_state.current_pdf = resume_file
st.session_state.resume_text = ""
st.session_state.analysis_complete = False
st.session_state.is_selected = False
st.rerun()
if resume_file:
st.subheader("Uploaded Resume")
col1, col2 = st.columns([4, 1])
with col1:
import tempfile, os
with tempfile.NamedTemporaryFile(delete=False, suffix=".pdf") as tmp_file:
tmp_file.write(resume_file.read())
tmp_file_path = tmp_file.name
resume_file.seek(0)
try:
pdf_viewer(tmp_file_path)
finally:
os.unlink(tmp_file_path)
with col2:
st.download_button(
label="📥 Download",
data=resume_file,
file_name=resume_file.name,
mime="application/pdf",
)
# Process the resume text
if not st.session_state.resume_text:
with st.spinner("Processing your resume..."):
resume_text = extract_text_from_pdf(resume_file)
if resume_text:
st.session_state.resume_text = resume_text
st.success("Resume processed successfully!")
else:
st.error("Could not process the PDF. Please try again.")
# Email input with session state
email = st.text_input(
"Candidate's email address",
value=st.session_state.candidate_email,
key="email_input",
)
st.session_state.candidate_email = email
# Analysis and next steps
if (
st.session_state.resume_text
and email
and not st.session_state.analysis_complete
):
if st.button("Analyze Resume"):
with st.spinner("Analyzing your resume..."):
resume_analyzer = create_resume_analyzer_agent()
email_agent = create_email_agent() # Create email agent here
if resume_analyzer and email_agent:
print("DEBUG: Starting resume analysis")
is_selected, feedback = analyze_resume(
resume_text=st.session_state.resume_text,
role_requirements=json_descriptions_data[role],
role=role,
analyzer=resume_analyzer,
)
print(
f"DEBUG: Analysis complete - Selected: {is_selected}, Feedback: {feedback}"
)
if is_selected:
st.success(
"Congratulations! Your skills match our requirements."
)
st.session_state.analysis_complete = True
st.session_state.is_selected = True
st.rerun()
else:
st.warning(
"Unfortunately, your skills don't match our requirements."
)
st.write(f"Feedback: {feedback}")
# Send rejection email
with st.spinner("Sending feedback email..."):
try:
send_rejection_email(
email_agent=email_agent,
to_email=email,
role=role,
feedback=feedback,
)
st.info(
"We've sent you an email with detailed feedback."
)
except Exception as e:
logger.error(f"Error sending rejection email: {e}")
st.error(
"Could not send feedback email. Please try again."
)
if st.session_state.get("analysis_complete") and st.session_state.get(
"is_selected", False
):
st.success("Congratulations! Your skills match our requirements.")
st.info(
"Click 'Proceed with Application' to continue with the interview process."
)
if st.button("Proceed with Application", key="proceed_button"):
print("DEBUG: Proceed button clicked") # Debug
with st.spinner("🔄 Processing your application..."):
try:
print("DEBUG: Creating email agent") # Debug
email_agent = create_email_agent()
print(f"DEBUG: Email agent created: {email_agent}") # Debug
print("DEBUG: Creating scheduler agent") # Debug
scheduler_agent = create_scheduler_agent()
print(f"DEBUG: Scheduler agent created: {scheduler_agent}") # Debug
# 3. Send selection email
with st.status(
"📧 Sending confirmation email...", expanded=True
) as status:
print(
f"DEBUG: Attempting to send email to {st.session_state.candidate_email}"
) # Debug
send_selection_email(
email_agent, st.session_state.candidate_email, role
)
print("DEBUG: Email sent successfully") # Debug
status.update(label="✅ Confirmation email sent!")
# 4. Schedule interview
with st.status(
"📅 Scheduling interview...", expanded=True
) as status:
print("DEBUG: Attempting to schedule interview") # Debug
schedule_interview(
scheduler_agent,
st.session_state.candidate_email,
email_agent,
role,
)
print("DEBUG: Interview scheduled successfully") # Debug
status.update(label="✅ Interview scheduled!")
print("DEBUG: All processes completed successfully") # Debug
st.success(
"""
🎉 Application Successfully Processed!
Please check your email for:
1. Selection confirmation ✅
2. Interview details with Zoom link 🔗
Next steps:
1. Review the role requirements
2. Prepare for your technical interview
3. Join the interview 5 minutes early
"""
)
except Exception as e:
print(f"DEBUG: Error occurred: {str(e)}") # Debug
print(f"DEBUG: Error type: {type(e)}") # Debug
import traceback
print(f"DEBUG: Full traceback: {traceback.format_exc()}") # Debug
st.error(f"An error occurred: {str(e)}")
st.error("Please try again or contact support.")
# Reset button
if st.sidebar.button("Reset Application"):
for key in st.session_state.keys():
if key != "api_key":
del st.session_state[key]
st.rerun()
if __name__ == "__main__":
main()