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

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +63 -36
app.py CHANGED
@@ -71,26 +71,30 @@ def convert_docx_to_pdf(docx_path):
71
  """Convert Word document to PDF using LibreOffice or system tools"""
72
  try:
73
  output_pdf = docx_path.replace('.docx', '.pdf')
 
74
 
75
  # Try LibreOffice conversion (works on Linux/HuggingFace)
76
  if platform.system() != 'Windows':
77
  try:
 
78
  result = subprocess.run([
79
  'libreoffice', '--headless', '--convert-to', 'pdf',
80
  '--outdir', os.path.dirname(docx_path), docx_path
81
  ], check=True, capture_output=True, timeout=30)
82
 
83
  if os.path.exists(output_pdf):
 
84
  return output_pdf
85
  else:
86
- print(f"LibreOffice: PDF not created, using DOCX")
87
  return docx_path
88
  except (subprocess.CalledProcessError, FileNotFoundError, subprocess.TimeoutExpired) as e:
89
- print(f"LibreOffice not available or failed: {e}. Using DOCX file.")
90
  return docx_path
91
  else:
92
  # For Windows, try using win32com with proper error handling
93
  try:
 
94
  import win32com.client
95
  import pythoncom
96
  import time
@@ -103,10 +107,14 @@ def convert_docx_to_pdf(docx_path):
103
  word.DisplayAlerts = 0
104
 
105
  # Open document
106
- doc = word.Documents.Open(os.path.abspath(docx_path))
 
 
 
107
 
108
  # Save as PDF (wdFormatPDF = 17)
109
- doc.SaveAs2(os.path.abspath(output_pdf), FileFormat=17)
 
110
 
111
  # Close document
112
  doc.Close(SaveChanges=False)
@@ -116,9 +124,10 @@ def convert_docx_to_pdf(docx_path):
116
 
117
  # Check if PDF was created
118
  if os.path.exists(output_pdf):
 
119
  return output_pdf
120
  else:
121
- print(f"PDF not created: {output_pdf}")
122
  return docx_path
123
 
124
  finally:
@@ -126,12 +135,15 @@ def convert_docx_to_pdf(docx_path):
126
  word.Quit()
127
  pythoncom.CoUninitialize()
128
 
 
 
 
129
  except Exception as e:
130
- print(f"Win32com PDF conversion error: {e}")
131
  # If conversion fails, return docx path
132
  return docx_path
133
  except Exception as e:
134
- print(f"PDF conversion error: {e}")
135
  return docx_path
136
 
137
  def replace_text_in_docx(docx_path, placeholder, replacement):
@@ -345,7 +357,7 @@ def process_certificates(template_file, csv_file, send_emails, placeholder_text,
345
  try:
346
  # Validate email config if sending emails
347
  if send_emails:
348
- if not all(email_config.values()):
349
  return "❌ Please configure email server settings first!", None
350
 
351
  # Update placeholder text
@@ -371,71 +383,86 @@ def process_certificates(template_file, csv_file, send_emails, placeholder_text,
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}")
400
  else:
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
 
418
  if success:
419
  email_successful += 1
420
- # Update result
421
- for i, res in enumerate(results):
422
- if name in res:
423
  results[i] = f"βœ… {name} ({email})"
424
  break
425
  else:
426
  email_failed += 1
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
435
 
436
  # Create ZIP file with all certificates
437
  zip_path = None
438
- if generated_files:
 
 
439
  try:
440
  # Create a temporary directory for the ZIP
441
  zip_dir = tempfile.mkdtemp()
@@ -445,7 +472,7 @@ def process_certificates(template_file, csv_file, send_emails, placeholder_text,
445
 
446
  # Create ZIP file
447
  with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
448
- for cert_file in generated_files:
449
  if os.path.exists(cert_file):
450
  # Add file to ZIP with just the filename (not full path)
451
  zipf.write(cert_file, os.path.basename(cert_file))
 
71
  """Convert Word document to PDF using LibreOffice or system tools"""
72
  try:
73
  output_pdf = docx_path.replace('.docx', '.pdf')
74
+ print(f"πŸ“„ Converting {os.path.basename(docx_path)} to PDF...")
75
 
76
  # Try LibreOffice conversion (works on Linux/HuggingFace)
77
  if platform.system() != 'Windows':
78
  try:
79
+ print("🐧 Using LibreOffice (Linux)...")
80
  result = subprocess.run([
81
  'libreoffice', '--headless', '--convert-to', 'pdf',
82
  '--outdir', os.path.dirname(docx_path), docx_path
83
  ], check=True, capture_output=True, timeout=30)
84
 
85
  if os.path.exists(output_pdf):
86
+ print(f"βœ… PDF created: {os.path.basename(output_pdf)}")
87
  return output_pdf
88
  else:
89
+ print(f"⚠️ LibreOffice: PDF not created, using DOCX")
90
  return docx_path
91
  except (subprocess.CalledProcessError, FileNotFoundError, subprocess.TimeoutExpired) as e:
92
+ print(f"⚠️ LibreOffice not available or failed: {e}. Using DOCX file.")
93
  return docx_path
94
  else:
95
  # For Windows, try using win32com with proper error handling
96
  try:
97
+ print("πŸͺŸ Using win32com (Windows)...")
98
  import win32com.client
99
  import pythoncom
100
  import time
 
107
  word.DisplayAlerts = 0
108
 
109
  # Open document
110
+ abs_docx = os.path.abspath(docx_path)
111
+ abs_pdf = os.path.abspath(output_pdf)
112
+ print(f"Opening: {abs_docx}")
113
+ doc = word.Documents.Open(abs_docx)
114
 
115
  # Save as PDF (wdFormatPDF = 17)
116
+ print(f"Saving as: {abs_pdf}")
117
+ doc.SaveAs2(abs_pdf, FileFormat=17)
118
 
119
  # Close document
120
  doc.Close(SaveChanges=False)
 
124
 
125
  # Check if PDF was created
126
  if os.path.exists(output_pdf):
127
+ print(f"βœ… PDF created: {os.path.basename(output_pdf)}")
128
  return output_pdf
129
  else:
130
+ print(f"❌ PDF not created: {output_pdf}")
131
  return docx_path
132
 
133
  finally:
 
135
  word.Quit()
136
  pythoncom.CoUninitialize()
137
 
138
+ except ImportError as ie:
139
+ print(f"⚠️ win32com not installed: {ie}. Returning DOCX file.")
140
+ return docx_path
141
  except Exception as e:
142
+ print(f"❌ Win32com PDF conversion error: {e}")
143
  # If conversion fails, return docx path
144
  return docx_path
145
  except Exception as e:
146
+ print(f"❌ PDF conversion error: {e}")
147
  return docx_path
148
 
149
  def replace_text_in_docx(docx_path, placeholder, replacement):
 
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
 
383
  successful = 0
384
  failed = 0
385
  generated_files = []
 
386
 
387
+ # Phase 1: Generate all certificates in parallel with progress
388
+ cert_futures = {}
389
 
390
  with ThreadPoolExecutor(max_workers=MAX_WORKERS) as executor:
391
+ # Submit all tasks
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):
401
+ name, email = cert_futures[future]
402
+ completed_count += 1
403
+
404
  try:
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:
434
  email_successful += 1
435
+ # Update existing result
436
+ for i in range(len(results)):
437
+ if name in results[i]:
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)):
444
+ if name in results[i]:
445
  results[i] = f"❌ {name} ({email}): {error}"
446
  break
447
+
448
+ # Update progress (fix division to show correct percentage)
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]:
455
+ results[i] = f"❌ {name} ({email}): {str(e)}"
456
+ break
457
 
458
  successful = email_successful
459
  failed = email_failed
460
 
461
  # Create ZIP file with all certificates
462
  zip_path = None
463
+ all_cert_files = [cert_file for cert_file, _, _ in generated_files] if generated_files else []
464
+
465
+ if all_cert_files:
466
  try:
467
  # Create a temporary directory for the ZIP
468
  zip_dir = tempfile.mkdtemp()
 
472
 
473
  # Create ZIP file
474
  with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
475
+ for cert_file in all_cert_files:
476
  if os.path.exists(cert_file):
477
  # Add file to ZIP with just the filename (not full path)
478
  zipf.write(cert_file, os.path.basename(cert_file))