diff --git a/certificate_automation/README.md b/certificate_automation/README.md new file mode 100644 index 0000000..14839e9 --- /dev/null +++ b/certificate_automation/README.md @@ -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 +``` diff --git a/certificate_automation/data/input/names/mentees.txt b/certificate_automation/data/input/names/mentees.txt new file mode 100644 index 0000000..7d0cf3b --- /dev/null +++ b/certificate_automation/data/input/names/mentees.txt @@ -0,0 +1,3 @@ +TestFN TestLN +FirstName LastName +TestFN TestLN diff --git a/certificate_automation/data/input/names/mentors.txt b/certificate_automation/data/input/names/mentors.txt new file mode 100644 index 0000000..2ae000a --- /dev/null +++ b/certificate_automation/data/input/names/mentors.txt @@ -0,0 +1,2 @@ +TestFN TestLN +FirstName LastName diff --git a/certificate_automation/requirements.txt b/certificate_automation/requirements.txt new file mode 100644 index 0000000..55e5f7d --- /dev/null +++ b/certificate_automation/requirements.txt @@ -0,0 +1,2 @@ +python-pptx>=0.6.21 +comtypes>=1.1.14 diff --git a/certificate_automation/src/__init__.py b/certificate_automation/src/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/certificate_automation/src/config.json b/certificate_automation/src/config.json new file mode 100644 index 0000000..4f435c3 --- /dev/null +++ b/certificate_automation/src/config.json @@ -0,0 +1,24 @@ +{ + "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": "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 + } + ] +} diff --git a/certificate_automation/src/generate_certificates.py b/certificate_automation/src/generate_certificates.py new file mode 100644 index 0000000..e6844c8 --- /dev/null +++ b/certificate_automation/src/generate_certificates.py @@ -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())