Sa-m commited on
Commit
a8883b6
·
verified ·
1 Parent(s): 69d3350

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +171 -112
app.py CHANGED
@@ -12,6 +12,7 @@ import speech_recognition as sr
12
  # import pygame # Not used directly in main app logic here
13
  import time
14
  from dotenv import load_dotenv
 
15
 
16
  # Load environment variables
17
  load_dotenv()
@@ -102,9 +103,9 @@ def generate_questions(roles, data):
102
  text = f"""If this is not a resume then return text uploaded pdf is not a resume. this is a resume overview of the candidate.
103
  The candidate details are in {data}. The candidate has applied for the role of {roles_str}.
104
  Generate questions for the candidate based on the role applied and on the Resume of the candidate.
105
- Not always necceassary to ask only technical questions related to the role but the logic of question
106
  should include the job applied for because there might be some deep tech questions which the user might not know.
107
- Ask some personal questions too.Ask no additional questions. Dont categorize the questions.
108
  ask 2 questions only. directly ask the questions not anything else.
109
  Also ask the questions in a polite way. Ask the questions in a way that the candidate can understand the question.
110
  and make sure the questions are related to these metrics: Communication skills, Teamwork and collaboration,
@@ -129,10 +130,12 @@ def generate_questions(roles, data):
129
  return questions
130
 
131
  def generate_overall_feedback(data, percent, answer, questions):
 
 
132
  prompt = f"""As an interviewer, provide concise feedback (max 150 words) for candidate {data}.
133
  Questions asked: {questions}
134
  Candidate's answers: {answer}
135
- Score: {percent}
136
  Feedback should include:
137
  1. Overall performance assessment (2-3 sentences)
138
  2. Key strengths (2-3 points)
@@ -146,35 +149,6 @@ def generate_overall_feedback(data, percent, answer, questions):
146
  print(f"Error generating overall feedback: {e}")
147
  return "Feedback could not be generated."
148
 
149
- def store_audio_text():
150
- r = sr.Recognizer()
151
- r.energy_threshold = 300
152
- r.dynamic_energy_threshold = True
153
- r.pause_threshold = 3
154
- with sr.Microphone() as source:
155
- print("Adjusting for ambient noise...")
156
- r.adjust_for_ambient_noise(source, duration=1)
157
- print("Speak now... (You have 200 seconds)")
158
- try:
159
- # Listen for up to 380 seconds, but stop if 200 seconds of silence
160
- audio = r.listen(source, timeout=380, phrase_time_limit=200)
161
- print("Processing audio...")
162
- text = r.recognize_google(audio)
163
- print(f"Recognized text: {text}")
164
- return text
165
- except sr.WaitTimeoutError:
166
- print("Listening timed out.")
167
- return " "
168
- except sr.RequestError as e:
169
- print(f"Could not request results from Google Speech Recognition service; {e}")
170
- return " "
171
- except sr.UnknownValueError:
172
- print("Google Speech Recognition could not understand audio")
173
- return " "
174
- except Exception as e:
175
- print(f"An error occurred during speech recognition: {e}")
176
- return " "
177
-
178
  def generate_metrics(data, answer, question):
179
  metrics = {}
180
  text = f"""Here is the overview of the candidate {data}. In the interview the question asked was {question}.
@@ -233,7 +207,11 @@ def generate_metrics(data, answer, question):
233
  def process_resume(file_obj):
234
  """Handles resume upload and processing."""
235
  if not file_obj:
236
- return "Please upload a PDF resume.", gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False)
 
 
 
 
237
 
238
  try:
239
  # Save uploaded file to a temporary location
@@ -247,7 +225,11 @@ def process_resume(file_obj):
247
  if not raw_text.strip():
248
  os.remove(file_path)
249
  os.rmdir(temp_dir)
250
- return "Could not extract text from the PDF.", gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False)
 
 
 
 
251
 
252
  processed_data = getallinfo(raw_text)
253
 
@@ -258,27 +240,36 @@ def process_resume(file_obj):
258
  return (
259
  f"File processed successfully!",
260
  gr.update(visible=True), # Role selection dropdown
261
- gr.update(visible=False),
262
- gr.update(visible=False),
263
- gr.update(visible=False),
264
- gr.update(visible=False),
265
- gr.update(visible=False),
266
- gr.update(visible=False),
267
- gr.update(visible=False),
268
- gr.update(visible=False),
269
- gr.update(visible=False),
270
- gr.update(visible=False),
271
- gr.update(visible=False),
272
- gr.update(visible=False),
273
  processed_data # Pass processed data for next step
274
  )
275
  except Exception as e:
276
- return f"Error processing file: {str(e)}", gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), None
 
 
 
 
 
 
277
 
278
  def start_interview(roles, processed_resume_data):
279
  """Starts the interview process."""
280
- if not roles or not processed_resume_data:
281
- return "Please select a role and ensure resume is processed.", "", [], [], {}, {}, gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False)
 
 
 
 
282
 
283
  try:
284
  questions = generate_questions(roles, processed_resume_data)
@@ -308,18 +299,25 @@ def start_interview(roles, processed_resume_data):
308
  gr.update(visible=False), # Submit Interview button (hidden initially)
309
  gr.update(visible=False), # Feedback textbox
310
  gr.update(visible=False), # Metrics display
311
- gr.update(visible=False), # Evaluation button (hidden initially)
312
  gr.update(visible=True), # Question display
313
  gr.update(visible=True), # Answer instructions
314
  interview_state
315
  )
316
  except Exception as e:
317
- return f"Error starting interview: {str(e)}", "", [], [], {}, {}, gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), None
 
 
 
 
318
 
319
  def submit_answer(audio, interview_state):
320
  """Handles submitting an answer via audio."""
321
  if not audio or not interview_state:
322
- return "No audio recorded or interview not started.", "", interview_state, gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False)
 
 
 
 
323
 
324
  try:
325
  # Save audio to a temporary file
@@ -327,8 +325,7 @@ def submit_answer(audio, interview_state):
327
  audio_file_path = os.path.join(temp_dir, "recorded_audio.wav")
328
  # audio is a tuple (sample_rate, numpy_array)
329
  sample_rate, audio_data = audio
330
- # Use soundfile or scipy to save the numpy array as a WAV file
331
- import soundfile as sf
332
  sf.write(audio_file_path, audio_data, sample_rate)
333
 
334
  # Convert audio file to text
@@ -382,12 +379,20 @@ def submit_answer(audio, interview_state):
382
 
383
  except Exception as e:
384
  print(f"Error processing audio answer: {e}")
385
- return "Error processing audio. Please try again.", "", interview_state, gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=True), gr.update(visible=True), gr.update(visible=True), gr.update(visible=False), gr.update(visible=True), gr.update(visible=True)
 
 
 
 
386
 
387
  def next_question(interview_state):
388
  """Moves to the next question or ends the interview."""
389
  if not interview_state:
390
- return "Interview not started.", "", interview_state, gr.update(visible=True), gr.update(visible=True), gr.update(visible=True), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False)
 
 
 
 
391
 
392
  current_q_index = interview_state["current_q_index"]
393
  total_questions = len(interview_state["questions"])
@@ -447,18 +452,32 @@ def login(username, password):
447
  # Simple mock login - replace with real authentication logic
448
  # For demo, accept any non-empty username/password
449
  if username and password:
450
- return f"Welcome, {username}!", gr.update(visible=True), gr.update(visible=False), gr.update(visible=False), "", ""
 
 
 
 
 
 
451
  else:
452
- return "Please enter username and password.", gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), username, password
 
 
 
 
453
 
454
  def logout():
455
- return "", gr.update(visible=False), gr.update(visible=True), gr.update(visible=True), "", ""
 
 
 
 
456
 
457
  def navigate_to_interview():
458
- return gr.update(visible=True), gr.update(visible=False) # Show interview, hide chat
459
 
460
  def navigate_to_chat():
461
- return gr.update(visible=False), gr.update(visible=True) # Hide interview, show chat
462
 
463
  # --- Gradio Interface ---
464
 
@@ -476,12 +495,6 @@ with gr.Blocks(title="PrepGenie - Mock Interview") as demo:
476
  password_input = gr.Textbox(label="Password", type="password")
477
  login_btn = gr.Button("Login")
478
  login_status = gr.Textbox(label="Status", interactive=False)
479
- # Initially visible login section
480
- login_btn.click(
481
- fn=login,
482
- inputs=[username_input, password_input],
483
- outputs=[login_status, login_section, interview_selection, chat_selection, username_input, password_input]
484
- )
485
 
486
  # --- Main App Sections (Initially Hidden) ---
487
  with gr.Column(visible=False) as main_app:
@@ -489,7 +502,8 @@ with gr.Blocks(title="PrepGenie - Mock Interview") as demo:
489
  with gr.Column(scale=1):
490
  logout_btn = gr.Button("Logout")
491
  with gr.Column(scale=4):
492
- gr.Markdown(f"### Welcome, User!") # This won't dynamically update easily in Gradio Blocks without JS
 
493
 
494
  with gr.Row():
495
  with gr.Column(scale=1):
@@ -497,7 +511,7 @@ with gr.Blocks(title="PrepGenie - Mock Interview") as demo:
497
  chat_btn = gr.Button("Chat with Resume")
498
  with gr.Column(scale=4):
499
  # --- Interview Section ---
500
- with gr.Column(visible=False) as interview_selection:
501
  gr.Markdown("## Mock Interview")
502
  # File Upload Section
503
  with gr.Row():
@@ -530,47 +544,10 @@ with gr.Blocks(title="PrepGenie - Mock Interview") as demo:
530
  metrics_display = gr.JSON(label="Metrics", visible=False)
531
 
532
  # Hidden textbox to hold processed resume data temporarily
533
- processed_resume_data = gr.Textbox(visible=False)
534
-
535
- # --- Event Listeners for Interview ---
536
- # Process Resume
537
- process_btn.click(
538
- fn=process_resume,
539
- inputs=[file_upload],
540
- outputs=[file_status, role_selection, start_interview_btn, question_display, answer_instructions, audio_input, submit_answer_btn, next_question_btn, submit_interview_btn, answer_display, feedback_display, metrics_display, processed_resume_data]
541
- )
542
-
543
- # Start Interview
544
- start_interview_btn.click(
545
- fn=start_interview,
546
- inputs=[role_selection, processed_resume_data],
547
- outputs=[file_status, question_display, interview_state["questions"], interview_state["answers"], interview_state["interactions"], interview_state["metrics_list"], audio_input, submit_answer_btn, next_question_btn, submit_interview_btn, feedback_display, metrics_display, interview_state, question_display, answer_instructions, interview_state]
548
- )
549
-
550
- # Submit Answer
551
- submit_answer_btn.click(
552
- fn=submit_answer,
553
- inputs=[audio_input, interview_state],
554
- outputs=[file_status, answer_display, interview_state, feedback_display, feedback_display, metrics_display, metrics_display, audio_input, submit_answer_btn, next_question_btn, submit_interview_btn, question_display, answer_instructions]
555
- )
556
-
557
- # Next Question
558
- next_question_btn.click(
559
- fn=next_question,
560
- inputs=[interview_state],
561
- outputs=[file_status, question_display, interview_state, audio_input, submit_answer_btn, next_question_btn, feedback_display, metrics_display, submit_interview_btn, question_display, answer_instructions, answer_display, metrics_display]
562
- )
563
-
564
- # Submit Interview (Placeholder for evaluation trigger)
565
- submit_interview_btn.click(
566
- fn=submit_interview,
567
- inputs=[interview_state],
568
- outputs=[file_status, interview_state]
569
- # In a full app, you might navigate to an evaluation page here
570
- )
571
 
572
  # --- Chat Section ---
573
- with gr.Column(visible=False) as chat_selection:
574
  gr.Markdown("## Chat with Resume (Placeholder)")
575
  gr.Markdown("This section would contain the chat interface logic from `chat.py`.")
576
  # You would integrate the chat logic here, similar to how interview is done.
@@ -579,10 +556,92 @@ with gr.Blocks(title="PrepGenie - Mock Interview") as demo:
579
 
580
 
581
  # Navigation buttons
582
- interview_btn.click(fn=navigate_to_interview, inputs=None, outputs=[interview_selection, chat_selection])
583
- chat_btn.click(fn=navigate_to_chat, inputs=None, outputs=[interview_selection, chat_selection])
584
- logout_btn.click(fn=logout, inputs=None, outputs=[login_status, login_section, interview_selection, chat_selection, username_input, password_input])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
585
 
586
  # Run the app
587
  if __name__ == "__main__":
588
- demo.launch(share=True) # You can add server_name="0.0.0.0", server_port=7860 for external access
 
12
  # import pygame # Not used directly in main app logic here
13
  import time
14
  from dotenv import load_dotenv
15
+ import soundfile as sf # For saving audio numpy array
16
 
17
  # Load environment variables
18
  load_dotenv()
 
103
  text = f"""If this is not a resume then return text uploaded pdf is not a resume. this is a resume overview of the candidate.
104
  The candidate details are in {data}. The candidate has applied for the role of {roles_str}.
105
  Generate questions for the candidate based on the role applied and on the Resume of the candidate.
106
+ Not always necessary to ask only technical questions related to the role but the logic of question
107
  should include the job applied for because there might be some deep tech questions which the user might not know.
108
+ Ask some personal questions too. Ask no additional questions. Don't categorize the questions.
109
  ask 2 questions only. directly ask the questions not anything else.
110
  Also ask the questions in a polite way. Ask the questions in a way that the candidate can understand the question.
111
  and make sure the questions are related to these metrics: Communication skills, Teamwork and collaboration,
 
130
  return questions
131
 
132
  def generate_overall_feedback(data, percent, answer, questions):
133
+ # Ensure percent is a string for formatting
134
+ percent_str = str(percent)
135
  prompt = f"""As an interviewer, provide concise feedback (max 150 words) for candidate {data}.
136
  Questions asked: {questions}
137
  Candidate's answers: {answer}
138
+ Score: {percent_str}
139
  Feedback should include:
140
  1. Overall performance assessment (2-3 sentences)
141
  2. Key strengths (2-3 points)
 
149
  print(f"Error generating overall feedback: {e}")
150
  return "Feedback could not be generated."
151
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
152
  def generate_metrics(data, answer, question):
153
  metrics = {}
154
  text = f"""Here is the overview of the candidate {data}. In the interview the question asked was {question}.
 
207
  def process_resume(file_obj):
208
  """Handles resume upload and processing."""
209
  if not file_obj:
210
+ return ("Please upload a PDF resume.", gr.update(visible=False), gr.update(visible=False),
211
+ gr.update(visible=False), gr.update(visible=False), gr.update(visible=False),
212
+ gr.update(visible=False), gr.update(visible=False), gr.update(visible=False),
213
+ gr.update(visible=False), gr.update(visible=False), gr.update(visible=False),
214
+ gr.update(visible=False), gr.update(visible=False))
215
 
216
  try:
217
  # Save uploaded file to a temporary location
 
225
  if not raw_text.strip():
226
  os.remove(file_path)
227
  os.rmdir(temp_dir)
228
+ return ("Could not extract text from the PDF.", gr.update(visible=False), gr.update(visible=False),
229
+ gr.update(visible=False), gr.update(visible=False), gr.update(visible=False),
230
+ gr.update(visible=False), gr.update(visible=False), gr.update(visible=False),
231
+ gr.update(visible=False), gr.update(visible=False), gr.update(visible=False),
232
+ gr.update(visible=False), gr.update(visible=False))
233
 
234
  processed_data = getallinfo(raw_text)
235
 
 
240
  return (
241
  f"File processed successfully!",
242
  gr.update(visible=True), # Role selection dropdown
243
+ gr.update(visible=True), # Start Interview button
244
+ gr.update(visible=False), # Question display (initially)
245
+ gr.update(visible=False), # Answer instructions (initially)
246
+ gr.update(visible=False), # Audio input (initially)
247
+ gr.update(visible=False), # Submit Answer button (initially)
248
+ gr.update(visible=False), # Next Question button (initially)
249
+ gr.update(visible=False), # Submit Interview button (initially)
250
+ gr.update(visible=False), # Answer display (initially)
251
+ gr.update(visible=False), # Feedback display (initially)
252
+ gr.update(visible=False), # Metrics display (initially)
253
+ gr.update(visible=False), # Processed resume data textbox (hidden)
 
254
  processed_data # Pass processed data for next step
255
  )
256
  except Exception as e:
257
+ error_msg = f"Error processing file: {str(e)}"
258
+ print(error_msg)
259
+ return (error_msg, gr.update(visible=False), gr.update(visible=False),
260
+ gr.update(visible=False), gr.update(visible=False), gr.update(visible=False),
261
+ gr.update(visible=False), gr.update(visible=False), gr.update(visible=False),
262
+ gr.update(visible=False), gr.update(visible=False), gr.update(visible=False),
263
+ gr.update(visible=False), gr.update(visible=False))
264
 
265
  def start_interview(roles, processed_resume_data):
266
  """Starts the interview process."""
267
+ if not roles or not processed_resume_
268
+ return ("Please select a role and ensure resume is processed.", "", [], [], {}, {},
269
+ gr.update(visible=False), gr.update(visible=False), gr.update(visible=False),
270
+ gr.update(visible=False), gr.update(visible=False), gr.update(visible=False),
271
+ gr.update(visible=False), gr.update(visible=False), gr.update(visible=False),
272
+ gr.update(visible=False), {}) # Return empty state on error
273
 
274
  try:
275
  questions = generate_questions(roles, processed_resume_data)
 
299
  gr.update(visible=False), # Submit Interview button (hidden initially)
300
  gr.update(visible=False), # Feedback textbox
301
  gr.update(visible=False), # Metrics display
 
302
  gr.update(visible=True), # Question display
303
  gr.update(visible=True), # Answer instructions
304
  interview_state
305
  )
306
  except Exception as e:
307
+ error_msg = f"Error starting interview: {str(e)}"
308
+ print(error_msg)
309
+ return (error_msg, "", [], [], {}, {}, gr.update(visible=False), gr.update(visible=False),
310
+ gr.update(visible=False), gr.update(visible=False), gr.update(visible=False),
311
+ gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), {})
312
 
313
  def submit_answer(audio, interview_state):
314
  """Handles submitting an answer via audio."""
315
  if not audio or not interview_state:
316
+ return ("No audio recorded or interview not started.", "", interview_state,
317
+ gr.update(visible=False), gr.update(visible=False), gr.update(visible=False),
318
+ gr.update(visible=False), gr.update(visible=True), gr.update(visible=True),
319
+ gr.update(visible=True), gr.update(visible=False), gr.update(visible=True),
320
+ gr.update(visible=True))
321
 
322
  try:
323
  # Save audio to a temporary file
 
325
  audio_file_path = os.path.join(temp_dir, "recorded_audio.wav")
326
  # audio is a tuple (sample_rate, numpy_array)
327
  sample_rate, audio_data = audio
328
+ # Use soundfile to save the numpy array as a WAV file
 
329
  sf.write(audio_file_path, audio_data, sample_rate)
330
 
331
  # Convert audio file to text
 
379
 
380
  except Exception as e:
381
  print(f"Error processing audio answer: {e}")
382
+ return ("Error processing audio. Please try again.", "", interview_state,
383
+ gr.update(visible=False), gr.update(visible=False), gr.update(visible=False),
384
+ gr.update(visible=False), gr.update(visible=True), gr.update(visible=True),
385
+ gr.update(visible=True), gr.update(visible=False), gr.update(visible=True),
386
+ gr.update(visible=True))
387
 
388
  def next_question(interview_state):
389
  """Moves to the next question or ends the interview."""
390
  if not interview_state:
391
+ return ("Interview not started.", "", interview_state, gr.update(visible=True),
392
+ gr.update(visible=True), gr.update(visible=True), gr.update(visible=False),
393
+ gr.update(visible=False), gr.update(visible=False), gr.update(visible=False),
394
+ gr.update(visible=False), gr.update(visible=True), gr.update(visible=True),
395
+ gr.update(visible=False), gr.update(visible=False))
396
 
397
  current_q_index = interview_state["current_q_index"]
398
  total_questions = len(interview_state["questions"])
 
452
  # Simple mock login - replace with real authentication logic
453
  # For demo, accept any non-empty username/password
454
  if username and password:
455
+ welcome_msg = f"Welcome, {username}!"
456
+ # Show main app, hide login
457
+ return (welcome_msg,
458
+ gr.update(visible=False), # login_section
459
+ gr.update(visible=True), # main_app
460
+ "", "", # Clear username/password inputs
461
+ username) # Update user_state
462
  else:
463
+ return ("Please enter username and password.",
464
+ gr.update(visible=True), # login_section stays visible
465
+ gr.update(visible=False), # main_app stays hidden
466
+ username, password, # Keep inputs
467
+ "") # user_state empty
468
 
469
  def logout():
470
+ return ("", # Clear login status
471
+ gr.update(visible=True), # Show login section
472
+ gr.update(visible=False), # Hide main app
473
+ "", "", # Clear username/password inputs
474
+ "") # Clear user_state
475
 
476
  def navigate_to_interview():
477
+ return (gr.update(visible=True), gr.update(visible=False)) # Show interview, hide chat
478
 
479
  def navigate_to_chat():
480
+ return (gr.update(visible=False), gr.update(visible=True)) # Hide interview, show chat
481
 
482
  # --- Gradio Interface ---
483
 
 
495
  password_input = gr.Textbox(label="Password", type="password")
496
  login_btn = gr.Button("Login")
497
  login_status = gr.Textbox(label="Status", interactive=False)
 
 
 
 
 
 
498
 
499
  # --- Main App Sections (Initially Hidden) ---
500
  with gr.Column(visible=False) as main_app:
 
502
  with gr.Column(scale=1):
503
  logout_btn = gr.Button("Logout")
504
  with gr.Column(scale=4):
505
+ # Dynamic welcome message (basic approach)
506
+ welcome_display = gr.Markdown("### Welcome, User!")
507
 
508
  with gr.Row():
509
  with gr.Column(scale=1):
 
511
  chat_btn = gr.Button("Chat with Resume")
512
  with gr.Column(scale=4):
513
  # --- Interview Section ---
514
+ with gr.Column(visible=False) as interview_selection: # Define interview_selection
515
  gr.Markdown("## Mock Interview")
516
  # File Upload Section
517
  with gr.Row():
 
544
  metrics_display = gr.JSON(label="Metrics", visible=False)
545
 
546
  # Hidden textbox to hold processed resume data temporarily
547
+ processed_resume_data_hidden = gr.Textbox(visible=False) # Renamed for clarity
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
548
 
549
  # --- Chat Section ---
550
+ with gr.Column(visible=False) as chat_selection: # Define chat_selection
551
  gr.Markdown("## Chat with Resume (Placeholder)")
552
  gr.Markdown("This section would contain the chat interface logic from `chat.py`.")
553
  # You would integrate the chat logic here, similar to how interview is done.
 
556
 
557
 
558
  # Navigation buttons
559
+ # Define the components for navigation *before* using them in event listeners
560
+ interview_view = interview_selection
561
+ chat_view = chat_selection
562
+
563
+ interview_btn.click(fn=navigate_to_interview, inputs=None, outputs=[interview_view, chat_view])
564
+ chat_btn.click(fn=navigate_to_chat, inputs=None, outputs=[interview_view, chat_view])
565
+ # Update welcome message when user_state changes (basic)
566
+ user_state.change(fn=lambda user: f"### Welcome, {user}!" if user else "### Welcome, User!", inputs=[user_state], outputs=[welcome_display])
567
+
568
+ # --- Event Listeners ---
569
+
570
+ # Process Resume
571
+ process_btn.click(
572
+ fn=process_resume,
573
+ inputs=[file_upload],
574
+ outputs=[
575
+ file_status, role_selection, start_interview_btn,
576
+ question_display, answer_instructions, audio_input,
577
+ submit_answer_btn, next_question_btn, submit_interview_btn,
578
+ answer_display, feedback_display, metrics_display,
579
+ processed_resume_data_hidden # Pass processed data for next step
580
+ ]
581
+ )
582
+
583
+ # Start Interview
584
+ start_interview_btn.click(
585
+ fn=start_interview,
586
+ inputs=[role_selection, processed_resume_data_hidden],
587
+ outputs=[
588
+ file_status, question_display,
589
+ interview_state["questions"], interview_state["answers"],
590
+ interview_state["interactions"], interview_state["metrics_list"],
591
+ audio_input, submit_answer_btn, next_question_btn,
592
+ submit_interview_btn, feedback_display, metrics_display,
593
+ question_display, answer_instructions, # These are UI updates
594
+ interview_state # Update the state object
595
+ ]
596
+ )
597
+
598
+ # Submit Answer
599
+ submit_answer_btn.click(
600
+ fn=submit_answer,
601
+ inputs=[audio_input, interview_state],
602
+ outputs=[
603
+ file_status, answer_display, interview_state,
604
+ feedback_display, feedback_display, # Update value and visibility
605
+ metrics_display, metrics_display, # Update value and visibility
606
+ audio_input, submit_answer_btn, next_question_btn,
607
+ submit_interview_btn, question_display, answer_instructions
608
+ ]
609
+ )
610
+
611
+ # Next Question
612
+ next_question_btn.click(
613
+ fn=next_question,
614
+ inputs=[interview_state],
615
+ outputs=[
616
+ file_status, question_display, interview_state,
617
+ audio_input, submit_answer_btn, next_question_btn,
618
+ feedback_display, metrics_display, submit_interview_btn,
619
+ question_display, answer_instructions,
620
+ answer_display, metrics_display # Clear previous answer/metrics display
621
+ ]
622
+ )
623
+
624
+ # Submit Interview (Placeholder for evaluation trigger)
625
+ submit_interview_btn.click(
626
+ fn=submit_interview,
627
+ inputs=[interview_state],
628
+ outputs=[file_status, interview_state]
629
+ # In a full app, you might navigate to an evaluation page here
630
+ )
631
+
632
+ # Login/Logout Event Listeners
633
+ login_btn.click(
634
+ fn=login,
635
+ inputs=[username_input, password_input],
636
+ outputs=[login_status, login_section, main_app, username_input, password_input, user_state]
637
+ )
638
+
639
+ logout_btn.click(
640
+ fn=logout,
641
+ inputs=None,
642
+ outputs=[login_status, login_section, main_app, username_input, password_input, user_state]
643
+ )
644
 
645
  # Run the app
646
  if __name__ == "__main__":
647
+ demo.launch(share=True) # You can add server_name="0.0.0.0", server_port=7860 for external access