Kunjan Shah commited on
Commit
644212b
·
1 Parent(s): 1fea7cc

Add Gradio app

Browse files
Files changed (1) hide show
  1. gradio_app.py +590 -0
gradio_app.py ADDED
@@ -0,0 +1,590 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import gradio as gr
3
+ import json
4
+ from datetime import datetime
5
+ import tempfile
6
+
7
+ # Import your existing modules
8
+ from core.utils import FileProcessor, star_rating
9
+ from core.question_generator import generate_question
10
+ from core.answering_competitor import Answering_competitor
11
+ from core.response_evaluator import scorer
12
+ from core.summary_utils import custom_css, generate_text_summary, clean_json_response
13
+ from core.generate_summary import generate_summary_content
14
+ from core.speech_converter import text_to_audio, load_model
15
+
16
+ # Initialize Whisper model
17
+ whisper_model = load_model()
18
+
19
+ # Global CSS for styling
20
+ CUSTOM_CSS = """
21
+ .gradio-container {
22
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
23
+ }
24
+
25
+ .question-container {
26
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
27
+ padding: 20px;
28
+ border-radius: 10px;
29
+ color: white;
30
+ margin: 10px 0;
31
+ }
32
+
33
+ .score-container {
34
+ background: #f8f9fa;
35
+ padding: 15px;
36
+ border-radius: 8px;
37
+ border-left: 4px solid #007bff;
38
+ margin: 10px 0;
39
+ }
40
+
41
+ .summary-section {
42
+ background: #ffffff;
43
+ padding: 20px;
44
+ border-radius: 10px;
45
+ box-shadow: 0 2px 10px rgba(0,0,0,0.1);
46
+ margin: 15px 0;
47
+ }
48
+
49
+ .metric-card {
50
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
51
+ color: white;
52
+ padding: 20px;
53
+ border-radius: 10px;
54
+ text-align: center;
55
+ margin: 10px;
56
+ }
57
+
58
+ .strength-card {
59
+ background: #d4edda;
60
+ border: 1px solid #c3e6cb;
61
+ color: #155724;
62
+ padding: 15px;
63
+ border-radius: 8px;
64
+ }
65
+
66
+ .weakness-card {
67
+ background: #f8d7da;
68
+ border: 1px solid #f5c6cb;
69
+ color: #721c24;
70
+ padding: 15px;
71
+ border-radius: 8px;
72
+ }
73
+ """
74
+
75
+ def initialize_app_state():
76
+ """Initialize the application state"""
77
+ return {
78
+ 'current_page': 'welcome',
79
+ 'questions': [],
80
+ 'current_question': 0,
81
+ 'resume_text': '',
82
+ 'jd_text': '',
83
+ 'job_role': '',
84
+ 'improve_percentage': 10,
85
+ 'user_answers': [],
86
+ 'ai_answers': [],
87
+ 'scores': [],
88
+ 'track_score': [],
89
+ 'summary_data': None,
90
+ 'questions_generated': False,
91
+ 'competitor_initialized': False
92
+ }
93
+
94
+ def process_files_and_generate_questions(resume_file, jd_file, jd_text, job_role, improve_percentage, app_state):
95
+ """Process uploaded files and generate questions"""
96
+ try:
97
+ # Process resume
98
+ if resume_file is None:
99
+ return "❌ Please upload a resume file", app_state, gr.update(visible=False)
100
+
101
+ resume_text = FileProcessor.read_resume(resume_file)
102
+
103
+ # Process job description
104
+ jd_content = ""
105
+ if jd_file is not None:
106
+ if jd_file.name.endswith('.pdf'):
107
+ jd_content = FileProcessor.read_job_description_pdf(jd_file)
108
+ elif jd_file.name.endswith('.txt'):
109
+ jd_content = FileProcessor.read_job_description_txt(jd_file)
110
+ elif jd_text and len(jd_text.strip()) > 10:
111
+ jd_content = jd_text.strip()
112
+ else:
113
+ return "❌ Please provide a job description", app_state, gr.update(visible=False)
114
+
115
+ if not job_role or len(job_role.strip()) == 0:
116
+ return "❌ Please enter a job role", app_state, gr.update(visible=False)
117
+
118
+ # Update state
119
+ app_state['resume_text'] = resume_text
120
+ app_state['jd_text'] = jd_content
121
+ app_state['job_role'] = job_role
122
+ app_state['improve_percentage'] = improve_percentage
123
+ app_state['current_page'] = 'loading'
124
+
125
+ # Generate questions
126
+ questions = generate_question(resume_text, jd_content, job_role)
127
+ app_state['questions'] = questions
128
+ app_state['questions_generated'] = True
129
+
130
+ # Initialize competitor
131
+ comp_ans_gen = Answering_competitor(
132
+ resume=resume_text,
133
+ job_description=jd_content,
134
+ difficulty_level=improve_percentage,
135
+ questions=questions
136
+ )
137
+ comp_ans_gen.extract_factors()
138
+ comp_ans_gen.determine_enhancement()
139
+ comp_ans_gen.generate_resume()
140
+
141
+ # Generate competitor answers
142
+ comp_answers = comp_ans_gen.answer_questions()
143
+ app_state['ai_answers'] = list(comp_answers.values())
144
+ app_state['competitor_initialized'] = True
145
+ app_state['current_page'] = 'questions'
146
+ app_state['current_question'] = 0
147
+
148
+ return "✅ Questions generated successfully! Starting interview...", app_state, gr.update(visible=True)
149
+
150
+ except Exception as e:
151
+ return f"❌ Error: {str(e)}", app_state, gr.update(visible=False)
152
+
153
+ def get_current_question_display(app_state):
154
+ """Get the current question display information"""
155
+ if not app_state['questions'] or app_state['current_question'] >= len(app_state['questions']):
156
+ return "No questions available", "", "", False, False, gr.update(visible=False)
157
+
158
+ current_q = app_state['current_question']
159
+ question = app_state['questions'][current_q]
160
+ ai_answer = app_state['ai_answers'][current_q] if current_q < len(app_state['ai_answers']) else ""
161
+
162
+ progress = f"Question {current_q + 1} of {len(app_state['questions'])}"
163
+
164
+ show_prev = current_q > 0
165
+ show_next = current_q < len(app_state['questions']) - 1
166
+ show_summary = current_q == len(app_state['questions']) - 1
167
+
168
+ return question, ai_answer, progress, show_prev, show_next or show_summary, gr.update(visible=True)
169
+
170
+ def submit_answer(user_answer, audio_file, app_state):
171
+ """Submit user answer and get scoring"""
172
+ if not user_answer and not audio_file:
173
+ return "Please provide an answer", app_state, "", ""
174
+
175
+ # Handle audio transcription if provided
176
+ final_answer = user_answer
177
+ if audio_file:
178
+ try:
179
+ # Create temporary file for audio processing
180
+ with tempfile.NamedTemporaryFile(suffix='.wav', delete=False) as tmp_file:
181
+ # Save audio file
182
+ with open(audio_file, 'rb') as f:
183
+ tmp_file.write(f.read())
184
+ tmp_file.flush()
185
+
186
+ # Transcribe audio
187
+ transcribed = whisper_model.transcribe(tmp_file.name)["text"]
188
+ final_answer = transcribed if not user_answer else user_answer + " " + transcribed
189
+
190
+ # Clean up
191
+ os.unlink(tmp_file.name)
192
+ except Exception as e:
193
+ return f"Audio transcription error: {str(e)}", app_state, "", ""
194
+
195
+ current_q = app_state['current_question']
196
+
197
+ # Ensure user_answers list is long enough
198
+ while len(app_state['user_answers']) <= current_q:
199
+ app_state['user_answers'].append("")
200
+
201
+ app_state['user_answers'][current_q] = final_answer
202
+
203
+ # Score the answer
204
+ try:
205
+ result_score = scorer(
206
+ jd=app_state['jd_text'],
207
+ ques=app_state['questions'][current_q],
208
+ user=final_answer,
209
+ competitor=app_state['ai_answers'][current_q]
210
+ )
211
+
212
+ # Track user score
213
+ user_score = result_score["user"]
214
+ app_state['track_score'].append(user_score)
215
+
216
+ # Format scoring display
217
+ user_scoring = f"""
218
+ **Your Score:**
219
+ Structure: {star_rating(user_score['structure_star']['score'])}
220
+ Depth: {star_rating(user_score['depth']['score'])}
221
+ Clarity: {star_rating(user_score['clarity']['score'])}
222
+ Correctness: {star_rating(user_score['correctness']['score'])}
223
+ """
224
+
225
+ competitor_score = result_score["competitor"]
226
+ ai_scoring = f"""
227
+ **AI Competitor Score:**
228
+ Structure: {star_rating(competitor_score['structure_star']['score'])}
229
+ Depth: {star_rating(competitor_score['depth']['score'])}
230
+ Clarity: {star_rating(competitor_score['clarity']['score'])}
231
+ Correctness: {star_rating(competitor_score['correctness']['score'])}
232
+ """
233
+
234
+ return "Answer submitted successfully!", app_state, user_scoring, ai_scoring
235
+
236
+ except Exception as e:
237
+ return f"Scoring error: {str(e)}", app_state, "", ""
238
+
239
+ def navigate_question(direction, app_state):
240
+ """Navigate between questions"""
241
+ if direction == "prev" and app_state['current_question'] > 0:
242
+ app_state['current_question'] -= 1
243
+ elif direction == "next" and app_state['current_question'] < len(app_state['questions']) - 1:
244
+ app_state['current_question'] += 1
245
+ elif direction == "summary":
246
+ app_state['current_page'] = 'summary'
247
+
248
+ return app_state
249
+
250
+ def generate_summary_display(app_state):
251
+ """Generate and display interview summary"""
252
+ if app_state['current_page'] != 'summary':
253
+ return "Summary not ready", app_state, "", gr.update(visible=False)
254
+
255
+ try:
256
+ # Ensure all answers are collected
257
+ if not app_state['summary_data']:
258
+ # Calculate scores
259
+ app_state['scores'] = [
260
+ [
261
+ int(track_score["structure_star"]["score"]),
262
+ int(track_score["depth"]["score"]),
263
+ int(track_score["clarity"]["score"]),
264
+ int(track_score["correctness"]["score"])
265
+ ]
266
+ for track_score in app_state['track_score']
267
+ ]
268
+
269
+ # Generate summary
270
+ summary_json = generate_summary_content(
271
+ app_state['resume_text'],
272
+ app_state['jd_text'],
273
+ app_state['job_role'],
274
+ app_state['questions'],
275
+ app_state['user_answers'],
276
+ app_state['ai_answers'],
277
+ app_state['scores']
278
+ )
279
+
280
+ cleaned_json = clean_json_response(summary_json)
281
+ summary_data = json.loads(cleaned_json)
282
+ app_state['summary_data'] = summary_data
283
+
284
+ summary_data = app_state['summary_data']
285
+
286
+ # Calculate average score
287
+ scores_list = app_state['scores']
288
+ avg_score = sum([sum(q_score) for q_score in scores_list]) / (len(scores_list) * 4) * 10 if scores_list else 0
289
+
290
+ # Format summary display
291
+ summary_html = f"""
292
+ <div class="summary-section">
293
+ <h1>🎯 Interview Performance Summary</h1>
294
+
295
+ <div class="metric-card">
296
+ <h2>Overall Score: {avg_score:.1f}/100</h2>
297
+ <p>Questions Analyzed: {len(summary_data.get("comparison_table", []))}</p>
298
+ <p>Topics Covered: {len(summary_data.get("topics_covered", []))}</p>
299
+ </div>
300
+
301
+ <h2>📈 Performance Trends</h2>
302
+ <div class="score-container">
303
+ {summary_data.get("trends", "No trend data available")}
304
+ </div>
305
+
306
+ <div style="display: flex; gap: 20px;">
307
+ <div style="flex: 1;">
308
+ <h2>💪 Your Strengths</h2>
309
+ <div class="strength-card">
310
+ {summary_data.get("strengths", "No strengths data available")}
311
+ </div>
312
+ </div>
313
+
314
+ <div style="flex: 1;">
315
+ <h2>🎯 Areas for Improvement</h2>
316
+ <div class="weakness-card">
317
+ {summary_data.get("weaknesses", "No weaknesses data available")}
318
+ </div>
319
+ </div>
320
+ </div>
321
+
322
+ <h2>📚 Recommended Resources</h2>
323
+ <div class="score-container">
324
+ {summary_data.get("resources", "No resources available")}
325
+ </div>
326
+ </div>
327
+ """
328
+
329
+ # Generate text summary for download
330
+ text_summary = generate_text_summary(summary_data, scores_list)
331
+
332
+ return summary_html, app_state, text_summary, gr.update(visible=True)
333
+
334
+ except Exception as e:
335
+ return f"Error generating summary: {str(e)}", app_state, "", gr.update(visible=False)
336
+
337
+ def create_gradio_interface():
338
+ """Create the main Gradio interface"""
339
+
340
+ with gr.Blocks(css=CUSTOM_CSS, title="AI Job Matcher - HiredGPT Duel") as app:
341
+ # State management
342
+ app_state = gr.State(initialize_app_state())
343
+
344
+ gr.Markdown("# 🤖 AI Job Matcher - HiredGPT Duel")
345
+ gr.Markdown("Test your interview skills against an AI competitor!")
346
+
347
+ with gr.Row():
348
+ with gr.Column(scale=1):
349
+ gr.Markdown("## 📄 Upload Your Resume")
350
+ resume_file = gr.File(label="Resume (PDF)", file_types=[".pdf"])
351
+
352
+ gr.Markdown("## 📋 Job Description")
353
+ jd_method = gr.Radio(
354
+ choices=["Upload File", "Paste Text"],
355
+ value="Upload File",
356
+ label="How to provide job description?"
357
+ )
358
+
359
+ jd_file = gr.File(
360
+ label="Job Description File (PDF/TXT)",
361
+ file_types=[".pdf", ".txt"],
362
+ visible=True
363
+ )
364
+
365
+ jd_text = gr.Textbox(
366
+ label="Job Description Text",
367
+ lines=5,
368
+ visible=False
369
+ )
370
+
371
+ gr.Markdown("## ⭐ Competitor Settings")
372
+ improve_percentage = gr.Slider(
373
+ minimum=10,
374
+ maximum=100,
375
+ step=10,
376
+ value=10,
377
+ label="AI Competitor Strength (%)"
378
+ )
379
+
380
+ gr.Markdown("## 💼 Job Role")
381
+ job_role = gr.Textbox(label="Job Title/Role")
382
+
383
+ start_btn = gr.Button("🚀 Start HiredGPT Duel", variant="primary")
384
+ status_msg = gr.Markdown("")
385
+
386
+ with gr.Column(scale=2):
387
+ # Question Interface
388
+ question_interface = gr.Group(visible=False)
389
+
390
+ with question_interface:
391
+ progress_display = gr.Markdown("")
392
+ question_display = gr.Markdown("")
393
+
394
+ with gr.Row():
395
+ with gr.Column():
396
+ gr.Markdown("### 👤 Your Answer")
397
+ user_answer = gr.Textbox(
398
+ label="Type your answer",
399
+ lines=6,
400
+ placeholder="Enter your response here..."
401
+ )
402
+ audio_input = gr.Audio(
403
+ label="Or record audio answer",
404
+ type="filepath"
405
+ )
406
+ submit_answer_btn = gr.Button("Submit Answer", variant="primary")
407
+
408
+ user_score_display = gr.Markdown("")
409
+
410
+ with gr.Column():
411
+ gr.Markdown("### 🤖 AI Competitor's Answer")
412
+ ai_answer_display = gr.Markdown("")
413
+ ai_score_display = gr.Markdown("")
414
+
415
+ with gr.Row():
416
+ prev_btn = gr.Button("⬅️ Previous", visible=False)
417
+ next_btn = gr.Button("Next ➡️", visible=False)
418
+ summary_btn = gr.Button("📊 View Summary", visible=False)
419
+
420
+ # Summary Interface
421
+ summary_interface = gr.Group(visible=False)
422
+
423
+ with summary_interface:
424
+ summary_display = gr.HTML("")
425
+ download_summary = gr.File(label="Download Summary", visible=False)
426
+ restart_btn = gr.Button("🔄 Start New Interview", variant="secondary")
427
+
428
+ # Event handlers
429
+ def toggle_jd_input(method):
430
+ if method == "Upload File":
431
+ return gr.update(visible=True), gr.update(visible=False)
432
+ else:
433
+ return gr.update(visible=False), gr.update(visible=True)
434
+
435
+ jd_method.change(
436
+ toggle_jd_input,
437
+ inputs=[jd_method],
438
+ outputs=[jd_file, jd_text]
439
+ )
440
+
441
+ def handle_start_click(resume, jd_file, jd_text, job_role, improve_pct, state):
442
+ msg, new_state, interface_update = process_files_and_generate_questions(
443
+ resume, jd_file, jd_text, job_role, improve_pct, state
444
+ )
445
+
446
+ if new_state['current_page'] == 'questions':
447
+ question, ai_answer, progress, show_prev, show_next, _ = get_current_question_display(new_state)
448
+ return (
449
+ msg, new_state, interface_update,
450
+ f"**{progress}**", f"### {question}",
451
+ "", ai_answer, "", "",
452
+ gr.update(visible=show_prev), gr.update(visible=show_next),
453
+ gr.update(visible=False), gr.update(visible=False)
454
+ )
455
+
456
+ return (
457
+ msg, new_state, interface_update,
458
+ "", "", "", "", "", "",
459
+ gr.update(visible=False), gr.update(visible=False),
460
+ gr.update(visible=False), gr.update(visible=False)
461
+ )
462
+
463
+ start_btn.click(
464
+ handle_start_click,
465
+ inputs=[resume_file, jd_file, jd_text, job_role, improve_percentage, app_state],
466
+ outputs=[
467
+ status_msg, app_state, question_interface,
468
+ progress_display, question_display,
469
+ user_answer, ai_answer_display, user_score_display, ai_score_display,
470
+ prev_btn, next_btn, summary_btn, summary_interface
471
+ ]
472
+ )
473
+
474
+ def handle_submit_answer(user_ans, audio, state):
475
+ msg, new_state, user_scoring, ai_scoring = submit_answer(user_ans, audio, state)
476
+
477
+ # Show AI answer after submission
478
+ current_q = new_state['current_question']
479
+ ai_answer = new_state['ai_answers'][current_q] if current_q < len(new_state['ai_answers']) else ""
480
+
481
+ # Update navigation buttons
482
+ show_prev = current_q > 0
483
+ show_next = current_q < len(new_state['questions']) - 1
484
+ show_summary = current_q == len(new_state['questions']) - 1
485
+
486
+ return (
487
+ new_state, ai_answer, user_scoring, ai_scoring,
488
+ gr.update(visible=show_prev),
489
+ gr.update(visible=show_next and not show_summary),
490
+ gr.update(visible=show_summary)
491
+ )
492
+
493
+ submit_answer_btn.click(
494
+ handle_submit_answer,
495
+ inputs=[user_answer, audio_input, app_state],
496
+ outputs=[
497
+ app_state, ai_answer_display, user_score_display, ai_score_display,
498
+ prev_btn, next_btn, summary_btn
499
+ ]
500
+ )
501
+
502
+ def handle_navigation(direction, state):
503
+ new_state = navigate_question(direction, state)
504
+
505
+ if new_state['current_page'] == 'summary':
506
+ summary_html, updated_state, text_summary, summary_visible = generate_summary_display(new_state)
507
+
508
+ # Create downloadable file
509
+ summary_file = None
510
+ if text_summary:
511
+ with tempfile.NamedTemporaryFile(mode='w', suffix='.txt', delete=False) as f:
512
+ f.write(text_summary)
513
+ summary_file = f.name
514
+
515
+ return (
516
+ updated_state,
517
+ gr.update(visible=False), summary_visible,
518
+ summary_html, summary_file,
519
+ "", "", "", "", "", "",
520
+ gr.update(visible=False), gr.update(visible=False), gr.update(visible=False)
521
+ )
522
+ else:
523
+ question, ai_answer, progress, show_prev, show_next, _ = get_current_question_display(new_state)
524
+ current_q = new_state['current_question']
525
+ user_ans = new_state['user_answers'][current_q] if current_q < len(new_state['user_answers']) else ""
526
+
527
+ return (
528
+ new_state,
529
+ gr.update(visible=True), gr.update(visible=False),
530
+ "", None,
531
+ f"**{progress}**", f"### {question}", user_ans, ai_answer, "", "",
532
+ gr.update(visible=show_prev), gr.update(visible=show_next),
533
+ gr.update(visible=current_q == len(new_state['questions']) - 1)
534
+ )
535
+
536
+ prev_btn.click(
537
+ lambda state: handle_navigation("prev", state),
538
+ inputs=[app_state],
539
+ outputs=[
540
+ app_state, question_interface, summary_interface,
541
+ summary_display, download_summary,
542
+ progress_display, question_display, user_answer, ai_answer_display,
543
+ user_score_display, ai_score_display,
544
+ prev_btn, next_btn, summary_btn
545
+ ]
546
+ )
547
+
548
+ next_btn.click(
549
+ lambda state: handle_navigation("next", state),
550
+ inputs=[app_state],
551
+ outputs=[
552
+ app_state, question_interface, summary_interface,
553
+ summary_display, download_summary,
554
+ progress_display, question_display, user_answer, ai_answer_display,
555
+ user_score_display, ai_score_display,
556
+ prev_btn, next_btn, summary_btn
557
+ ]
558
+ )
559
+
560
+ summary_btn.click(
561
+ lambda state: handle_navigation("summary", state),
562
+ inputs=[app_state],
563
+ outputs=[
564
+ app_state, question_interface, summary_interface,
565
+ summary_display, download_summary,
566
+ progress_display, question_display, user_answer, ai_answer_display,
567
+ user_score_display, ai_score_display,
568
+ prev_btn, next_btn, summary_btn
569
+ ]
570
+ )
571
+
572
+ def restart_app():
573
+ return initialize_app_state(), gr.update(visible=False), gr.update(visible=False)
574
+
575
+ restart_btn.click(
576
+ restart_app,
577
+ outputs=[app_state, question_interface, summary_interface]
578
+ )
579
+
580
+ return app
581
+
582
+ if __name__ == "__main__":
583
+ # Create and launch the Gradio app
584
+ app = create_gradio_interface()
585
+ app.launch(
586
+
587
+ server_port=7860,
588
+ share=False,
589
+ debug=True
590
+ )