Balaprime commited on
Commit
c149b39
Β·
verified Β·
1 Parent(s): 214eaf2

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +339 -38
src/streamlit_app.py CHANGED
@@ -1,40 +1,341 @@
1
- import altair as alt
2
- import numpy as np
3
- import pandas as pd
4
  import streamlit as st
 
 
 
 
 
 
5
 
6
- """
7
- # Welcome to Streamlit!
8
-
9
- Edit `/streamlit_app.py` to customize this app to your heart's desire :heart:.
10
- If you have any questions, checkout our [documentation](https://docs.streamlit.io) and [community
11
- forums](https://discuss.streamlit.io).
12
-
13
- In the meantime, below is an example of what you can do with just a few lines of code:
14
- """
15
-
16
- num_points = st.slider("Number of points in spiral", 1, 10000, 1100)
17
- num_turns = st.slider("Number of turns in spiral", 1, 300, 31)
18
-
19
- indices = np.linspace(0, 1, num_points)
20
- theta = 2 * np.pi * num_turns * indices
21
- radius = indices
22
-
23
- x = radius * np.cos(theta)
24
- y = radius * np.sin(theta)
25
-
26
- df = pd.DataFrame({
27
- "x": x,
28
- "y": y,
29
- "idx": indices,
30
- "rand": np.random.randn(num_points),
31
- })
32
-
33
- st.altair_chart(alt.Chart(df, height=700, width=700)
34
- .mark_point(filled=True)
35
- .encode(
36
- x=alt.X("x", axis=None),
37
- y=alt.Y("y", axis=None),
38
- color=alt.Color("idx", legend=None, scale=alt.Scale()),
39
- size=alt.Size("rand", legend=None, scale=alt.Scale(range=[1, 150])),
40
- ))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import streamlit as st
2
+ import re
3
+ import os
4
+ from dotenv import load_dotenv
5
+ from llm_utils import generate_tech_questions, parse_tech_stack
6
+ import sqlite3
7
+ from datetime import datetime
8
 
9
+ # Load environment variables
10
+ load_dotenv()
11
+
12
+ # Configure page
13
+ st.set_page_config(
14
+ page_title="AI-Powered Interview Assistant | TalentScout",
15
+ page_icon="πŸ€–",
16
+ layout="wide"
17
+ )
18
+
19
+ # Initialize SQLite database
20
+ def init_db():
21
+ conn = sqlite3.connect("candidates.db")
22
+ c = conn.cursor()
23
+ c.execute('''CREATE TABLE IF NOT EXISTS candidates (
24
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
25
+ timestamp TEXT,
26
+ name TEXT,
27
+ email TEXT,
28
+ phone TEXT,
29
+ experience REAL,
30
+ role TEXT,
31
+ location TEXT,
32
+ tech_stack TEXT,
33
+ questions TEXT,
34
+ answers TEXT
35
+ )''')
36
+ conn.commit()
37
+ conn.close()
38
+
39
+ init_db()
40
+
41
+ # Constants
42
+ STEP_LABELS = {
43
+ "greeting": "πŸ‘‹ Welcome",
44
+ "form": "πŸ“ Candidate Information",
45
+ "tech_stack": "πŸ› οΈ Technical Skills",
46
+ "questions": "❓ Technical Assessment",
47
+ "review": "πŸ” Review Answers",
48
+ "end": "🏁 Complete"
49
+ }
50
+
51
+ # Initialize session state
52
+ def init_session_state():
53
+ if "step" not in st.session_state:
54
+ st.session_state.step = "greeting"
55
+ if "candidate_info" not in st.session_state:
56
+ st.session_state.candidate_info = {}
57
+ if "tech_stack" not in st.session_state:
58
+ st.session_state.tech_stack = []
59
+ if "questions" not in st.session_state:
60
+ st.session_state.questions = []
61
+ if "answers" not in st.session_state:
62
+ st.session_state.answers = []
63
+ if "current_question_index" not in st.session_state:
64
+ st.session_state.current_question_index = 0
65
+ if "chat_history" not in st.session_state:
66
+ st.session_state.chat_history = []
67
+
68
+ init_session_state()
69
+
70
+ # UI Components
71
+ def show_progress(current, total):
72
+ progress = current / total
73
+ st.progress(progress)
74
+ st.caption(f"Progress: {current} of {total} ({int(progress*100)}%)")
75
+
76
+ def validate_email(email):
77
+ return re.match(r"^[\w\.-]+@[\w\.-]+\.\w+$", email.strip())
78
+
79
+ def validate_phone(phone):
80
+ return re.match(r"^\+?[\d\s-]{10,15}$", phone.strip())
81
+
82
+ def generate_summary_text():
83
+ summary = []
84
+ summary.append("TALENTSCOUT APPLICATION SUMMARY")
85
+ summary.append(f"Timestamp: {datetime.now().isoformat()}")
86
+ summary.append("\n=== CANDIDATE INFORMATION ===")
87
+ for key, value in st.session_state.candidate_info.items():
88
+ summary.append(f"{key.title()}: {value}")
89
+
90
+ summary.append("\n=== TECHNICAL SKILLS ===")
91
+ summary.append(", ".join([tech for tech in st.session_state.tech_stack if len(tech) > 1]))
92
+
93
+ summary.append("\n=== QUESTIONS & ANSWERS ===")
94
+ for i, (q, a) in enumerate(zip(st.session_state.questions, st.session_state.answers)):
95
+ summary.append(f"\nQ{i+1}: {q}")
96
+ summary.append(f"A: {a}")
97
+
98
+ return "\n".join(summary)
99
+
100
+ # Save to database
101
+ def save_to_db():
102
+ conn = sqlite3.connect("candidates.db")
103
+ c = conn.cursor()
104
+ c.execute('''INSERT INTO candidates (
105
+ timestamp, name, email, phone, experience, role, location, tech_stack, questions, answers
106
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)''', (
107
+ datetime.now().isoformat(),
108
+ st.session_state.candidate_info.get("name", ""),
109
+ st.session_state.candidate_info.get("email", ""),
110
+ st.session_state.candidate_info.get("phone", ""),
111
+ st.session_state.candidate_info.get("experience", 0),
112
+ st.session_state.candidate_info.get("role", ""),
113
+ st.session_state.candidate_info.get("location", ""),
114
+ ",".join(st.session_state.tech_stack),
115
+ ";".join(st.session_state.questions),
116
+ ";".join(st.session_state.answers)
117
+ ))
118
+ conn.commit()
119
+ conn.close()
120
+
121
+ # Step: Greeting (Chat-based)
122
+ if st.session_state.step == "greeting":
123
+ st.title("Welcome to TalentScout!")
124
+ st.markdown("Hi! I'm TalentScout's Hiring Assistant. Ready to showcase your skills? Let's start with your name!")
125
+
126
+ chat_container = st.container()
127
+ with chat_container:
128
+ for message in st.session_state.chat_history:
129
+ with st.chat_message(message["role"]):
130
+ st.markdown(message["content"])
131
+
132
+ user_input = st.chat_input("Your response...")
133
+ if user_input:
134
+ st.session_state.chat_history.append({"role": "user", "content": user_input})
135
+ end_keywords = ["exit", "quit", "done", "stop"]
136
+ if any(keyword in user_input.lower() for keyword in end_keywords):
137
+ st.session_state.chat_history.append({"role": "assistant", "content": "Thank you for your time! If you change your mind, feel free to restart."})
138
+ st.session_state.step = "end"
139
+ else:
140
+ st.session_state.candidate_info["name"] = user_input.strip()
141
+ st.session_state.chat_history.append({"role": "assistant", "content": f"Nice to meet you, {user_input.strip()}! Let's continue with your information."})
142
+ st.session_state.step = "form"
143
+ st.rerun()
144
+
145
+ # Step: Candidate Information
146
+ elif st.session_state.step == "form":
147
+ st.title("Candidate Information")
148
+ st.write(f"Hello {st.session_state.candidate_info['name']}! Let's collect some basic information.")
149
+
150
+ with st.form("candidate_form"):
151
+ cols = st.columns(3)
152
+ with cols[0]:
153
+ email = st.text_input("Email*", placeholder="john@example.com")
154
+ with cols[1]:
155
+ phone = st.text_input("Phone*", placeholder="+1 1234567890")
156
+ experience = st.number_input("Experience (Years)*", min_value=0, max_value=50)
157
+ with cols[2]:
158
+ role = st.text_input("Position*", placeholder="Software Engineer")
159
+ location = st.text_input("Location*", placeholder="City, Country")
160
+
161
+ submitted = st.form_submit_button("Continue β†’")
162
+
163
+ if submitted:
164
+ errors = []
165
+ if not validate_email(email):
166
+ errors.append("Please enter a valid email address")
167
+ if not validate_phone(phone):
168
+ errors.append("Please enter a valid phone number (10-15 digits)")
169
+ if not role.strip():
170
+ errors.append("Please specify desired position(s)")
171
+ if not location.strip():
172
+ errors.append("Please enter your current location")
173
+ if errors:
174
+ for error in errors:
175
+ st.error(error)
176
+ else:
177
+ st.session_state.candidate_info.update({
178
+ "email": email.strip(),
179
+ "phone": phone.strip(),
180
+ "experience": experience,
181
+ "role": role.strip(),
182
+ "location": location.strip()
183
+ })
184
+ st.session_state.step = "tech_stack"
185
+ st.rerun()
186
+
187
+ # Step: Technical Skills
188
+ elif st.session_state.step == "tech_stack":
189
+ st.title("Technical Skills Assessment")
190
+ st.success(f"Hello {st.session_state.candidate_info['name']}! Let's discuss your technical skills.")
191
+
192
+ with st.form("tech_stack_form"):
193
+ st.write("Please list the technologies, frameworks, and tools you're proficient in:")
194
+ tech_input = st.text_area(
195
+ "Tech Stack*",
196
+ placeholder="Python, JavaScript, React, PostgreSQL, Docker...",
197
+ height=150
198
+ )
199
+
200
+ submitted = st.form_submit_button("Generate Questions β†’")
201
+
202
+ if submitted:
203
+ if not tech_input.strip():
204
+ st.error("Please enter at least one technology")
205
+ else:
206
+ tech_list = parse_tech_stack(tech_input)
207
+ if len(tech_list) == 0:
208
+ st.error("Could not identify any technologies. Please use standard names.")
209
+ else:
210
+ st.session_state.tech_stack = tech_list
211
+ st.session_state.step = "questions"
212
+ st.session_state.questions = []
213
+ st.session_state.answers = []
214
+ st.session_state.current_question_index = 0
215
+ st.rerun()
216
+
217
+ # Step: Technical Questions
218
+ elif st.session_state.step == "questions":
219
+ st.title("Technical Assessment")
220
+
221
+ if not st.session_state.questions:
222
+ with st.spinner("Generating customized questions based on your skills..."):
223
+ try:
224
+ questions = generate_tech_questions(
225
+ st.session_state.tech_stack,
226
+ st.session_state.candidate_info["experience"]
227
+ )
228
+ st.session_state.questions = [q for q in questions if q and ":" in q]
229
+ if not st.session_state.questions:
230
+ raise ValueError("No valid questions generated")
231
+ except Exception as e:
232
+ st.error(f"Failed to generate questions: {str(e)}. Please try again with different technologies.")
233
+ st.session_state.step = "tech_stack"
234
+ st.rerun()
235
+
236
+ current_idx = st.session_state.current_question_index
237
+ total_questions = len(st.session_state.questions)
238
+
239
+ if current_idx < total_questions:
240
+ show_progress(current_idx + 1, total_questions)
241
+
242
+ current_question = st.session_state.questions[current_idx]
243
+ tech, question = current_question.split(':', 1)
244
+ st.subheader(f"Question {current_idx + 1}/{total_questions} ({tech.strip()})")
245
+ st.markdown(f"**{question.strip()}**")
246
+
247
+ answer_key = f"answer_{current_idx}"
248
+ answer = st.text_area(
249
+ "Your answer:",
250
+ key=answer_key,
251
+ height=200,
252
+ value=st.session_state.answers[current_idx] if current_idx < len(st.session_state.answers) else ""
253
+ )
254
+
255
+ cols = st.columns([1, 1, 2])
256
+ with cols[0]:
257
+ if st.button("← Previous", disabled=current_idx == 0, key=f"prev_{current_idx}"):
258
+ st.session_state.current_question_index -= 1
259
+ st.rerun()
260
+ with cols[1]:
261
+ next_disabled = not answer.strip()
262
+ if st.button("Next β†’", disabled=next_disabled, key=f"next_{current_idx}"):
263
+ while len(st.session_state.answers) <= current_idx:
264
+ st.session_state.answers.append("")
265
+ st.session_state.answers[current_idx] = answer.strip()
266
+ st.session_state.current_question_index += 1
267
+ st.rerun()
268
+
269
+ with st.expander("Finish early"):
270
+ if st.button("Complete Assessment", key=f"complete_{current_idx}"):
271
+ st.session_state.step = "review"
272
+ st.rerun()
273
+ else:
274
+ st.session_state.step = "review"
275
+ st.rerun()
276
+
277
+ # Step: Review Answers
278
+ elif st.session_state.step == "review":
279
+ st.title("Review Your Answers")
280
+ st.write("Please review your responses before submission.")
281
+
282
+ for i, (question, answer) in enumerate(zip(st.session_state.questions, st.session_state.answers)):
283
+ with st.expander(f"Question {i+1}: {question}"):
284
+ st.write(answer)
285
+ if st.button("Edit", key=f"edit_{i}"):
286
+ st.session_state.current_question_index = i
287
+ st.session_state.step = "questions"
288
+ st.rerun()
289
+
290
+ if st.button("Submit Assessment"):
291
+ save_to_db()
292
+ st.session_state.step = "end"
293
+ st.rerun()
294
+
295
+ if st.button("← Back to Questions"):
296
+ st.session_state.current_question_index = len(st.session_state.questions) - 1
297
+ st.session_state.step = "questions"
298
+ st.rerun()
299
+
300
+ # Step: Completion
301
+ elif st.session_state.step == "end":
302
+ st.title("πŸŽ‰ Assessment Complete")
303
+ st.markdown("""
304
+ <div style='background:#f0f2f6; padding:20px; border-radius:10px'>
305
+ <h3 style='color:#2c3e50'>TalentScout Assessment Complete</h3>
306
+ <p>Thank you for your time. Our team will review your responses shortly.</p>
307
+ </div>
308
+ """, unsafe_allow_html=True)
309
+
310
+ st.success("""
311
+ **Thank you for completing the assessment!**
312
+ We'll review your responses and contact you within 3-5 business days.
313
+ """)
314
+
315
+ with st.container(border=True):
316
+ st.markdown(f"""
317
+ **Candidate:** {st.session_state.candidate_info.get("name", "N/A")}
318
+ **Role:** {st.session_state.candidate_info.get("role", "N/A")}
319
+ **Experience:** {st.session_state.candidate_info.get("experience", 0)} years
320
+ **Location:** {st.session_state.candidate_info.get("location", "N/A")}
321
+ """)
322
+ tech_stack = [tech for tech in st.session_state.tech_stack if len(tech) > 1]
323
+ if tech_stack:
324
+ st.markdown("**Technical Skills:** " + ", ".join(tech_stack))
325
+ else:
326
+ st.warning("No technical skills specified")
327
+ st.markdown(f"**Questions Completed:** {len(st.session_state.answers)}/{len(st.session_state.questions)}")
328
+
329
+ st.download_button(
330
+ label="πŸ“„ Download Full Summary",
331
+ data=generate_summary_text(),
332
+ file_name="talent_scout_summary.txt",
333
+ mime="text/plain",
334
+ use_container_width=True
335
+ )
336
+
337
+ if st.button("πŸ”„ Start New Assessment", use_container_width=True, key="restart_button"):
338
+ for key in list(st.session_state.keys()):
339
+ del st.session_state[key]
340
+ init_session_state()
341
+ st.rerun()