Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
145 changes: 145 additions & 0 deletions certificate_automation/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
# WCC Certificate Automation

Automated certificate generation from PowerPoint templates and converting them to PDF format using JSON configuration

## Prerequisites

- Python 3.7 or higher
- Microsoft PowerPoint (required for PDF conversion on Windows)

## Installation

Install required Python packages:
```bash
pip install -r requirements.txt
```


#### Dependencies

- `python-pptx>=0.6.21`: PowerPoint file manipulation
- `comtypes>=1.1.14`: COM automation for PowerPoint



## Project Structure

```
wcc_certificate_automation/
├── README.md # This file
├── requirements.txt # Python dependencies
├── src/
│ ├── config.json # Main configuration file
│ └── generate_certificates.py # Main automation script
└── data/
├── input/
│ ├── templates/ # PPTX template files
│ │ ├── mentee.pptx
│ │ └── mentor.pptx
│ └── names/ # Names text files
│ ├── mentees.txt
│ └── mentors.txt
└── output/ # Generated certificates
├── ppts/ # Generated PPTX files
│ ├── mentee/
│ └── mentor/
└── pdfs/ # Generated PDF files
├── mentee/
└── mentor/
```

## Configuration

Edit `src/config.json` to customize certificate generation settings.

### Config File

```json
{
"certificate_types": [
{
"type": "mentee",
"template": "../data/input/templates/mentee.pptx",
"names_file": "../data/input/names/mentees.txt",
"pdf_dir": "../data/output/pdfs/mentee/",
"ppt_dir": "../data/output/ppts/mentee/",
"placeholder_text": "Sample Sample",
"font_name": "Georgia",
"font_size": 59.5
}
]
}
```

- **type**: Certificate type identifier (e.g., "mentee", "mentor")
- **template**: Path to the PPTX template file
- **names_file**: Path to text file containing names (one per line)
- **pdf_dir**: Output directory for PDF certificates
- **ppt_dir**: Output directory for PPTX certificates
- **placeholder_text**: Text in template to be replaced with names
- **font_name**: Font to use for names
- **font_size**: Font size in points

### Names Files

Create text files in `data/input/names/` with one name per line:

**Example** (`data/input/names/mentees.txt`):
```
John Smith
Jane Doe
Alice Johnson
```

### Template Files

Create PPTX template files in `data/input/templates/` with text placeholder:

**Example** (`data/input/templates/mentee.pptx`):

## Usage

Navigate to the `src` directory and run the main script:

```bash
cd src
python generate_certificates.py
```

The script will automatically:
1. Check for duplicate names in the input files
2. Generate PPTX certificates for each person
3. Verify all PPTX certificates were created
4. Convert all PPTX certificates to PDF format
5. Verify all PDF certificates were created
6. Display summary statistics

## Output Format

Generated certificate files are named using the person's name directly:
- PPTX: `data/output/ppts/mentee/John Smith.pptx`
- PDF: `data/output/pdfs/mentee/John Smith.pdf`

## Sample Logs

```
Generating MENTEE mentee certificates at ../data/output/ppts/mentee/
[1/68] Generated: ../data/output/ppts/mentee/John Smith.pptx
[2/68] Generated: ../data/output/ppts/mentee/Jane Doe.pptx
...

Successfully generated 68/68 mentee certificates

Checking metrics MENTEE certificates

Expected certificates: 68
Found certificates: 68

All mentee certificates are present!

Generating MENTEE mentee certificates at ../data/output/pdfs/mentee/
[1/68] Generated: ../data/output/pdfs/mentee/John Smith.pdf
...

Type: mentee Total: 68 PPTX Generated: 68 PDF Generated: 68
```
3 changes: 3 additions & 0 deletions certificate_automation/data/input/names/mentees.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
TestFN TestLN
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please move this folder under tools and update README there to include this extra automation. Thanks for the amazing script, I will give a try locally also, to extend and create another type of certificate.

FirstName LastName
TestFN TestLN
2 changes: 2 additions & 0 deletions certificate_automation/data/input/names/mentors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
TestFN TestLN
FirstName LastName
2 changes: 2 additions & 0 deletions certificate_automation/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
python-pptx>=0.6.21
comtypes>=1.1.14
Empty file.
24 changes: 24 additions & 0 deletions certificate_automation/src/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"certificate_types": [
{
"type": "mentee",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

great idea! Looking forward to you merge it and I can extend to new certificates :)

"template": "../data/input/templates/mentee.pptx",
"names_file": "../data/input/names/mentees.txt",
"pdf_dir": "../data/output/pdfs/mentee/",
"ppt_dir": "../data/output/ppts/mentee/",
"placeholder_text": "Sample Sample",
"font_name": "Georgia",
"font_size": 59.5
},
{
"type": "mentor",
"template": "../data/input/templates/mentor.pptx",
"names_file": "../data/input/names/mentors.txt",
"pdf_dir": "../data/output/pdfs/mentor/",
"ppt_dir": "../data/output/ppts/mentor/",
"placeholder_text": "Sample Sample",
"font_name": "Georgia",
"font_size": 59.5
}
]
}
176 changes: 176 additions & 0 deletions certificate_automation/src/generate_certificates.py
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we write some tests too?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

Very important!

Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
from collections import Counter
from pathlib import Path

import comtypes.client
from pptx import Presentation
import os
import json
import sys
from pptx.util import Pt

def load_config(config_path="config.json"):
with open(config_path, 'r', encoding='utf-8') as f:
return json.load(f)

def check_duplicates(names, cert_type):
counts = Counter(names)
duplicates = [name for name, count in counts.items() if count > 1]

if duplicates:
print(f"\nWARNING: Found {len(duplicates)} duplicate name(s) in {cert_type} list:")
for name in duplicates:
print(f" - {name} (appears {counts[name]} times)")

def load_names(names_file, cert_type):
all_names = []
with open(names_file, 'r', encoding='utf-8') as f:
all_names += [line.strip() for line in f if line.strip()]
check_duplicates(all_names, cert_type)
return set(all_names)

def generate_certificates_for_type(names, cert_config, file_type):
template = cert_config['template']
input_dir = cert_config["ppt_dir"] if file_type == "pdf" else None
output_dir = cert_config["ppt_dir"] if file_type == "pptx" else cert_config[
'pdf_dir']
placeholder_text = cert_config['placeholder_text']
font_name = cert_config['font_name']
font_size = cert_config['font_size']
cert_type = cert_config['type']

os.makedirs(output_dir, exist_ok=True)

print(f"Generating {cert_type.upper()} {cert_type} certificates at {output_dir}")

file_count = 0

for i, name in enumerate(names, 1):
file_name = None
try:
if file_type == "pptx":
file_name = generate_pptx(font_name, font_size, name, output_dir,
placeholder_text, template)
elif file_type == "pdf":
file_name = generate_pdf(name, input_dir, output_dir)

print(f"[{i}/{len(names)}] Generated: {file_name}")
file_count += 1

except Exception as e:
print(f"[{i}/{len(names)}] ERROR generating {file_type} "
f"certificate for {name}: {e}")

print(f"\nSuccessfully generated {file_count}/{len(names)} {cert_type} certificates")
return file_count


def generate_pptx(font_name, font_size, name, output_dir, placeholder_text,
template):
try:
prs = Presentation(template)
for slide in prs.slides:
for shape in slide.shapes:
if shape.has_text_frame and shape.text.strip() == placeholder_text:
tf = shape.text_frame
tf.clear()

p = tf.paragraphs[0]
run = p.add_run()
run.text = name
run.font.name = font_name
run.font.size = Pt(font_size)
pptx_path = os.path.join(output_dir, f"{name}.pptx")
prs.save(pptx_path)
return pptx_path
except Exception as e:
raise e

def generate_pdf(name, input_dir, output_path):
try:

output_path = Path(output_path)

pptx_path = os.path.abspath(os.path.join(input_dir, f"{name}.pptx"))

if not os.path.exists(pptx_path):
raise FileNotFoundError(f"PPTX not found: {pptx_path}")

presentation = powerpoint.Presentations.Open(pptx_path)

pdf_path = output_path / f"{name}.pdf"

presentation.SaveAs(str(pdf_path.resolve()), 32)

presentation.Close()

return pdf_path

except Exception as e:
raise e

def check_metrics(names, cert_config, file_type):
cert_type = cert_config['type']
output_dir = cert_config["ppt_dir"] if file_type == "pptx" else \
cert_config['pdf_dir']

print(f"Checking metrics {cert_type.upper()} certificates")

folder_path = Path(output_dir)

if not folder_path.exists():
print(f"ERROR: Directory does not exist: {output_dir}")
return 0, len(names)

existing_files = {f.stem for f in folder_path.glob(f"*.{file_type}")}

print(f"\nExpected certificates: {len(names)}")
print(f"Found certificates: {len(existing_files)}")

missing = []
for name in names:
if name not in existing_files:
missing.append(name)

if missing:
print(f"\nMissing {len(missing)} certificate(s):")
for name in missing:
print(f" - {name}")
else:
print(f"\nAll {cert_type} certificates are present!")

def main():
global powerpoint
try:
config = load_config()

powerpoint = comtypes.client.CreateObject("PowerPoint.Application")
powerpoint.Visible = 1

for cert_config in config['certificate_types']:
names_file = cert_config['names_file']
cert_type = cert_config['type']

names = load_names(names_file, cert_type)

pptx_generated = generate_certificates_for_type(names,
cert_config,
"pptx")
check_metrics(names, cert_config, "pptx")
pdf_generated = generate_certificates_for_type(names,
cert_config, "pdf")
check_metrics(names, cert_config, "pdf")
total_certificates = len(names)
print(f"Type: {cert_config['type']} Total: {total_certificates} "
f"PPTX Generated: {pptx_generated} PDF Generated: {pdf_generated}")


except Exception as e:
print(f"Error while running the certificate generation automation:"
f" {e}")
return 1
finally:
if powerpoint:
powerpoint.Quit()

if __name__ == "__main__":
sys.exit(main())