capradeepgujaran commited on
Commit
058bca9
·
verified ·
1 Parent(s): 56b1a6d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +247 -396
app.py CHANGED
@@ -5,18 +5,53 @@ from PIL import Image, ImageDraw, ImageFont
5
  from datetime import datetime
6
  import json
7
  import tempfile
 
 
8
 
9
- # Initialize Groq client
10
- client = Groq(
11
- api_key=os.getenv("GROQ_API_KEY")
12
- )
 
13
 
14
- class QuizApp:
15
- def __init__(self):
16
- self.current_questions = []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
 
18
- def generate_questions(self, text, num_questions):
19
- prompt = f"""Create exactly {num_questions} multiple choice questions based on this text:
20
  {text}
21
 
22
  For each question:
@@ -40,129 +75,231 @@ class QuizApp:
40
  ]
41
  Keep all options concise (10 words or less each).
42
  """
 
 
 
 
 
43
 
44
- try:
45
- response = client.chat.completions.create(
46
- messages=[
47
- {
48
- "role": "system",
49
- "content": "You are a quiz generator. Create clear questions with concise answer options."
50
- },
51
- {
52
- "role": "user",
53
- "content": prompt
54
- }
55
- ],
56
- model="llama-3.2-3b-preview",
57
- temperature=0.3,
58
- max_tokens=2048
59
- )
60
-
61
- response_text = response.choices[0].message.content.strip()
62
- response_text = response_text.replace("```json", "").replace("```", "").strip()
63
-
64
- start_idx = response_text.find("[")
65
- end_idx = response_text.rfind("]")
66
 
67
- if start_idx == -1 or end_idx == -1:
68
- raise ValueError("No valid JSON array found in response")
69
-
70
- response_text = response_text[start_idx:end_idx + 1]
71
- questions = json.loads(response_text)
72
-
73
- validated_questions = []
74
- for q in questions:
75
- if not all(key in q for key in ["question", "options", "correct_answer"]):
76
- continue
77
- clean_options = [opt.strip()[:100] for opt in q["options"] if isinstance(opt, str)]
78
- if len(clean_options) != 4:
79
- continue
80
- clean_q = {
81
- "question": q["question"].strip(),
82
- "options": clean_options,
83
- "correct_answer": int(q["correct_answer"]) % 4
84
- }
85
- validated_questions.append(clean_q)
86
 
87
- if not validated_questions:
88
- raise ValueError("No valid questions after validation")
89
-
90
- self.current_questions = validated_questions[:num_questions]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91
  return True, self.current_questions
92
-
93
  except Exception as e:
94
- print(f"Error in question generation: {str(e)}")
95
  return False, []
96
-
97
- def calculate_score(self, answers):
98
  if not answers or not self.current_questions:
99
- return 0, None, []
100
 
101
- total = len(self.current_questions)
102
- correct = 0
103
  feedback = []
 
104
 
105
- for i, (q, a) in enumerate(zip(self.current_questions, answers)):
 
 
 
 
106
  try:
107
- if a is not None:
108
- selected_index = q["options"].index(a)
109
- is_correct = selected_index == q["correct_answer"]
110
- if is_correct:
111
- correct += 1
112
- feedback.append({
113
- "is_correct": is_correct,
114
- "selected": a,
115
- "correct_answer": q["options"][q["correct_answer"]]
116
- })
117
- else:
118
- feedback.append({
119
- "is_correct": False,
120
- "selected": None,
121
- "correct_answer": q["options"][q["correct_answer"]]
122
- })
123
- except (ValueError, TypeError) as e:
124
- print(f"Error processing answer {i}: {e}")
125
- feedback.append({
126
- "is_correct": False,
127
- "selected": None,
128
- "correct_answer": q["options"][q["correct_answer"]]
129
- })
130
-
131
- score = (correct / total) * 100
132
- passed = score >= 80
133
 
134
- return score, passed, feedback
135
-
136
 
137
- # Update the interface functions
138
  def create_quiz_interface():
139
- quiz_app = QuizApp()
 
 
 
140
 
141
  with gr.Blocks(title="CertifyMe AI", theme=gr.themes.Soft()) as demo:
 
142
  current_questions = gr.State([])
143
 
144
- # Header
145
  gr.Markdown("""
146
  # 🎓 CertifyMe AI
147
  ### Transform Your Knowledge into Recognized Achievements
148
  """)
149
 
150
- # Tabs
151
  with gr.Tabs() as tabs:
152
- # Step 1: Profile Setup
153
- with gr.Tab("📋 Step 1: Profile Setup") as tab1:
154
  with gr.Row():
155
  name = gr.Textbox(label="Full Name", placeholder="Enter your full name")
156
  email = gr.Textbox(label="Email", placeholder="Enter your email")
157
 
158
  text_input = gr.Textbox(
159
- label="Learning Content",
160
  placeholder="Enter the text content you want to be assessed on",
161
  lines=10
162
  )
163
 
164
  num_questions = gr.Slider(
165
- minimum=1,
166
  maximum=5,
167
  value=3,
168
  step=1,
@@ -175,25 +312,18 @@ def create_quiz_interface():
175
 
176
  generate_btn = gr.Button("Generate Assessment", variant="primary", size="lg")
177
 
178
- # Step 2: Take Assessment
179
- with gr.Tab("📝 Step 2: Take Assessment") as tab2:
180
  feedback_box = gr.Markdown("")
181
  question_box = gr.Markdown("")
182
- answers = []
183
- for i in range(5):
184
- with gr.Group():
185
- radio = gr.Radio(
186
- choices=[],
187
- label=f"Question {i+1}",
188
- visible=False,
189
- interactive=True
190
- )
191
- answers.append(radio)
192
-
193
  submit_btn = gr.Button("Submit Assessment", variant="primary", size="lg")
194
 
195
- # Step 3: Get Certified
196
- with gr.Tab("🎓 Step 3: Get Certified") as tab3:
197
  score_display = gr.Number(label="Your Score")
198
  result_message = gr.Markdown("")
199
  course_name = gr.Textbox(
@@ -201,115 +331,10 @@ def create_quiz_interface():
201
  value="Professional Assessment Certification"
202
  )
203
  certificate_display = gr.Image(label="Your Certificate")
204
-
205
- def update_questions(text, num_questions):
206
- if not text.strip():
207
- return (
208
- gr.update(value=""),
209
- gr.update(value="⚠️ Please enter some text content to generate questions."),
210
- *[gr.update(visible=False, choices=[]) for _ in range(5)],
211
- [],
212
- gr.update(selected=1)
213
- )
214
-
215
- success, questions = quiz_app.generate_questions(text, num_questions)
216
-
217
- if not success or not questions:
218
- return (
219
- gr.update(value=""),
220
- gr.update(value="❌ Failed to generate questions. Please try again."),
221
- *[gr.update(visible=False, choices=[]) for _ in range(5)],
222
- [],
223
- gr.update(selected=1)
224
- )
225
-
226
- # Create question display
227
- questions_html = "# 📝 Assessment Questions\n\n"
228
- questions_html += "> Please select one answer for each question.\n\n"
229
-
230
- # Update radio buttons
231
- updates = []
232
- for i, q in enumerate(questions):
233
- questions_html += f"### Question {i+1}\n{q['question']}\n\n"
234
- updates.append(gr.update(
235
- visible=True,
236
- choices=q["options"],
237
- value=None,
238
- label=f"Select your answer:"
239
- ))
240
-
241
- # Hide unused radio buttons
242
- for i in range(len(questions), 5):
243
- updates.append(gr.update(visible=False, choices=[]))
244
-
245
- return (
246
- gr.update(value=questions_html),
247
- gr.update(value=""),
248
- *updates,
249
- questions,
250
- gr.update(selected=1)
251
- )
252
-
253
- def submit_quiz(q1, q2, q3, q4, q5, questions):
254
- answers = [q1, q2, q3, q4, q5][:len(questions)]
255
-
256
- if not all(a is not None for a in answers):
257
- return (
258
- gr.update(value="⚠️ Please answer all questions before submitting."),
259
- *[gr.update() for _ in range(5)],
260
- 0,
261
- "",
262
- gr.update(selected=1)
263
- )
264
-
265
- score, passed, feedback = quiz_app.calculate_score(answers)
266
-
267
- # Create feedback HTML
268
- feedback_html = "# Assessment Results\n\n"
269
- for i, (q, f) in enumerate(zip(quiz_app.current_questions, feedback)):
270
- color = "green" if f["is_correct"] else "red"
271
- symbol = "✅" if f["is_correct"] else "❌"
272
- feedback_html += f"""
273
- ### Question {i+1}
274
- {q["question"]}
275
-
276
- <div style="color: {color}; padding: 10px; margin: 5px 0; border-left: 3px solid {color};">
277
- {symbol} Your answer: {f["selected"]}
278
- {'' if f["is_correct"] else f'<br>Correct answer: {f["correct_answer"]}'}
279
- </div>
280
- """
281
-
282
- # Add result message
283
- if passed:
284
- feedback_html += f"""
285
- <div style="background-color: #e6ffe6; padding: 20px; margin-top: 20px; border-radius: 10px;">
286
- <h3 style="color: #008000;">🎉 Congratulations!</h3>
287
- <p>You passed the assessment with a score of {score:.1f}%</p>
288
- <p>Your certificate has been generated.</p>
289
- </div>
290
- """
291
- result_msg = f"🎉 Congratulations! You passed with {score:.1f}%"
292
- else:
293
- feedback_html += f"""
294
- <div style="background-color: #ffe6e6; padding: 20px; margin-top: 20px; border-radius: 10px;">
295
- <h3 style="color: #cc0000;">Please Try Again</h3>
296
- <p>Your score: {score:.1f}%</p>
297
- <p>You need 80% or higher to pass and receive a certificate.</p>
298
- </div>
299
- """
300
- result_msg = f"Score: {score:.1f}%. You need 80% to pass."
301
-
302
- return (
303
- gr.update(value=feedback_html),
304
- *[gr.update(visible=False) for _ in range(5)],
305
- score,
306
- result_msg,
307
- gr.update(selected=2)
308
- )
309
 
310
  # Event handlers
311
  generate_btn.click(
312
- fn=update_questions,
313
  inputs=[text_input, num_questions],
314
  outputs=[
315
  question_box,
@@ -321,7 +346,7 @@ def create_quiz_interface():
321
  )
322
 
323
  submit_btn.click(
324
- fn=submit_quiz,
325
  inputs=[*answers, current_questions],
326
  outputs=[
327
  feedback_box,
@@ -332,188 +357,14 @@ def create_quiz_interface():
332
  ]
333
  )
334
 
335
-
336
- def generate_certificate(score, name, course_name, company_logo=None, participant_photo=None):
337
- """
338
- Generate a certificate with custom styling and optional logo/photo
339
-
340
- Args:
341
- score (float): Assessment score (0-100)
342
- name (str): Participant's name
343
- course_name (str): Name of the course/assessment
344
- company_logo (str, optional): Path to company logo image
345
- participant_photo (str, optional): Path to participant's photo
346
-
347
- Returns:
348
- str: Path to generated certificate image file
349
- """
350
- try:
351
- # Create certificate with a light blue background
352
- certificate = Image.new('RGB', (1200, 800), '#F0F8FF')
353
- draw = ImageDraw.Draw(certificate)
354
-
355
- # Try to load fonts, fallback to default if necessary
356
- try:
357
- title_font = ImageFont.truetype("arial.ttf", 60)
358
- text_font = ImageFont.truetype("arial.ttf", 40)
359
- subtitle_font = ImageFont.truetype("arial.ttf", 30)
360
- except Exception as e:
361
- print(f"Font loading error: {e}. Using default font.")
362
- title_font = ImageFont.load_default()
363
- text_font = ImageFont.load_default()
364
- subtitle_font = ImageFont.load_default()
365
-
366
- # Add decorative border
367
- border_color = '#4682B4' # Steel blue
368
- draw.rectangle([20, 20, 1180, 780], outline=border_color, width=3)
369
-
370
- # Add inner border
371
- draw.rectangle([40, 40, 1160, 760], outline=border_color, width=1)
372
-
373
- # Add certificate content
374
- # Title
375
- draw.text(
376
- (600, 100),
377
- "CertifyMe AI",
378
- font=title_font,
379
- fill='#4682B4',
380
- anchor="mm"
381
- )
382
-
383
- # Subtitle
384
- draw.text(
385
- (600, 160),
386
- "Certificate of Achievement",
387
- font=subtitle_font,
388
- fill='#4682B4',
389
- anchor="mm"
390
- )
391
-
392
- # Main content
393
- draw.text(
394
- (600, 300),
395
- "This is to certify that",
396
- font=text_font,
397
- fill='black',
398
- anchor="mm"
399
- )
400
-
401
- # Participant name
402
- name = name.strip() if name else "Participant"
403
- draw.text(
404
- (600, 380),
405
- name,
406
- font=text_font,
407
- fill='#4682B4',
408
- anchor="mm"
409
- )
410
-
411
- # Completion text
412
- draw.text(
413
- (600, 460),
414
- "has successfully completed",
415
- font=text_font,
416
- fill='black',
417
- anchor="mm"
418
- )
419
-
420
- # Course name
421
- course_name = course_name.strip() if course_name else "Assessment"
422
- draw.text(
423
- (600, 540),
424
- course_name,
425
- font=text_font,
426
- fill='#4682B4',
427
- anchor="mm"
428
- )
429
-
430
- # Score
431
- draw.text(
432
- (600, 620),
433
- f"with a score of {score:.1f}%",
434
- font=text_font,
435
- fill='black',
436
- anchor="mm"
437
- )
438
-
439
- # Date
440
- current_date = datetime.now().strftime("%B %d, %Y")
441
- draw.text(
442
- (600, 700),
443
- current_date,
444
- font=text_font,
445
- fill='black',
446
- anchor="mm"
447
- )
448
-
449
- # Add logo if provided
450
- if company_logo is not None:
451
- try:
452
- logo = Image.open(company_logo)
453
- # Maintain aspect ratio
454
- logo.thumbnail((150, 150))
455
- # Calculate position to place logo
456
- logo_x = 50
457
- logo_y = 50
458
- certificate.paste(logo, (logo_x, logo_y))
459
- except Exception as e:
460
- print(f"Error adding logo: {e}")
461
-
462
- # Add photo if provided
463
- if participant_photo is not None:
464
- try:
465
- photo = Image.open(participant_photo)
466
- # Maintain aspect ratio
467
- photo.thumbnail((150, 150))
468
- # Calculate position to place photo
469
- photo_x = 1000
470
- photo_y = 50
471
- certificate.paste(photo, (photo_x, photo_y))
472
- except Exception as e:
473
- print(f"Error adding photo: {e}")
474
-
475
- # Add decorative corners
476
- corner_size = 20
477
- corner_color = '#4682B4'
478
-
479
- # Top-left corner
480
- draw.line([(20, 40), (20 + corner_size, 40)], fill=corner_color, width=2)
481
- draw.line([(40, 20), (40, 20 + corner_size)], fill=corner_color, width=2)
482
-
483
- # Top-right corner
484
- draw.line([(1180 - corner_size, 40), (1180, 40)], fill=corner_color, width=2)
485
- draw.line([(1160, 20), (1160, 20 + corner_size)], fill=corner_color, width=2)
486
-
487
- # Bottom-left corner
488
- draw.line([(20, 760), (20 + corner_size, 760)], fill=corner_color, width=2)
489
- draw.line([(40, 780 - corner_size), (40, 780)], fill=corner_color, width=2)
490
-
491
- # Bottom-right corner
492
- draw.line([(1180 - corner_size, 760), (1180, 760)], fill=corner_color, width=2)
493
- draw.line([(1160, 780 - corner_size), (1160, 780)], fill=corner_color, width=2)
494
-
495
- # Save certificate
496
- temp_file = tempfile.NamedTemporaryFile(delete=False, suffix='.png')
497
- certificate.save(temp_file.name, 'PNG', quality=95)
498
- return temp_file.name
499
-
500
- except Exception as e:
501
- print(f"Error generating certificate: {e}")
502
- return None
503
-
504
- # Certificate generation event handler
505
-
506
  score_display.change(
507
- fn=generate_certificate,
508
  inputs=[score_display, name, course_name, company_logo, participant_photo],
509
  outputs=certificate_display
510
  )
 
511
  return demo
512
 
513
  if __name__ == "__main__":
514
- if not os.getenv("GROQ_API_KEY"):
515
- print("Please set your GROQ_API_KEY environment variable")
516
- exit(1)
517
-
518
  demo = create_quiz_interface()
519
  demo.launch()
 
5
  from datetime import datetime
6
  import json
7
  import tempfile
8
+ from typing import List, Dict, Tuple, Optional
9
+ from dataclasses import dataclass
10
 
11
+ @dataclass
12
+ class Question:
13
+ question: str
14
+ options: List[str]
15
+ correct_answer: int
16
 
17
+ @dataclass
18
+ class QuizFeedback:
19
+ is_correct: bool
20
+ selected: Optional[str]
21
+ correct_answer: str
22
+
23
+ class QuizGenerator:
24
+ def __init__(self, api_key: str):
25
+ self.client = Groq(api_key=api_key)
26
+
27
+ def generate_questions(self, text: str, num_questions: int) -> List[Question]:
28
+ prompt = self._create_prompt(text, num_questions)
29
+
30
+ try:
31
+ response = self.client.chat.completions.create(
32
+ messages=[
33
+ {
34
+ "role": "system",
35
+ "content": "You are a quiz generator. Create clear questions with concise answer options."
36
+ },
37
+ {
38
+ "role": "user",
39
+ "content": prompt
40
+ }
41
+ ],
42
+ model="llama-3.2-3b-preview",
43
+ temperature=0.3,
44
+ max_tokens=2048
45
+ )
46
+
47
+ questions = self._parse_response(response.choices[0].message.content)
48
+ return self._validate_questions(questions, num_questions)
49
+
50
+ except Exception as e:
51
+ raise QuizGenerationError(f"Failed to generate questions: {str(e)}")
52
 
53
+ def _create_prompt(self, text: str, num_questions: int) -> str:
54
+ return f"""Create exactly {num_questions} multiple choice questions based on this text:
55
  {text}
56
 
57
  For each question:
 
75
  ]
76
  Keep all options concise (10 words or less each).
77
  """
78
+
79
+ def _parse_response(self, response_text: str) -> List[Dict]:
80
+ response_text = response_text.replace("```json", "").replace("```", "").strip()
81
+ start_idx = response_text.find("[")
82
+ end_idx = response_text.rfind("]")
83
 
84
+ if start_idx == -1 or end_idx == -1:
85
+ raise ValueError("No valid JSON array found in response")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
86
 
87
+ response_text = response_text[start_idx:end_idx + 1]
88
+ return json.loads(response_text)
89
+
90
+ def _validate_questions(self, questions: List[Dict], num_questions: int) -> List[Question]:
91
+ validated = []
92
+ for q in questions:
93
+ if not self._is_valid_question(q):
94
+ continue
95
+ validated.append(Question(
96
+ question=q["question"].strip(),
97
+ options=[opt.strip()[:100] for opt in q["options"]],
98
+ correct_answer=int(q["correct_answer"]) % 4
99
+ ))
100
+
101
+ if not validated:
102
+ raise ValueError("No valid questions after validation")
 
 
 
103
 
104
+ return validated[:num_questions]
105
+
106
+ def _is_valid_question(self, question: Dict) -> bool:
107
+ return (
108
+ all(key in question for key in ["question", "options", "correct_answer"]) and
109
+ isinstance(question["options"], list) and
110
+ len(question["options"]) == 4 and
111
+ all(isinstance(opt, str) for opt in question["options"])
112
+ )
113
+
114
+ class CertificateGenerator:
115
+ def __init__(self):
116
+ self.certificate_size = (1200, 800)
117
+ self.border_color = '#4682B4'
118
+ self.background_color = '#F0F8FF'
119
+
120
+ def generate(
121
+ self,
122
+ name: str,
123
+ score: float,
124
+ course_name: str,
125
+ company_logo: Optional[str] = None,
126
+ participant_photo: Optional[str] = None
127
+ ) -> str:
128
+ certificate = self._create_base_certificate()
129
+ draw = ImageDraw.Draw(certificate)
130
+
131
+ fonts = self._load_fonts()
132
+ self._add_borders(draw)
133
+ self._add_content(draw, fonts, name, score, course_name)
134
+ self._add_images(certificate, company_logo, participant_photo)
135
+
136
+ return self._save_certificate(certificate)
137
+
138
+ def _create_base_certificate(self) -> Image.Image:
139
+ return Image.new('RGB', self.certificate_size, self.background_color)
140
+
141
+ def _load_fonts(self) -> Dict[str, ImageFont.FreeTypeFont]:
142
+ try:
143
+ return {
144
+ 'title': ImageFont.truetype("arial.ttf", 60),
145
+ 'text': ImageFont.truetype("arial.ttf", 40),
146
+ 'subtitle': ImageFont.truetype("arial.ttf", 30)
147
+ }
148
+ except Exception as e:
149
+ print(f"Font loading error: {e}. Using default font.")
150
+ default = ImageFont.load_default()
151
+ return {'title': default, 'text': default, 'subtitle': default}
152
+
153
+ def _add_borders(self, draw: ImageDraw.Draw):
154
+ # Main borders
155
+ draw.rectangle([20, 20, 1180, 780], outline=self.border_color, width=3)
156
+ draw.rectangle([40, 40, 1160, 760], outline=self.border_color, width=1)
157
+
158
+ # Decorative corners
159
+ self._add_decorative_corners(draw)
160
+
161
+ def _add_decorative_corners(self, draw: ImageDraw.Draw):
162
+ corner_size = 20
163
+ corners = [
164
+ # Top-left
165
+ [(20, 40), (20 + corner_size, 40)],
166
+ [(40, 20), (40, 20 + corner_size)],
167
+ # Top-right
168
+ [(1180 - corner_size, 40), (1180, 40)],
169
+ [(1160, 20), (1160, 20 + corner_size)],
170
+ # Bottom-left
171
+ [(20, 760), (20 + corner_size, 760)],
172
+ [(40, 780 - corner_size), (40, 780)],
173
+ # Bottom-right
174
+ [(1180 - corner_size, 760), (1180, 760)],
175
+ [(1160, 780 - corner_size), (1160, 780)]
176
+ ]
177
+
178
+ for corner in corners:
179
+ draw.line(corner, fill=self.border_color, width=2)
180
+
181
+ def _add_content(
182
+ self,
183
+ draw: ImageDraw.Draw,
184
+ fonts: Dict[str, ImageFont.FreeTypeFont],
185
+ name: str,
186
+ score: float,
187
+ course_name: str
188
+ ):
189
+ # Title and headers
190
+ draw.text((600, 100), "CertifyMe AI", font=fonts['title'], fill=self.border_color, anchor="mm")
191
+ draw.text((600, 160), "Certificate of Achievement", font=fonts['subtitle'], fill=self.border_color, anchor="mm")
192
+
193
+ # Main content
194
+ content = [
195
+ (300, "This is to certify that", 'black'),
196
+ (380, name.strip() or "Participant", self.border_color),
197
+ (460, "has successfully completed", 'black'),
198
+ (540, course_name.strip() or "Assessment", self.border_color),
199
+ (620, f"with a score of {score:.1f}%", 'black'),
200
+ (700, datetime.now().strftime("%B %d, %Y"), 'black')
201
+ ]
202
+
203
+ for y, text, color in content:
204
+ draw.text((600, y), text, font=fonts['text'], fill=color, anchor="mm")
205
+
206
+ def _add_images(
207
+ self,
208
+ certificate: Image.Image,
209
+ company_logo: Optional[str],
210
+ participant_photo: Optional[str]
211
+ ):
212
+ if company_logo:
213
+ self._add_image(certificate, company_logo, (50, 50))
214
+ if participant_photo:
215
+ self._add_image(certificate, participant_photo, (1000, 50))
216
+
217
+ def _add_image(self, certificate: Image.Image, image_path: str, position: Tuple[int, int]):
218
+ try:
219
+ img = Image.open(image_path)
220
+ img.thumbnail((150, 150))
221
+ certificate.paste(img, position)
222
+ except Exception as e:
223
+ print(f"Error adding image: {e}")
224
+
225
+ def _save_certificate(self, certificate: Image.Image) -> str:
226
+ temp_file = tempfile.NamedTemporaryFile(delete=False, suffix='.png')
227
+ certificate.save(temp_file.name, 'PNG', quality=95)
228
+ return temp_file.name
229
+
230
+ class QuizApp:
231
+ def __init__(self, api_key: str):
232
+ self.quiz_generator = QuizGenerator(api_key)
233
+ self.certificate_generator = CertificateGenerator()
234
+ self.current_questions: List[Question] = []
235
+
236
+ def generate_questions(self, text: str, num_questions: int) -> Tuple[bool, List[Question]]:
237
+ try:
238
+ self.current_questions = self.quiz_generator.generate_questions(text, num_questions)
239
  return True, self.current_questions
 
240
  except Exception as e:
241
+ print(f"Error generating questions: {e}")
242
  return False, []
243
+
244
+ def calculate_score(self, answers: List[Optional[str]]) -> Tuple[float, bool, List[QuizFeedback]]:
245
  if not answers or not self.current_questions:
246
+ return 0, False, []
247
 
 
 
248
  feedback = []
249
+ correct = 0
250
 
251
+ for question, answer in zip(self.current_questions, answers):
252
+ if answer is None:
253
+ feedback.append(QuizFeedback(False, None, question.options[question.correct_answer]))
254
+ continue
255
+
256
  try:
257
+ selected_index = question.options.index(answer)
258
+ is_correct = selected_index == question.correct_answer
259
+ if is_correct:
260
+ correct += 1
261
+ feedback.append(QuizFeedback(
262
+ is_correct,
263
+ answer,
264
+ question.options[question.correct_answer]
265
+ ))
266
+ except ValueError:
267
+ feedback.append(QuizFeedback(False, answer, question.options[question.correct_answer]))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
268
 
269
+ score = (correct / len(self.current_questions)) * 100
270
+ return score, score >= 80, feedback
271
 
 
272
  def create_quiz_interface():
273
+ if not os.getenv("GROQ_API_KEY"):
274
+ raise EnvironmentError("Please set your GROQ_API_KEY environment variable")
275
+
276
+ quiz_app = QuizApp(os.getenv("GROQ_API_KEY"))
277
 
278
  with gr.Blocks(title="CertifyMe AI", theme=gr.themes.Soft()) as demo:
279
+ # State management
280
  current_questions = gr.State([])
281
 
282
+ # UI Components layout
283
  gr.Markdown("""
284
  # 🎓 CertifyMe AI
285
  ### Transform Your Knowledge into Recognized Achievements
286
  """)
287
 
 
288
  with gr.Tabs() as tabs:
289
+ # Profile Setup Tab
290
+ with gr.Tab("📋 Step 1: Profile Setup"):
291
  with gr.Row():
292
  name = gr.Textbox(label="Full Name", placeholder="Enter your full name")
293
  email = gr.Textbox(label="Email", placeholder="Enter your email")
294
 
295
  text_input = gr.Textbox(
296
+ label="Learning Content",
297
  placeholder="Enter the text content you want to be assessed on",
298
  lines=10
299
  )
300
 
301
  num_questions = gr.Slider(
302
+ minimum=1,
303
  maximum=5,
304
  value=3,
305
  step=1,
 
312
 
313
  generate_btn = gr.Button("Generate Assessment", variant="primary", size="lg")
314
 
315
+ # Assessment Tab
316
+ with gr.Tab("📝 Step 2: Take Assessment"):
317
  feedback_box = gr.Markdown("")
318
  question_box = gr.Markdown("")
319
+ answers = [
320
+ gr.Radio(choices=[], label=f"Question {i+1}", visible=False)
321
+ for i in range(5)
322
+ ]
 
 
 
 
 
 
 
323
  submit_btn = gr.Button("Submit Assessment", variant="primary", size="lg")
324
 
325
+ # Certification Tab
326
+ with gr.Tab("🎓 Step 3: Get Certified"):
327
  score_display = gr.Number(label="Your Score")
328
  result_message = gr.Markdown("")
329
  course_name = gr.Textbox(
 
331
  value="Professional Assessment Certification"
332
  )
333
  certificate_display = gr.Image(label="Your Certificate")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
334
 
335
  # Event handlers
336
  generate_btn.click(
337
+ fn=quiz_app.update_questions,
338
  inputs=[text_input, num_questions],
339
  outputs=[
340
  question_box,
 
346
  )
347
 
348
  submit_btn.click(
349
+ fn=quiz_app.submit_quiz,
350
  inputs=[*answers, current_questions],
351
  outputs=[
352
  feedback_box,
 
357
  ]
358
  )
359
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
360
  score_display.change(
361
+ fn=quiz_app.certificate_generator.generate,
362
  inputs=[score_display, name, course_name, company_logo, participant_photo],
363
  outputs=certificate_display
364
  )
365
+
366
  return demo
367
 
368
  if __name__ == "__main__":
 
 
 
 
369
  demo = create_quiz_interface()
370
  demo.launch()