Sandaru2 commited on
Commit
6369537
Β·
verified Β·
1 Parent(s): 76b796f

Upload 2 files

Browse files
Files changed (2) hide show
  1. app.py +72 -10
  2. packages.txt +0 -0
app.py CHANGED
@@ -264,8 +264,10 @@ def preview_with_test_name(template_file, test_name, placeholder_text="[NAME]"):
264
  return f"❌ Error: {str(e)}"
265
 
266
  def send_email_with_certificate(recipient_email, recipient_name, cert_file_path):
267
- """Send certificate via email as PDF"""
268
  try:
 
 
269
  # Create message
270
  msg = MIMEMultipart()
271
  msg['From'] = email_config['sender_email']
@@ -279,7 +281,11 @@ def send_email_with_certificate(recipient_email, recipient_name, cert_file_path)
279
 
280
  # Check if file exists and is a valid PDF or DOCX
281
  if not os.path.exists(cert_file_path):
282
- return False, f"Certificate file not found: {cert_file_path}"
 
 
 
 
283
 
284
  # Determine file type and set appropriate MIME type
285
  file_ext = os.path.splitext(cert_file_path)[1].lower()
@@ -303,15 +309,20 @@ def send_email_with_certificate(recipient_email, recipient_name, cert_file_path)
303
  part.add_header('Content-Type', mime_type)
304
  msg.attach(part)
305
 
 
 
306
  # Send email
307
  with smtplib.SMTP(email_config['smtp_server'], email_config['smtp_port']) as server:
308
  server.starttls()
309
  server.login(email_config['sender_email'], email_config['sender_password'])
310
  server.send_message(msg)
311
 
 
312
  return True, None
313
  except Exception as e:
314
- return False, str(e)
 
 
315
 
316
  def send_email_with_retry(recipient_email, recipient_name, cert_file_path, max_retries=3):
317
  """Send email with retry logic and delay"""
@@ -355,10 +366,23 @@ def generate_single_certificate(template_path, name, placeholder_text):
355
  def process_certificates(template_file, csv_file, send_emails, placeholder_text, progress=gr.Progress()):
356
  """Main function to process all certificates with threading"""
357
  try:
 
 
 
 
 
 
358
  # Validate email config if sending emails
359
  if send_emails:
360
  if not all([email_config.get('smtp_server'), email_config.get('sender_email'), email_config.get('sender_password')]):
361
- return "❌ Please configure email server settings first!", None
 
 
 
 
 
 
 
362
 
363
  # Update placeholder text
364
  setup_text_replacement(placeholder_text)
@@ -366,17 +390,27 @@ def process_certificates(template_file, csv_file, send_emails, placeholder_text,
366
  # Load template
367
  template_path = load_template(template_file)
368
  if template_path is None:
369
- return "❌ Could not load template file. Please upload a valid Word document (.docx).", None
 
 
 
 
370
 
371
  # Read CSV
372
  df = pd.read_csv(csv_file)
 
 
373
 
374
  # Validate CSV columns
375
  if 'Name' not in df.columns:
376
- return "❌ CSV must have a 'Name' column!", None
 
 
377
 
378
  if send_emails and 'Email address' not in df.columns:
379
- return "❌ CSV must have an 'Email address' column for sending emails!", None
 
 
380
 
381
  results = []
382
  total = len(df)
@@ -384,6 +418,8 @@ def process_certificates(template_file, csv_file, send_emails, placeholder_text,
384
  failed = 0
385
  generated_files = []
386
 
 
 
387
  # Phase 1: Generate all certificates in parallel with progress
388
  cert_futures = {}
389
 
@@ -392,9 +428,12 @@ def process_certificates(template_file, csv_file, send_emails, placeholder_text,
392
  for idx, row in df.iterrows():
393
  name = row['Name']
394
  email = row.get('Email address', '')
 
395
  future = executor.submit(generate_single_certificate, template_path, name, placeholder_text)
396
  cert_futures[future] = (name, email)
397
 
 
 
398
  # Process completed futures with progress
399
  completed_count = 0
400
  for future in as_completed(cert_futures):
@@ -405,29 +444,38 @@ def process_certificates(template_file, csv_file, send_emails, placeholder_text,
405
  cert_file, error = future.result()
406
 
407
  if cert_file and os.path.exists(cert_file):
 
 
408
  generated_files.append((cert_file, name, email))
409
  if not send_emails:
410
  successful += 1
411
  results.append(f"βœ… {name}")
412
  else:
 
413
  failed += 1
414
  results.append(f"❌ {name}: {error if error else 'Generation failed'}")
415
 
416
  except Exception as e:
 
417
  failed += 1
418
  results.append(f"❌ {name}: {str(e)}")
419
 
420
  # Update progress
421
  progress(completed_count / total, desc=f"πŸ“„ Generated {completed_count}/{total} certificates")
422
 
 
 
423
  # Phase 2: Send emails sequentially with rate limiting and progress
424
  if send_emails and generated_files:
425
  email_successful = 0
426
  email_failed = 0
427
  email_total = len(generated_files)
428
 
 
 
429
  for idx, (cert_file, name, email) in enumerate(generated_files):
430
  try:
 
431
  success, error = send_email_with_retry(email, name, cert_file)
432
 
433
  if success:
@@ -438,6 +486,7 @@ def process_certificates(template_file, csv_file, send_emails, placeholder_text,
438
  results[i] = f"βœ… {name} ({email})"
439
  break
440
  else:
 
441
  email_failed += 1
442
  # Update existing result
443
  for i in range(len(results)):
@@ -449,6 +498,7 @@ def process_certificates(template_file, csv_file, send_emails, placeholder_text,
449
  progress((idx + 1) / email_total, desc=f"πŸ“§ Sent {idx + 1}/{email_total} emails")
450
 
451
  except Exception as e:
 
452
  email_failed += 1
453
  for i in range(len(results)):
454
  if name in results[i]:
@@ -457,6 +507,8 @@ def process_certificates(template_file, csv_file, send_emails, placeholder_text,
457
 
458
  successful = email_successful
459
  failed = email_failed
 
 
460
 
461
  # Create ZIP file with all certificates
462
  zip_path = None
@@ -464,6 +516,7 @@ def process_certificates(template_file, csv_file, send_emails, placeholder_text,
464
 
465
  if all_cert_files:
466
  try:
 
467
  # Create a temporary directory for the ZIP
468
  zip_dir = tempfile.mkdtemp()
469
  timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
@@ -477,19 +530,28 @@ def process_certificates(template_file, csv_file, send_emails, placeholder_text,
477
  # Add file to ZIP with just the filename (not full path)
478
  zipf.write(cert_file, os.path.basename(cert_file))
479
 
480
- print(f"ZIP file created: {zip_path}")
481
  except Exception as e:
482
- print(f"Error creating ZIP: {e}")
483
  zip_path = None
484
 
485
  # Create summary
 
 
 
 
 
486
  summary = f"Processed: {total} | Success: {successful} | Failed: {failed}\n\n"
487
  summary += "\n".join(results)
488
 
489
  return summary, zip_path
490
 
491
  except Exception as e:
492
- return f"❌ Error: {str(e)}", None
 
 
 
 
493
 
494
  # Create Gradio Interface
495
  with gr.Blocks(title="Certificate Generator & Email Sender", theme=gr.themes.Soft()) as app:
 
264
  return f"❌ Error: {str(e)}"
265
 
266
  def send_email_with_certificate(recipient_email, recipient_name, cert_file_path):
267
+ """Send certificate via email as PDF or DOCX"""
268
  try:
269
+ print(f"πŸ“§ Preparing email for {recipient_name} ({recipient_email})...")
270
+
271
  # Create message
272
  msg = MIMEMultipart()
273
  msg['From'] = email_config['sender_email']
 
281
 
282
  # Check if file exists and is a valid PDF or DOCX
283
  if not os.path.exists(cert_file_path):
284
+ error_msg = f"Certificate file not found: {cert_file_path}"
285
+ print(f"❌ {error_msg}")
286
+ return False, error_msg
287
+
288
+ print(f"πŸ“Ž Attaching: {os.path.basename(cert_file_path)}")
289
 
290
  # Determine file type and set appropriate MIME type
291
  file_ext = os.path.splitext(cert_file_path)[1].lower()
 
309
  part.add_header('Content-Type', mime_type)
310
  msg.attach(part)
311
 
312
+ print(f"πŸš€ Sending email to {recipient_email}...")
313
+
314
  # Send email
315
  with smtplib.SMTP(email_config['smtp_server'], email_config['smtp_port']) as server:
316
  server.starttls()
317
  server.login(email_config['sender_email'], email_config['sender_password'])
318
  server.send_message(msg)
319
 
320
+ print(f"βœ… Email sent successfully to {recipient_name}")
321
  return True, None
322
  except Exception as e:
323
+ error_msg = f"Email error: {str(e)}"
324
+ print(f"❌ {error_msg}")
325
+ return False, error_msg
326
 
327
  def send_email_with_retry(recipient_email, recipient_name, cert_file_path, max_retries=3):
328
  """Send email with retry logic and delay"""
 
366
  def process_certificates(template_file, csv_file, send_emails, placeholder_text, progress=gr.Progress()):
367
  """Main function to process all certificates with threading"""
368
  try:
369
+ print("\n" + "="*60)
370
+ print("πŸš€ Starting certificate processing...")
371
+ print(f"πŸ“‹ Send emails: {send_emails}")
372
+ print(f"πŸ”€ Placeholder text: '{placeholder_text}'")
373
+ print("="*60 + "\n")
374
+
375
  # Validate email config if sending emails
376
  if send_emails:
377
  if not all([email_config.get('smtp_server'), email_config.get('sender_email'), email_config.get('sender_password')]):
378
+ error_msg = "❌ Please configure email server settings first!"
379
+ print(error_msg)
380
+ return error_msg, None
381
+
382
+ print(f"πŸ“§ Email configured:")
383
+ print(f" Server: {email_config['smtp_server']}:{email_config['smtp_port']}")
384
+ print(f" Sender: {email_config['sender_email']}")
385
+ print(f" Subject: {email_config.get('email_subject', 'Your Certificate')}")
386
 
387
  # Update placeholder text
388
  setup_text_replacement(placeholder_text)
 
390
  # Load template
391
  template_path = load_template(template_file)
392
  if template_path is None:
393
+ error_msg = "❌ Could not load template file. Please upload a valid Word document (.docx)."
394
+ print(error_msg)
395
+ return error_msg, None
396
+
397
+ print(f"βœ… Template loaded: {os.path.basename(template_path)}\n")
398
 
399
  # Read CSV
400
  df = pd.read_csv(csv_file)
401
+ print(f"πŸ“Š CSV loaded: {len(df)} students found")
402
+ print(f" Columns: {', '.join(df.columns.tolist())}\n")
403
 
404
  # Validate CSV columns
405
  if 'Name' not in df.columns:
406
+ error_msg = "❌ CSV must have a 'Name' column!"
407
+ print(error_msg)
408
+ return error_msg, None
409
 
410
  if send_emails and 'Email address' not in df.columns:
411
+ error_msg = "❌ CSV must have an 'Email address' column for sending emails!"
412
+ print(error_msg)
413
+ return error_msg, None
414
 
415
  results = []
416
  total = len(df)
 
418
  failed = 0
419
  generated_files = []
420
 
421
+ print(f"πŸ“„ Phase 1: Generating {total} certificates in parallel (max {MAX_WORKERS} workers)...\n")
422
+
423
  # Phase 1: Generate all certificates in parallel with progress
424
  cert_futures = {}
425
 
 
428
  for idx, row in df.iterrows():
429
  name = row['Name']
430
  email = row.get('Email address', '')
431
+ print(f" πŸ“ Queued: {name}")
432
  future = executor.submit(generate_single_certificate, template_path, name, placeholder_text)
433
  cert_futures[future] = (name, email)
434
 
435
+ print(f"\nβš™οΈ Processing {len(cert_futures)} certificates...\n")
436
+
437
  # Process completed futures with progress
438
  completed_count = 0
439
  for future in as_completed(cert_futures):
 
444
  cert_file, error = future.result()
445
 
446
  if cert_file and os.path.exists(cert_file):
447
+ file_ext = os.path.splitext(cert_file)[1]
448
+ print(f" βœ… [{completed_count}/{total}] Generated {file_ext} for: {name}")
449
  generated_files.append((cert_file, name, email))
450
  if not send_emails:
451
  successful += 1
452
  results.append(f"βœ… {name}")
453
  else:
454
+ print(f" ❌ [{completed_count}/{total}] Failed for: {name} - {error if error else 'Unknown error'}")
455
  failed += 1
456
  results.append(f"❌ {name}: {error if error else 'Generation failed'}")
457
 
458
  except Exception as e:
459
+ print(f" ❌ [{completed_count}/{total}] Exception for: {name} - {str(e)}")
460
  failed += 1
461
  results.append(f"❌ {name}: {str(e)}")
462
 
463
  # Update progress
464
  progress(completed_count / total, desc=f"πŸ“„ Generated {completed_count}/{total} certificates")
465
 
466
+ print(f"\nβœ… Phase 1 complete: {len(generated_files)} certificates generated\n")
467
+
468
  # Phase 2: Send emails sequentially with rate limiting and progress
469
  if send_emails and generated_files:
470
  email_successful = 0
471
  email_failed = 0
472
  email_total = len(generated_files)
473
 
474
+ print(f"πŸ“§ Phase 2: Sending {email_total} emails sequentially...\n")
475
+
476
  for idx, (cert_file, name, email) in enumerate(generated_files):
477
  try:
478
+ print(f" πŸ“§ [{idx+1}/{email_total}] Sending to: {name} ({email})...")
479
  success, error = send_email_with_retry(email, name, cert_file)
480
 
481
  if success:
 
486
  results[i] = f"βœ… {name} ({email})"
487
  break
488
  else:
489
+ print(f" ⚠️ Email failed: {error}")
490
  email_failed += 1
491
  # Update existing result
492
  for i in range(len(results)):
 
498
  progress((idx + 1) / email_total, desc=f"πŸ“§ Sent {idx + 1}/{email_total} emails")
499
 
500
  except Exception as e:
501
+ print(f" ⚠️ Email exception: {str(e)}")
502
  email_failed += 1
503
  for i in range(len(results)):
504
  if name in results[i]:
 
507
 
508
  successful = email_successful
509
  failed = email_failed
510
+
511
+ print(f"\nβœ… Phase 2 complete: {email_successful} emails sent, {email_failed} failed\n")
512
 
513
  # Create ZIP file with all certificates
514
  zip_path = None
 
516
 
517
  if all_cert_files:
518
  try:
519
+ print(f"πŸ“¦ Creating ZIP file with {len(all_cert_files)} certificates...")
520
  # Create a temporary directory for the ZIP
521
  zip_dir = tempfile.mkdtemp()
522
  timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
 
530
  # Add file to ZIP with just the filename (not full path)
531
  zipf.write(cert_file, os.path.basename(cert_file))
532
 
533
+ print(f"βœ… ZIP file created: {zip_filename} ({os.path.getsize(zip_path) / 1024:.1f} KB)\n")
534
  except Exception as e:
535
+ print(f"❌ Error creating ZIP: {e}\n")
536
  zip_path = None
537
 
538
  # Create summary
539
+ print("="*60)
540
+ print(f"πŸŽ‰ Processing complete!")
541
+ print(f" Total: {total} | Success: {successful} | Failed: {failed}")
542
+ print("="*60 + "\n")
543
+
544
  summary = f"Processed: {total} | Success: {successful} | Failed: {failed}\n\n"
545
  summary += "\n".join(results)
546
 
547
  return summary, zip_path
548
 
549
  except Exception as e:
550
+ error_msg = f"❌ Error: {str(e)}"
551
+ print(error_msg)
552
+ import traceback
553
+ print(traceback.format_exc())
554
+ return error_msg, None
555
 
556
  # Create Gradio Interface
557
  with gr.Blocks(title="Certificate Generator & Email Sender", theme=gr.themes.Soft()) as app:
packages.txt CHANGED
Binary files a/packages.txt and b/packages.txt differ