capradeepgujaran commited on
Commit
fb9140d
1 Parent(s): 9e4ad7d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +54 -231
app.py CHANGED
@@ -21,6 +21,10 @@ class QuizFeedback:
21
  selected: Optional[str]
22
  correct_answer: str
23
 
 
 
 
 
24
  class QuizGenerator:
25
  def __init__(self, api_key: str):
26
  self.client = Groq(api_key=api_key)
@@ -113,34 +117,26 @@ class QuizGenerator:
113
  )
114
 
115
  class FontManager:
116
- """Manages font installation and loading for the certificate generator"""
117
-
118
  @staticmethod
119
  def install_fonts():
120
- """Install required fonts if they're not already present"""
121
  try:
122
- # Install fonts package
123
- subprocess.run([
124
- "apt-get", "update", "-y"
125
- ], check=True)
126
- subprocess.run([
127
- "apt-get", "install", "-y",
128
- "fonts-liberation", # Liberation Sans fonts
129
- "fontconfig", # Font configuration
130
- "fonts-dejavu-core" # DejaVu fonts as fallback
131
- ], check=True)
132
-
133
- # Clear font cache
134
  subprocess.run(["fc-cache", "-f"], check=True)
135
  print("Fonts installed successfully")
136
- except subprocess.CalledProcessError as e:
137
- print(f"Warning: Could not install fonts: {e}")
138
  except Exception as e:
139
- print(f"Warning: Unexpected error installing fonts: {e}")
140
 
141
  @staticmethod
142
  def get_font_paths() -> Dict[str, str]:
143
- """Get the paths to the required fonts with multiple fallbacks"""
144
  standard_paths = [
145
  "/usr/share/fonts",
146
  "/usr/local/share/fonts",
@@ -153,7 +149,6 @@ class FontManager:
153
  'bold': None
154
  }
155
 
156
- # Common font filenames to try
157
  fonts_to_try = {
158
  'regular': [
159
  'LiberationSans-Regular.ttf',
@@ -168,14 +163,12 @@ class FontManager:
168
  }
169
 
170
  def find_font(font_name: str) -> Optional[str]:
171
- """Search for a font file in standard locations"""
172
  for base_path in standard_paths:
173
  for root, _, files in os.walk(os.path.expanduser(base_path)):
174
  if font_name in files:
175
  return os.path.join(root, font_name)
176
  return None
177
 
178
- # Try to find each font
179
  for style in ['regular', 'bold']:
180
  for font_name in fonts_to_try[style]:
181
  font_path = find_font(font_name)
@@ -183,20 +176,19 @@ class FontManager:
183
  font_paths[style] = font_path
184
  break
185
 
186
- # If no fonts found, try using fc-match as fallback
187
  if not all(font_paths.values()):
188
  try:
189
  for style in ['regular', 'bold']:
190
  if not font_paths[style]:
191
  result = subprocess.run(
192
- ['fc-match', '-f', '%{file}', 'sans-serif:style=' + style],
193
  capture_output=True,
194
  text=True
195
  )
196
  if result.returncode == 0 and result.stdout.strip():
197
  font_paths[style] = result.stdout.strip()
198
  except Exception as e:
199
- print(f"Warning: Could not use fc-match to find fonts: {e}")
200
 
201
  return font_paths
202
 
@@ -211,30 +203,9 @@ class CertificateGenerator:
211
  self.border_color = '#4682B4'
212
  self.background_color = '#F0F8FF'
213
 
214
- # Install fonts if needed
215
  FontManager.install_fonts()
216
  self.font_paths = FontManager.get_font_paths()
217
 
218
- def _load_fonts(self) -> Dict[str, ImageFont.FreeTypeFont]:
219
- """Load fonts with fallbacks"""
220
- fonts = {}
221
- try:
222
- if self.font_paths['regular'] and self.font_paths['bold']:
223
- fonts['title'] = ImageFont.truetype(self.font_paths['bold'], 60)
224
- fonts['text'] = ImageFont.truetype(self.font_paths['regular'], 40)
225
- fonts['subtitle'] = ImageFont.truetype(self.font_paths['regular'], 30)
226
- else:
227
- raise ValueError("No suitable fonts found")
228
- except Exception as e:
229
- print(f"Font loading error: {e}. Using default font.")
230
- default = ImageFont.load_default()
231
- fonts = {
232
- 'title': default,
233
- 'text': default,
234
- 'subtitle': default
235
- }
236
- return fonts
237
-
238
  def generate(
239
  self,
240
  score: float,
@@ -243,9 +214,6 @@ class CertificateGenerator:
243
  company_logo: Optional[str] = None,
244
  participant_photo: Optional[str] = None
245
  ) -> str:
246
- """
247
- Generate a certificate with custom styling and optional logo/photo
248
- """
249
  try:
250
  certificate = self._create_base_certificate()
251
  draw = ImageDraw.Draw(certificate)
@@ -260,42 +228,42 @@ class CertificateGenerator:
260
  print(f"Error generating certificate: {e}")
261
  return None
262
 
263
- def _create_base_certificate(self) -> Image.Image:
264
- return Image.new('RGB', self.certificate_size, self.background_color)
265
-
266
  def _load_fonts(self) -> Dict[str, ImageFont.FreeTypeFont]:
 
267
  try:
268
- return {
269
- 'title': ImageFont.truetype("arial.ttf", 60),
270
- 'text': ImageFont.truetype("arial.ttf", 40),
271
- 'subtitle': ImageFont.truetype("arial.ttf", 30)
272
- }
 
273
  except Exception as e:
274
  print(f"Font loading error: {e}. Using default font.")
275
  default = ImageFont.load_default()
276
- return {'title': default, 'text': default, 'subtitle': default}
 
 
 
 
 
 
 
 
277
 
278
  def _add_borders(self, draw: ImageDraw.Draw):
279
- # Main borders
280
  draw.rectangle([20, 20, 1180, 780], outline=self.border_color, width=3)
281
  draw.rectangle([40, 40, 1160, 760], outline=self.border_color, width=1)
282
-
283
- # Decorative corners
284
  self._add_decorative_corners(draw)
285
 
286
  def _add_decorative_corners(self, draw: ImageDraw.Draw):
287
  corner_size = 20
288
  corners = [
289
- # Top-left
290
  [(20, 40), (20 + corner_size, 40)],
291
  [(40, 20), (40, 20 + corner_size)],
292
- # Top-right
293
  [(1180 - corner_size, 40), (1180, 40)],
294
  [(1160, 20), (1160, 20 + corner_size)],
295
- # Bottom-left
296
  [(20, 760), (20 + corner_size, 760)],
297
  [(40, 780 - corner_size), (40, 780)],
298
- # Bottom-right
299
  [(1180 - corner_size, 760), (1180, 760)],
300
  [(1160, 780 - corner_size), (1160, 780)]
301
  ]
@@ -311,15 +279,12 @@ class CertificateGenerator:
311
  course_name: str,
312
  score: float
313
  ):
314
- # Title and headers
315
  draw.text((600, 100), "CertifyMe AI", font=fonts['title'], fill=self.border_color, anchor="mm")
316
  draw.text((600, 160), "Certificate of Achievement", font=fonts['subtitle'], fill=self.border_color, anchor="mm")
317
 
318
- # Clean inputs
319
  name = str(name).strip() or "Participant"
320
  course_name = str(course_name).strip() or "Assessment"
321
 
322
- # Main content
323
  content = [
324
  (300, "This is to certify that", 'black'),
325
  (380, name, self.border_color),
@@ -355,7 +320,6 @@ class CertificateGenerator:
355
  temp_file = tempfile.NamedTemporaryFile(delete=False, suffix='.png')
356
  certificate.save(temp_file.name, 'PNG', quality=95)
357
  return temp_file.name
358
-
359
 
360
  class QuizApp:
361
  def __init__(self, api_key: str):
@@ -530,16 +494,15 @@ def create_quiz_interface():
530
  if not os.getenv("GROQ_API_KEY"):
531
  raise EnvironmentError("Please set your GROQ_API_KEY environment variable")
532
 
533
- global quiz_app # Make quiz_app accessible to helper functions
534
  quiz_app = QuizApp(os.getenv("GROQ_API_KEY"))
535
 
536
  with gr.Blocks(title="CertifyMe AI", theme=gr.themes.Soft()) as demo:
537
  # State management
538
  current_questions = gr.State([])
539
  current_question_idx = gr.State(0)
540
- answer_state = gr.State([None] * 5) # Store all answers
541
 
542
- # Header
543
  gr.Markdown("""
544
  # 🎓 CertifyMe AI
545
  ### Transform Your Knowledge into Recognized Achievements
@@ -547,7 +510,7 @@ def create_quiz_interface():
547
 
548
  with gr.Tabs() as tabs:
549
  # Profile Setup Tab
550
- with gr.Tab(id=1,label="📋 Step 1: Profile Setup"):
551
  with gr.Row():
552
  name = gr.Textbox(label="Full Name", placeholder="Enter your full name")
553
  email = gr.Textbox(label="Email", placeholder="Enter your email")
@@ -571,13 +534,12 @@ def create_quiz_interface():
571
  participant_photo = gr.Image(label="Your Photo (Optional)", type="filepath")
572
 
573
  generate_btn = gr.Button("Generate Assessment", variant="primary", size="lg")
574
-
575
  # Assessment Tab
576
- with gr.Tab(id=2,label="📝 Step 2: Take Assessment") as assessment_tab:
577
  with gr.Column(visible=True) as question_box:
578
- # Question section
579
  with gr.Group():
580
- question_display = gr.Markdown("", label="Current Question")
581
  current_options = gr.Radio(
582
  choices=[],
583
  label="Select your answer:",
@@ -619,9 +581,9 @@ def create_quiz_interface():
619
  size="lg",
620
  visible=True
621
  )
622
-
623
  # Certification Tab
624
- with gr.Tab(id=3,label="🎓 Step 3: Get Certified"):
625
  score_display = gr.Number(label="Your Score")
626
  result_message = gr.Markdown("")
627
  course_name = gr.Textbox(
@@ -630,154 +592,7 @@ def create_quiz_interface():
630
  )
631
  certificate_display = gr.Image(label="Your Certificate")
632
 
633
- # Helper Functions
634
-
635
- def on_generate_questions(text: str, num_questions: int) -> List:
636
- success, questions = quiz_app.generate_questions(text, num_questions)
637
- tab_index = 2 # Index for "Take Assessment" tab
638
-
639
- if not success:
640
- return [
641
- "",
642
- gr.update(choices=[], visible=False),
643
- "",
644
- gr.update(visible=False),
645
- [],
646
- 0,
647
- [None] * 5,
648
- gr.Tabs(selected=2),
649
- gr.update(visible=False),
650
- gr.update(visible=False)
651
- ]
652
-
653
- initial_answers = [None] * len(questions)
654
- question = questions[0]
655
-
656
- return [
657
- f"## Question 1\n{question.question}\n\nPlease select one answer:",
658
- gr.update(
659
- choices=question.options,
660
- value=None,
661
- visible=True,
662
- label="Select your answer for Question 1:"
663
- ),
664
- f"Question 1 of {len(questions)}",
665
- gr.update(visible=True),
666
- questions,
667
- 0,
668
- initial_answers,
669
- gr.Tabs(selected=tab_index),
670
- gr.update(visible=False),
671
- gr.update(visible=False)
672
- ]
673
-
674
- def navigate(direction: int, current_idx: int, questions: List, answers: List, current_answer: Optional[str]) -> List:
675
- if not questions:
676
- return [
677
- 0,
678
- answers,
679
- "",
680
- gr.update(choices=[], value=None, visible=False),
681
- "",
682
- gr.update(visible=False)
683
- ]
684
-
685
- new_answers = list(answers)
686
- if current_answer and 0 <= current_idx < len(new_answers):
687
- new_answers[current_idx] = current_answer
688
-
689
- new_idx = max(0, min(len(questions) - 1, current_idx + direction))
690
- question = questions[new_idx]
691
-
692
- return [
693
- new_idx,
694
- new_answers,
695
- f"## Question {new_idx + 1}\n{question.question}\n\nPlease select one answer:",
696
- gr.update(
697
- choices=question.options,
698
- value=new_answers[new_idx] if new_idx < len(new_answers) else None,
699
- visible=True,
700
- label=f"Select your answer for Question {new_idx + 1}:"
701
- ),
702
- f"Question {new_idx + 1} of {len(questions)}",
703
- gr.update(visible=True)
704
- ]
705
-
706
- def handle_prev(current_idx, questions, answers, current_answer):
707
- return navigate(-1, current_idx, questions, answers, current_answer)
708
-
709
- def handle_next(current_idx, questions, answers, current_answer):
710
- return navigate(1, current_idx, questions, answers, current_answer)
711
-
712
- def update_answer_state(answer, idx, current_answers):
713
- new_answers = list(current_answers)
714
- if 0 <= idx < len(new_answers):
715
- new_answers[idx] = answer
716
- return new_answers
717
-
718
- def on_submit(questions, answers, current_idx, current_answer):
719
- final_answers = list(answers)
720
- if 0 <= current_idx < len(final_answers):
721
- final_answers[current_idx] = current_answer
722
-
723
- if not all(a is not None for a in final_answers[:len(questions)]):
724
- return [
725
- "⚠️ Please answer all questions before submitting.",
726
- gr.update(visible=True),
727
- 0,
728
- "",
729
- gr.update(visible=True),
730
- gr.Tabs(selected=1),
731
- gr.update(visible=False)
732
- ]
733
-
734
- score, passed, feedback = quiz_app.calculate_score(final_answers[:len(questions)])
735
-
736
- feedback_html = "# Assessment Results\n\n"
737
- for i, (q, f) in enumerate(zip(questions, feedback)):
738
- color = "green" if f.is_correct else "red"
739
- symbol = "✅" if f.is_correct else "❌"
740
- feedback_html += f"""### Question {i+1}
741
- {q.question}
742
-
743
- <div style="color: {color}; padding: 10px; margin: 5px 0; border-left: 3px solid {color};">
744
- {symbol} Your answer: {f.selected or "No answer"}
745
- {'' if f.is_correct else f'<br>Correct answer: {f.correct_answer}'}
746
- </div>
747
- """
748
-
749
- result_msg = "🎉 Passed!" if passed else "Please try again"
750
- if not passed:
751
- feedback_html += f"""
752
- <div style="background-color: #ffe6e6; padding: 20px; margin-top: 20px; border-radius: 10px;">
753
- <h3 style="color: #cc0000;">Please Try Again</h3>
754
- <p>Your score: {score:.1f}%</p>
755
- <p>You need 80% or higher to pass and receive a certificate.</p>
756
- </div>
757
- """
758
-
759
- return [
760
- feedback_html,
761
- gr.update(visible=True),
762
- score,
763
- result_msg,
764
- gr.update(visible=not passed),
765
- gr.update(selected=1),
766
- gr.update(visible=passed)
767
- ]
768
-
769
- def reset_quiz(text, num_questions):
770
- """Handle quiz reset"""
771
- return on_generate_questions(text, num_questions)
772
-
773
- def view_certificate():
774
- """Navigate to certificate tab"""
775
- return gr.Tabs(selected=3)
776
-
777
- def back_to_assessment():
778
- return gr.Tabs(selected=2) #This will navigate
779
-
780
- # Event handlers
781
  generate_btn.click(
782
  fn=on_generate_questions,
783
  inputs=[text_input, num_questions],
@@ -794,7 +609,7 @@ def create_quiz_interface():
794
  view_cert_btn
795
  ]
796
  ).then(
797
- fn=lambda: gr.update(selected=1),
798
  outputs=tabs
799
  )
800
 
@@ -877,11 +692,14 @@ def create_quiz_interface():
877
  )
878
 
879
  view_cert_btn.click(
880
- fn=view_certificate,
881
- outputs=[tabs]
882
  )
883
 
884
-
 
 
 
885
 
886
  score_display.change(
887
  fn=lambda s, n, c, l, p: quiz_app.certificate_generator.generate(s, n, c, l, p) or gr.update(value=None),
@@ -891,6 +709,11 @@ def create_quiz_interface():
891
 
892
  return demo
893
 
 
 
 
 
 
894
  if __name__ == "__main__":
895
  demo = create_quiz_interface()
896
  demo.launch()
 
21
  selected: Optional[str]
22
  correct_answer: str
23
 
24
+ class QuizGenerationError(Exception):
25
+ """Exception raised for errors in quiz generation"""
26
+ pass
27
+
28
  class QuizGenerator:
29
  def __init__(self, api_key: str):
30
  self.client = Groq(api_key=api_key)
 
117
  )
118
 
119
  class FontManager:
 
 
120
  @staticmethod
121
  def install_fonts():
 
122
  try:
123
+ subprocess.run(["apt-get", "update", "-y"], check=True)
124
+ subprocess.run(
125
+ [
126
+ "apt-get", "install", "-y",
127
+ "fonts-liberation",
128
+ "fontconfig",
129
+ "fonts-dejavu-core"
130
+ ],
131
+ check=True
132
+ )
 
 
133
  subprocess.run(["fc-cache", "-f"], check=True)
134
  print("Fonts installed successfully")
 
 
135
  except Exception as e:
136
+ print(f"Warning: Font installation error: {e}")
137
 
138
  @staticmethod
139
  def get_font_paths() -> Dict[str, str]:
 
140
  standard_paths = [
141
  "/usr/share/fonts",
142
  "/usr/local/share/fonts",
 
149
  'bold': None
150
  }
151
 
 
152
  fonts_to_try = {
153
  'regular': [
154
  'LiberationSans-Regular.ttf',
 
163
  }
164
 
165
  def find_font(font_name: str) -> Optional[str]:
 
166
  for base_path in standard_paths:
167
  for root, _, files in os.walk(os.path.expanduser(base_path)):
168
  if font_name in files:
169
  return os.path.join(root, font_name)
170
  return None
171
 
 
172
  for style in ['regular', 'bold']:
173
  for font_name in fonts_to_try[style]:
174
  font_path = find_font(font_name)
 
176
  font_paths[style] = font_path
177
  break
178
 
 
179
  if not all(font_paths.values()):
180
  try:
181
  for style in ['regular', 'bold']:
182
  if not font_paths[style]:
183
  result = subprocess.run(
184
+ ['fc-match', '-f', '%{file}', f'sans-serif:style={style}'],
185
  capture_output=True,
186
  text=True
187
  )
188
  if result.returncode == 0 and result.stdout.strip():
189
  font_paths[style] = result.stdout.strip()
190
  except Exception as e:
191
+ print(f"Warning: Font matching error: {e}")
192
 
193
  return font_paths
194
 
 
203
  self.border_color = '#4682B4'
204
  self.background_color = '#F0F8FF'
205
 
 
206
  FontManager.install_fonts()
207
  self.font_paths = FontManager.get_font_paths()
208
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
209
  def generate(
210
  self,
211
  score: float,
 
214
  company_logo: Optional[str] = None,
215
  participant_photo: Optional[str] = None
216
  ) -> str:
 
 
 
217
  try:
218
  certificate = self._create_base_certificate()
219
  draw = ImageDraw.Draw(certificate)
 
228
  print(f"Error generating certificate: {e}")
229
  return None
230
 
 
 
 
231
  def _load_fonts(self) -> Dict[str, ImageFont.FreeTypeFont]:
232
+ fonts = {}
233
  try:
234
+ if self.font_paths['regular'] and self.font_paths['bold']:
235
+ fonts['title'] = ImageFont.truetype(self.font_paths['bold'], 60)
236
+ fonts['text'] = ImageFont.truetype(self.font_paths['regular'], 40)
237
+ fonts['subtitle'] = ImageFont.truetype(self.font_paths['regular'], 30)
238
+ else:
239
+ raise ValueError("No suitable fonts found")
240
  except Exception as e:
241
  print(f"Font loading error: {e}. Using default font.")
242
  default = ImageFont.load_default()
243
+ fonts = {
244
+ 'title': default,
245
+ 'text': default,
246
+ 'subtitle': default
247
+ }
248
+ return fonts
249
+
250
+ def _create_base_certificate(self) -> Image.Image:
251
+ return Image.new('RGB', self.certificate_size, self.background_color)
252
 
253
  def _add_borders(self, draw: ImageDraw.Draw):
 
254
  draw.rectangle([20, 20, 1180, 780], outline=self.border_color, width=3)
255
  draw.rectangle([40, 40, 1160, 760], outline=self.border_color, width=1)
 
 
256
  self._add_decorative_corners(draw)
257
 
258
  def _add_decorative_corners(self, draw: ImageDraw.Draw):
259
  corner_size = 20
260
  corners = [
 
261
  [(20, 40), (20 + corner_size, 40)],
262
  [(40, 20), (40, 20 + corner_size)],
 
263
  [(1180 - corner_size, 40), (1180, 40)],
264
  [(1160, 20), (1160, 20 + corner_size)],
 
265
  [(20, 760), (20 + corner_size, 760)],
266
  [(40, 780 - corner_size), (40, 780)],
 
267
  [(1180 - corner_size, 760), (1180, 760)],
268
  [(1160, 780 - corner_size), (1160, 780)]
269
  ]
 
279
  course_name: str,
280
  score: float
281
  ):
 
282
  draw.text((600, 100), "CertifyMe AI", font=fonts['title'], fill=self.border_color, anchor="mm")
283
  draw.text((600, 160), "Certificate of Achievement", font=fonts['subtitle'], fill=self.border_color, anchor="mm")
284
 
 
285
  name = str(name).strip() or "Participant"
286
  course_name = str(course_name).strip() or "Assessment"
287
 
 
288
  content = [
289
  (300, "This is to certify that", 'black'),
290
  (380, name, self.border_color),
 
320
  temp_file = tempfile.NamedTemporaryFile(delete=False, suffix='.png')
321
  certificate.save(temp_file.name, 'PNG', quality=95)
322
  return temp_file.name
 
323
 
324
  class QuizApp:
325
  def __init__(self, api_key: str):
 
494
  if not os.getenv("GROQ_API_KEY"):
495
  raise EnvironmentError("Please set your GROQ_API_KEY environment variable")
496
 
497
+ global quiz_app
498
  quiz_app = QuizApp(os.getenv("GROQ_API_KEY"))
499
 
500
  with gr.Blocks(title="CertifyMe AI", theme=gr.themes.Soft()) as demo:
501
  # State management
502
  current_questions = gr.State([])
503
  current_question_idx = gr.State(0)
504
+ answer_state = gr.State([None] * 5)
505
 
 
506
  gr.Markdown("""
507
  # 🎓 CertifyMe AI
508
  ### Transform Your Knowledge into Recognized Achievements
 
510
 
511
  with gr.Tabs() as tabs:
512
  # Profile Setup Tab
513
+ with gr.Tab(label="📋 Step 1: Profile Setup", id=1) as setup_tab:
514
  with gr.Row():
515
  name = gr.Textbox(label="Full Name", placeholder="Enter your full name")
516
  email = gr.Textbox(label="Email", placeholder="Enter your email")
 
534
  participant_photo = gr.Image(label="Your Photo (Optional)", type="filepath")
535
 
536
  generate_btn = gr.Button("Generate Assessment", variant="primary", size="lg")
537
+
538
  # Assessment Tab
539
+ with gr.Tab(label="📝 Step 2: Take Assessment", id=2) as assessment_tab:
540
  with gr.Column(visible=True) as question_box:
 
541
  with gr.Group():
542
+ question_display = gr.Markdown("")
543
  current_options = gr.Radio(
544
  choices=[],
545
  label="Select your answer:",
 
581
  size="lg",
582
  visible=True
583
  )
584
+
585
  # Certification Tab
586
+ with gr.Tab(label="🎓 Step 3: Get Certified", id=3) as cert_tab:
587
  score_display = gr.Number(label="Your Score")
588
  result_message = gr.Markdown("")
589
  course_name = gr.Textbox(
 
592
  )
593
  certificate_display = gr.Image(label="Your Certificate")
594
 
595
+ # Event handlers
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
596
  generate_btn.click(
597
  fn=on_generate_questions,
598
  inputs=[text_input, num_questions],
 
609
  view_cert_btn
610
  ]
611
  ).then(
612
+ fn=goto_take_assessment,
613
  outputs=tabs
614
  )
615
 
 
692
  )
693
 
694
  view_cert_btn.click(
695
+ fn=goto_certificate,
696
+ outputs=tabs
697
  )
698
 
699
+ back_to_assessment.click(
700
+ fn=goto_take_assessment,
701
+ outputs=tabs
702
+ )
703
 
704
  score_display.change(
705
  fn=lambda s, n, c, l, p: quiz_app.certificate_generator.generate(s, n, c, l, p) or gr.update(value=None),
 
709
 
710
  return demo
711
 
712
+ if __name__ == "__main__":
713
+ demo = create_quiz_interface()
714
+ demo.launch()
715
+ return demo
716
+
717
  if __name__ == "__main__":
718
  demo = create_quiz_interface()
719
  demo.launch()