Upload app.py
Browse files
app.py
CHANGED
|
@@ -24,7 +24,19 @@ email_config = {
|
|
| 24 |
'smtp_server': '',
|
| 25 |
'smtp_port': 587,
|
| 26 |
'sender_email': '',
|
| 27 |
-
'sender_password': ''
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 28 |
}
|
| 29 |
|
| 30 |
# Text replacement configuration
|
|
@@ -36,14 +48,16 @@ text_replacement_config = {
|
|
| 36 |
EMAIL_DELAY = 2 # Seconds between emails to avoid being flagged as spam
|
| 37 |
MAX_WORKERS = 3 # Maximum parallel threads for certificate generation
|
| 38 |
|
| 39 |
-
def setup_email_server(smtp_server, smtp_port, sender_email, sender_password):
|
| 40 |
"""Configure email server settings"""
|
| 41 |
global email_config
|
| 42 |
email_config = {
|
| 43 |
'smtp_server': smtp_server,
|
| 44 |
'smtp_port': int(smtp_port),
|
| 45 |
'sender_email': sender_email,
|
| 46 |
-
'sender_password': sender_password
|
|
|
|
|
|
|
| 47 |
}
|
| 48 |
return "✅ Email server configured successfully!"
|
| 49 |
|
|
@@ -244,22 +258,10 @@ def send_email_with_certificate(recipient_email, recipient_name, cert_file_path)
|
|
| 244 |
msg = MIMEMultipart()
|
| 245 |
msg['From'] = email_config['sender_email']
|
| 246 |
msg['To'] = recipient_email
|
| 247 |
-
msg['Subject'] = 'Your Certificate of Participation
|
| 248 |
|
| 249 |
-
# Email body
|
| 250 |
-
body =
|
| 251 |
-
Dear {recipient_name},
|
| 252 |
-
|
| 253 |
-
Congratulations on successfully completing the webinar on "Modern Project Management Trends and Global Opportunities for Project Management"!
|
| 254 |
-
|
| 255 |
-
Please find your certificate of participation attached to this email.
|
| 256 |
-
|
| 257 |
-
This 2 PDU Programme is accepted by PMI-USA (PDU Code: 25675RL34G).
|
| 258 |
-
|
| 259 |
-
Best regards,
|
| 260 |
-
Pro Consultancy International (Pvt.) Ltd
|
| 261 |
-
PMI Authorized Training Partner
|
| 262 |
-
"""
|
| 263 |
|
| 264 |
msg.attach(MIMEText(body, 'plain'))
|
| 265 |
|
|
@@ -369,24 +371,29 @@ def process_certificates(template_file, csv_file, send_emails, placeholder_text,
|
|
| 369 |
successful = 0
|
| 370 |
failed = 0
|
| 371 |
generated_files = []
|
|
|
|
| 372 |
|
| 373 |
# Phase 1: Generate all certificates in parallel
|
| 374 |
-
progress(0, desc="Generating certificates...")
|
| 375 |
cert_tasks = []
|
| 376 |
|
| 377 |
with ThreadPoolExecutor(max_workers=MAX_WORKERS) as executor:
|
| 378 |
# Submit all certificate generation tasks
|
| 379 |
for idx, row in df.iterrows():
|
| 380 |
name = row['Name']
|
|
|
|
| 381 |
future = executor.submit(generate_single_certificate, template_path, name, placeholder_text)
|
| 382 |
-
cert_tasks.append((future, name,
|
| 383 |
|
| 384 |
# Collect results as they complete
|
| 385 |
-
|
|
|
|
| 386 |
try:
|
| 387 |
cert_file, error = future.result(timeout=60)
|
|
|
|
|
|
|
| 388 |
if cert_file:
|
| 389 |
generated_files.append(cert_file)
|
|
|
|
| 390 |
if not send_emails:
|
| 391 |
successful += 1
|
| 392 |
results.append(f"✅ {name}")
|
|
@@ -394,19 +401,17 @@ def process_certificates(template_file, csv_file, send_emails, placeholder_text,
|
|
| 394 |
failed += 1
|
| 395 |
results.append(f"❌ {name}: {error}")
|
| 396 |
except Exception as e:
|
|
|
|
| 397 |
failed += 1
|
| 398 |
results.append(f"❌ {name}: {str(e)}")
|
| 399 |
-
|
| 400 |
-
# Update progress
|
| 401 |
-
progress((idx + 1) / total, desc=f"Generated {idx + 1}/{total} certificates")
|
| 402 |
|
| 403 |
# Phase 2: Send emails sequentially with rate limiting (if requested)
|
| 404 |
-
if send_emails:
|
| 405 |
-
progress(0, desc="Sending emails...")
|
| 406 |
email_successful = 0
|
| 407 |
email_failed = 0
|
|
|
|
| 408 |
|
| 409 |
-
for idx, (cert_file,
|
| 410 |
if cert_file and os.path.exists(cert_file):
|
| 411 |
success, error = send_email_with_retry(email, name, cert_file)
|
| 412 |
|
|
@@ -422,11 +427,8 @@ def process_certificates(template_file, csv_file, send_emails, placeholder_text,
|
|
| 422 |
# Update result
|
| 423 |
for i, res in enumerate(results):
|
| 424 |
if name in res:
|
| 425 |
-
results[i] = f"❌ {name} ({email}):
|
| 426 |
break
|
| 427 |
-
|
| 428 |
-
# Update progress
|
| 429 |
-
progress((idx + 1) / len(generated_files), desc=f"Sent {idx + 1}/{len(generated_files)} emails")
|
| 430 |
|
| 431 |
successful = email_successful
|
| 432 |
failed = email_failed
|
|
@@ -496,12 +498,35 @@ with gr.Blocks(title="Certificate Generator & Email Sender", theme=gr.themes.Sof
|
|
| 496 |
type="password"
|
| 497 |
)
|
| 498 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 499 |
config_btn = gr.Button("💾 Save Configuration", variant="primary")
|
| 500 |
config_status = gr.Textbox(label="Status", interactive=False)
|
| 501 |
|
| 502 |
config_btn.click(
|
| 503 |
fn=setup_email_server,
|
| 504 |
-
inputs=[smtp_server, smtp_port, sender_email, sender_password],
|
| 505 |
outputs=config_status
|
| 506 |
)
|
| 507 |
|
|
|
|
| 24 |
'smtp_server': '',
|
| 25 |
'smtp_port': 587,
|
| 26 |
'sender_email': '',
|
| 27 |
+
'sender_password': '',
|
| 28 |
+
'email_subject': 'Your Certificate of Participation',
|
| 29 |
+
'email_body': '''Dear {name},
|
| 30 |
+
|
| 31 |
+
Congratulations on successfully completing the webinar on "Modern Project Management Trends and Global Opportunities for Project Management"!
|
| 32 |
+
|
| 33 |
+
Please find your certificate of participation attached to this email.
|
| 34 |
+
|
| 35 |
+
This 2 PDU Programme is accepted by PMI-USA (PDU Code: 25675RL34G).
|
| 36 |
+
|
| 37 |
+
Best regards,
|
| 38 |
+
Pro Consultancy International (Pvt.) Ltd
|
| 39 |
+
PMI Authorized Training Partner'''
|
| 40 |
}
|
| 41 |
|
| 42 |
# Text replacement configuration
|
|
|
|
| 48 |
EMAIL_DELAY = 2 # Seconds between emails to avoid being flagged as spam
|
| 49 |
MAX_WORKERS = 3 # Maximum parallel threads for certificate generation
|
| 50 |
|
| 51 |
+
def setup_email_server(smtp_server, smtp_port, sender_email, sender_password, email_subject, email_body):
|
| 52 |
"""Configure email server settings"""
|
| 53 |
global email_config
|
| 54 |
email_config = {
|
| 55 |
'smtp_server': smtp_server,
|
| 56 |
'smtp_port': int(smtp_port),
|
| 57 |
'sender_email': sender_email,
|
| 58 |
+
'sender_password': sender_password,
|
| 59 |
+
'email_subject': email_subject,
|
| 60 |
+
'email_body': email_body
|
| 61 |
}
|
| 62 |
return "✅ Email server configured successfully!"
|
| 63 |
|
|
|
|
| 258 |
msg = MIMEMultipart()
|
| 259 |
msg['From'] = email_config['sender_email']
|
| 260 |
msg['To'] = recipient_email
|
| 261 |
+
msg['Subject'] = email_config.get('email_subject', 'Your Certificate of Participation')
|
| 262 |
|
| 263 |
+
# Email body - replace {name} placeholder with actual name
|
| 264 |
+
body = email_config.get('email_body', 'Dear {name},\n\nPlease find your certificate attached.').format(name=recipient_name)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 265 |
|
| 266 |
msg.attach(MIMEText(body, 'plain'))
|
| 267 |
|
|
|
|
| 371 |
successful = 0
|
| 372 |
failed = 0
|
| 373 |
generated_files = []
|
| 374 |
+
cert_data = [] # Store (cert_file, name, email) tuples
|
| 375 |
|
| 376 |
# Phase 1: Generate all certificates in parallel
|
|
|
|
| 377 |
cert_tasks = []
|
| 378 |
|
| 379 |
with ThreadPoolExecutor(max_workers=MAX_WORKERS) as executor:
|
| 380 |
# Submit all certificate generation tasks
|
| 381 |
for idx, row in df.iterrows():
|
| 382 |
name = row['Name']
|
| 383 |
+
email = row.get('Email address', '')
|
| 384 |
future = executor.submit(generate_single_certificate, template_path, name, placeholder_text)
|
| 385 |
+
cert_tasks.append((future, name, email))
|
| 386 |
|
| 387 |
# Collect results as they complete
|
| 388 |
+
completed = 0
|
| 389 |
+
for future, name, email in cert_tasks:
|
| 390 |
try:
|
| 391 |
cert_file, error = future.result(timeout=60)
|
| 392 |
+
completed += 1
|
| 393 |
+
|
| 394 |
if cert_file:
|
| 395 |
generated_files.append(cert_file)
|
| 396 |
+
cert_data.append((cert_file, name, email))
|
| 397 |
if not send_emails:
|
| 398 |
successful += 1
|
| 399 |
results.append(f"✅ {name}")
|
|
|
|
| 401 |
failed += 1
|
| 402 |
results.append(f"❌ {name}: {error}")
|
| 403 |
except Exception as e:
|
| 404 |
+
completed += 1
|
| 405 |
failed += 1
|
| 406 |
results.append(f"❌ {name}: {str(e)}")
|
|
|
|
|
|
|
|
|
|
| 407 |
|
| 408 |
# Phase 2: Send emails sequentially with rate limiting (if requested)
|
| 409 |
+
if send_emails and cert_data:
|
|
|
|
| 410 |
email_successful = 0
|
| 411 |
email_failed = 0
|
| 412 |
+
email_total = len(cert_data)
|
| 413 |
|
| 414 |
+
for idx, (cert_file, name, email) in enumerate(cert_data):
|
| 415 |
if cert_file and os.path.exists(cert_file):
|
| 416 |
success, error = send_email_with_retry(email, name, cert_file)
|
| 417 |
|
|
|
|
| 427 |
# Update result
|
| 428 |
for i, res in enumerate(results):
|
| 429 |
if name in res:
|
| 430 |
+
results[i] = f"❌ {name} ({email}): {error}"
|
| 431 |
break
|
|
|
|
|
|
|
|
|
|
| 432 |
|
| 433 |
successful = email_successful
|
| 434 |
failed = email_failed
|
|
|
|
| 498 |
type="password"
|
| 499 |
)
|
| 500 |
|
| 501 |
+
email_subject = gr.Textbox(
|
| 502 |
+
label="Email Subject",
|
| 503 |
+
value="Your Certificate of Participation",
|
| 504 |
+
placeholder="Subject line for certificate emails"
|
| 505 |
+
)
|
| 506 |
+
|
| 507 |
+
email_body = gr.Textbox(
|
| 508 |
+
label="Email Body (use {name} for student name)",
|
| 509 |
+
value="""Dear {name},
|
| 510 |
+
|
| 511 |
+
Congratulations on successfully completing the webinar on "Modern Project Management Trends and Global Opportunities for Project Management"!
|
| 512 |
+
|
| 513 |
+
Please find your certificate of participation attached to this email.
|
| 514 |
+
|
| 515 |
+
This 2 PDU Programme is accepted by PMI-USA (PDU Code: 25675RL34G).
|
| 516 |
+
|
| 517 |
+
Best regards,
|
| 518 |
+
Pro Consultancy International (Pvt.) Ltd
|
| 519 |
+
PMI Authorized Training Partner""",
|
| 520 |
+
lines=12,
|
| 521 |
+
placeholder="Email body template"
|
| 522 |
+
)
|
| 523 |
+
|
| 524 |
config_btn = gr.Button("💾 Save Configuration", variant="primary")
|
| 525 |
config_status = gr.Textbox(label="Status", interactive=False)
|
| 526 |
|
| 527 |
config_btn.click(
|
| 528 |
fn=setup_email_server,
|
| 529 |
+
inputs=[smtp_server, smtp_port, sender_email, sender_password, email_subject, email_body],
|
| 530 |
outputs=config_status
|
| 531 |
)
|
| 532 |
|