capradeepgujaran commited on
Commit
e178727
1 Parent(s): 1386b3c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +127 -61
app.py CHANGED
@@ -1,7 +1,7 @@
1
  import gradio as gr
2
  from groq import Groq
3
  import os
4
- from PIL import Image, ImageDraw, ImageFont
5
  from datetime import datetime
6
  import json
7
  import tempfile
@@ -9,6 +9,7 @@ from typing import List, Dict, Tuple, Optional
9
  from dataclasses import dataclass
10
  import subprocess
11
  import re
 
12
 
13
  @dataclass
14
  class Question:
@@ -44,7 +45,7 @@ class QuizGenerator:
44
  }
45
  ],
46
  model="llama-3.2-3b-preview",
47
- temperature=0,
48
  max_tokens=1024
49
  )
50
 
@@ -313,14 +314,26 @@ class CertificateGenerator:
313
  return fonts
314
 
315
  def _add_professional_border(self, draw: ImageDraw.Draw):
316
- # Single elegant inner border with padding
317
  padding = 40
318
- draw.rectangle(
319
- [(padding, padding),
320
- (self.certificate_size[0] - padding, self.certificate_size[1] - padding)],
321
- outline='#1C1D1F',
322
- width=2
323
- )
 
 
 
 
 
 
 
 
 
 
 
 
324
 
325
  def _add_content(
326
  self,
@@ -328,59 +341,40 @@ class CertificateGenerator:
328
  fonts: Dict[str, ImageFont.FreeTypeFont],
329
  name: str,
330
  course_name: str,
331
- score: float
 
332
  ):
 
333
  # Add "CERTIFICATE OF COMPLETION" text
334
- draw.text((60, 140), "CERTIFICATE OF COMPLETION", font=fonts['subtitle'], fill='#666666')
335
 
336
  # Add course name (large and bold)
337
  course_name = course_name.strip() or "Assessment"
338
- text_width = draw.textlength(course_name, fonts['title'])
339
- max_width = self.certificate_size[0] - 120 # Leave margins
340
- if text_width > max_width:
341
- words = course_name.split()
342
- lines = []
343
- current_line = []
344
- current_width = 0
345
- for word in words:
346
- word_width = draw.textlength(word + " ", fonts['title'])
347
- if current_width + word_width <= max_width:
348
- current_line.append(word)
349
- current_width += word_width
350
- else:
351
- lines.append(" ".join(current_line))
352
- current_line = [word]
353
- current_width = word_width
354
- if current_line:
355
- lines.append(" ".join(current_line))
356
- course_name = "\n".join(lines)
357
-
358
- draw.multiline_text((60, 200), course_name, font=fonts['title'], fill='#1C1D1F', spacing=10)
359
 
360
  # Add instructor info
361
- draw.text((60, 300), "Instructor", font=fonts['subtitle'], fill='#666666')
362
- draw.text((60, 330), "CertifyMe AI", font=fonts['text'], fill='#1C1D1F')
363
 
364
  # Add participant name (large)
365
  name = name.strip() or "Participant"
366
- draw.text((60, 420), name, font=fonts['name'], fill='#1C1D1F')
367
 
368
- # Add date and score info with spacing
369
  date_str = datetime.now().strftime("%b. %d, %Y")
370
 
371
  # Date section
372
- draw.text((60, 500), "Date", font=fonts['subtitle'], fill='#666666')
373
- draw.text((60, 530), date_str, font=fonts['text'], fill='#1C1D1F')
374
 
375
  # Score section
376
- draw.text((300, 500), "Score", font=fonts['subtitle'], fill='#666666')
377
- draw.text((300, 530), f"{float(score):.1f}%", font=fonts['text'], fill='#1C1D1F')
378
-
379
- # Footer section with certificate number and reference
380
  certificate_id = f"Certificate no: {datetime.now().strftime('%Y%m%d')}-{abs(hash(name)) % 10000:04d}"
381
  ref_number = f"Reference Number: {abs(hash(name + date_str)) % 10000:04d}"
382
 
383
- # Draw footer text aligned to left and right
384
  draw.text((60, 720), certificate_id, font=fonts['subtitle'], fill='#666666')
385
  draw.text((1140, 720), ref_number, font=fonts['subtitle'], fill='#666666', anchor="ra")
386
 
@@ -395,26 +389,67 @@ class CertificateGenerator:
395
  print(f"Error adding logo: {e}")
396
 
397
  def _add_photo(self, certificate: Image.Image, photo_path: str):
 
398
  try:
 
 
 
 
 
399
  photo = Image.open(photo_path)
400
- # Create circular mask
401
- size = (100, 100)
 
 
 
 
 
 
 
402
  mask = Image.new('L', size, 0)
403
  draw = ImageDraw.Draw(mask)
404
  draw.ellipse((0, 0, size[0], size[1]), fill=255)
405
 
406
  # Resize photo maintaining aspect ratio
407
- photo.thumbnail(size)
 
 
 
 
 
 
408
 
409
- # Create a circular photo
 
 
 
 
 
 
 
 
 
 
410
  output = Image.new('RGBA', size, (0, 0, 0, 0))
411
  output.paste(photo, (0, 0))
412
  output.putalpha(mask)
413
 
414
- # Position in top-right corner with padding
415
- certificate.paste(output, (1000, 50), mask=output)
 
 
 
 
 
 
 
 
 
 
416
  except Exception as e:
417
- print(f"Error adding photo: {e}")
 
 
418
 
419
  def generate(
420
  self,
@@ -424,35 +459,66 @@ class CertificateGenerator:
424
  company_logo: Optional[str] = None,
425
  participant_photo: Optional[str] = None
426
  ) -> str:
 
427
  try:
428
- certificate = self._create_base_certificate()
 
429
  draw = ImageDraw.Draw(certificate)
430
 
431
- # Add professional border
432
  self._add_professional_border(draw)
433
 
 
434
  fonts = self._load_fonts()
435
- self._add_content(draw, fonts, str(name), str(course_name), float(score))
436
 
437
- if company_logo:
 
438
  self._add_logo(certificate, company_logo)
439
 
 
440
  if participant_photo:
 
441
  self._add_photo(certificate, participant_photo)
442
 
 
 
 
 
443
  return self._save_certificate(certificate)
 
444
  except Exception as e:
445
- print(f"Error generating certificate: {e}")
 
 
446
  return None
447
-
448
  def _create_base_certificate(self) -> Image.Image:
449
- return Image.new('RGB', self.certificate_size, self.background_color)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
450
 
451
  def _save_certificate(self, certificate: Image.Image) -> str:
452
- temp_file = tempfile.NamedTemporaryFile(delete=False, suffix='.png')
453
- certificate.save(temp_file.name, 'PNG', quality=95)
454
- return temp_file.name
455
-
 
 
 
 
 
456
 
457
  class QuizApp:
458
  def __init__(self, api_key: str):
 
1
  import gradio as gr
2
  from groq import Groq
3
  import os
4
+ from PIL import Image, ImageDraw, ImageFont, ImageFilter
5
  from datetime import datetime
6
  import json
7
  import tempfile
 
9
  from dataclasses import dataclass
10
  import subprocess
11
  import re
12
+ import random
13
 
14
  @dataclass
15
  class Question:
 
45
  }
46
  ],
47
  model="llama-3.2-3b-preview",
48
+ temperature=0.1,
49
  max_tokens=1024
50
  )
51
 
 
314
  return fonts
315
 
316
  def _add_professional_border(self, draw: ImageDraw.Draw):
317
+ """Add professional border with improved corners"""
318
  padding = 40
319
+ border_width = 2
320
+ corner_radius = 10
321
+
322
+ # Draw rounded rectangle border
323
+ x0, y0 = padding, padding
324
+ x1, y1 = self.certificate_size[0] - padding, self.certificate_size[1] - padding
325
+
326
+ # Draw corners
327
+ draw.arc((x0, y0, x0 + corner_radius * 2, y0 + corner_radius * 2), 180, 270, '#1C1D1F', border_width)
328
+ draw.arc((x1 - corner_radius * 2, y0, x1, y0 + corner_radius * 2), 270, 0, '#1C1D1F', border_width)
329
+ draw.arc((x0, y1 - corner_radius * 2, x0 + corner_radius * 2, y1), 90, 180, '#1C1D1F', border_width)
330
+ draw.arc((x1 - corner_radius * 2, y1 - corner_radius * 2, x1, y1), 0, 90, '#1C1D1F', border_width)
331
+
332
+ # Draw lines
333
+ draw.line((x0 + corner_radius, y0, x1 - corner_radius, y0), '#1C1D1F', border_width) # Top
334
+ draw.line((x0 + corner_radius, y1, x1 - corner_radius, y1), '#1C1D1F', border_width) # Bottom
335
+ draw.line((x0, y0 + corner_radius, x0, y1 - corner_radius), '#1C1D1F', border_width) # Left
336
+ draw.line((x1, y0 + corner_radius, x1, y1 - corner_radius), '#1C1D1F', border_width) # Right
337
 
338
  def _add_content(
339
  self,
 
341
  fonts: Dict[str, ImageFont.FreeTypeFont],
342
  name: str,
343
  course_name: str,
344
+ score: float,
345
+ y_offset: int = 140
346
  ):
347
+ """Add content with adjusted vertical positioning"""
348
  # Add "CERTIFICATE OF COMPLETION" text
349
+ draw.text((60, y_offset), "CERTIFICATE OF COMPLETION", font=fonts['subtitle'], fill='#666666')
350
 
351
  # Add course name (large and bold)
352
  course_name = course_name.strip() or "Assessment"
353
+ draw.text((60, y_offset + 60), course_name, font=fonts['title'], fill='#1C1D1F')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
354
 
355
  # Add instructor info
356
+ draw.text((60, y_offset + 160), "Instructor", font=fonts['subtitle'], fill='#666666')
357
+ draw.text((60, y_offset + 190), "CertifyMe AI", font=fonts['text'], fill='#1C1D1F')
358
 
359
  # Add participant name (large)
360
  name = name.strip() or "Participant"
361
+ draw.text((60, y_offset + 280), name, font=fonts['name'], fill='#1C1D1F')
362
 
363
+ # Add date and score info
364
  date_str = datetime.now().strftime("%b. %d, %Y")
365
 
366
  # Date section
367
+ draw.text((60, y_offset + 360), "Date", font=fonts['subtitle'], fill='#666666')
368
+ draw.text((60, y_offset + 390), date_str, font=fonts['text'], fill='#1C1D1F')
369
 
370
  # Score section
371
+ draw.text((300, y_offset + 360), "Score", font=fonts['subtitle'], fill='#666666')
372
+ draw.text((300, y_offset + 390), f"{float(score):.1f}%", font=fonts['text'], fill='#1C1D1F')
373
+
374
+ # Footer section
375
  certificate_id = f"Certificate no: {datetime.now().strftime('%Y%m%d')}-{abs(hash(name)) % 10000:04d}"
376
  ref_number = f"Reference Number: {abs(hash(name + date_str)) % 10000:04d}"
377
 
 
378
  draw.text((60, 720), certificate_id, font=fonts['subtitle'], fill='#666666')
379
  draw.text((1140, 720), ref_number, font=fonts['subtitle'], fill='#666666', anchor="ra")
380
 
 
389
  print(f"Error adding logo: {e}")
390
 
391
  def _add_photo(self, certificate: Image.Image, photo_path: str):
392
+ """Add a clear circular profile photo in the top-right corner with adjusted position"""
393
  try:
394
+ if not photo_path or not os.path.exists(photo_path):
395
+ print(f"Photo path does not exist: {photo_path}")
396
+ return
397
+
398
+ # Open and process photo
399
  photo = Image.open(photo_path)
400
+
401
+ # Define size for circular photo
402
+ size = (120, 120)
403
+
404
+ # Convert to RGB if not already
405
+ if photo.mode not in ('RGB', 'RGBA'):
406
+ photo = photo.convert('RGB')
407
+
408
+ # Create high-quality circular mask
409
  mask = Image.new('L', size, 0)
410
  draw = ImageDraw.Draw(mask)
411
  draw.ellipse((0, 0, size[0], size[1]), fill=255)
412
 
413
  # Resize photo maintaining aspect ratio
414
+ aspect = photo.width / photo.height
415
+ if aspect > 1:
416
+ new_height = size[1]
417
+ new_width = int(new_height * aspect)
418
+ else:
419
+ new_width = size[0]
420
+ new_height = int(new_width / aspect)
421
 
422
+ photo = photo.resize((new_width, max(new_height, 1)), Image.Resampling.LANCZOS)
423
+
424
+ # Center crop
425
+ if aspect > 1:
426
+ left = (new_width - size[0]) // 2
427
+ photo = photo.crop((left, 0, left + size[0], size[1]))
428
+ else:
429
+ top = (new_height - size[1]) // 2
430
+ photo = photo.crop((0, top, size[0], top + size[1]))
431
+
432
+ # Create circular photo
433
  output = Image.new('RGBA', size, (0, 0, 0, 0))
434
  output.paste(photo, (0, 0))
435
  output.putalpha(mask)
436
 
437
+ # Adjusted position - moved down from top
438
+ photo_x = certificate.width - size[0] - 60 # 60px from right
439
+ photo_y = 50 # Increased from 40 to 50px from top
440
+
441
+ # Add white background circle
442
+ bg = Image.new('RGBA', size, (255, 255, 255, 255))
443
+ certificate.paste(bg, (photo_x, photo_y), mask=mask)
444
+
445
+ # Paste the photo
446
+ certificate.paste(output, (photo_x, photo_y), mask=output)
447
+ print(f"Successfully added photo at position ({photo_x}, {photo_y})")
448
+
449
  except Exception as e:
450
+ print(f"Error adding photo: {str(e)}")
451
+ import traceback
452
+ traceback.print_exc()
453
 
454
  def generate(
455
  self,
 
459
  company_logo: Optional[str] = None,
460
  participant_photo: Optional[str] = None
461
  ) -> str:
462
+ """Generate certificate with improved photo handling"""
463
  try:
464
+ # Create base certificate
465
+ certificate = Image.new('RGB', self.certificate_size, self.background_color)
466
  draw = ImageDraw.Draw(certificate)
467
 
468
+ # Add border
469
  self._add_professional_border(draw)
470
 
471
+ # Load fonts
472
  fonts = self._load_fonts()
 
473
 
474
+ # Add company logo if provided
475
+ if company_logo and os.path.exists(company_logo):
476
  self._add_logo(certificate, company_logo)
477
 
478
+ # Add participant photo if provided
479
  if participant_photo:
480
+ print(f"Processing photo: {participant_photo}") # Debug info
481
  self._add_photo(certificate, participant_photo)
482
 
483
+ # Add content
484
+ self._add_content(draw, fonts, str(name), str(course_name), float(score))
485
+
486
+ # Save certificate
487
  return self._save_certificate(certificate)
488
+
489
  except Exception as e:
490
+ print(f"Error generating certificate: {str(e)}")
491
+ import traceback
492
+ traceback.print_exc()
493
  return None
494
+
495
  def _create_base_certificate(self) -> Image.Image:
496
+ """Create base certificate with improved background"""
497
+ # Create base image
498
+ certificate = Image.new('RGB', self.certificate_size, self.background_color)
499
+
500
+ # Add subtle gradient background (optional)
501
+ draw = ImageDraw.Draw(certificate)
502
+
503
+ # Add very subtle grain texture for professional look (optional)
504
+ width, height = certificate.size
505
+ for x in range(0, width, 4):
506
+ for y in range(0, height, 4):
507
+ if random.random() > 0.5:
508
+ draw.point((x, y), fill=(250, 250, 250))
509
+
510
+ return certificate
511
 
512
  def _save_certificate(self, certificate: Image.Image) -> str:
513
+ """Save certificate with improved error handling"""
514
+ try:
515
+ temp_file = tempfile.NamedTemporaryFile(delete=False, suffix='.png')
516
+ certificate.save(temp_file.name, 'PNG', quality=95)
517
+ print(f"Certificate saved to: {temp_file.name}") # Debug info
518
+ return temp_file.name
519
+ except Exception as e:
520
+ print(f"Error saving certificate: {str(e)}")
521
+ return None
522
 
523
  class QuizApp:
524
  def __init__(self, api_key: str):