Certificate Generation with Python
The past two days have been quite challenging.
After hours of effort yesterday, I began to doubt whether I could complete the task assigned to me.
It seemed like there was no way out.
The task seemed simple at first: I had a Google Sheet with 158 participants, a certificate in the form of a Word file, and I needed to send emails using Outlook.
Contents
Initial Setup and Challenges with Outlook
Sounds easy, right?
I spent almost 2-3 hours trying to configure Outlook using my university’s email ID.
Unfortunately, it didn’t work out due to some barriers, possibly related to app password generation, but I wasn’t sure why.
Later, I tried the setup on my own PC, only to discover that Word and Outlook are paid applications for Windows 11.
I had to complete the task by Monday, and yesterday was Saturday. So, I needed to find an alternative.
I experimented with Google Sheets’ Apps Script, but ran into another issue:
“being a Google product, it didn’t work well with Word. The certificates were generated, but all showed <<Full Name>> instead of the actual names.”
Exploring Alternative Solutions: Python and Google Sheets
I then tried using Python, integrating several Google Cloud APIs, only to realize that I was essentially doing the same thing but in a different way.
Frustrated, I decided to call it a day and went to sleep.
Today, I had to prepare for my exams, so I focused on that first.
A Fresh Start: Tackling the Problem with Python
After I was done studying, I returned to the task with a fresh mind.
I realized that Python was still my best option, so I continued working with various word-related modules.
Unfortunately, none of them allowed me to modify the Word file due to its complex design.
Then, I remembered that I had the certificate template in image format as well. This seemed like a promising solution.
Generating Certificates with Python
I extracted the participants’ names from the Google Sheet and used the following Python code to generate the certificates:
Script 👇 (Click for Code)
from PIL import Image, ImageDraw, ImageFont
import os
# Load the certificate template
template_path = 'certificate_template.jpg'
template = Image.open(template_path)
# Set up font and text position
font_path = 'font file path/arial.ttf' # Path to the font file
font_size = 50 # Adjust the size as needed
font = ImageFont.truetype(font_path, font_size)
text_color = (50, 50, 200) # Color of the text
# Coordinates for the text (adjust these based on your template)
name_x, name_y = 200, 320
# Load participant names
# All The participant names were in this list.
# Can easily generate using pandas
participants = ["ZZZ", "XXX", "YYY"]
# Output directory
output_dir = "certificates"
os.makedirs(output_dir, exist_ok=True)
# Generate certificates
for name in participants:
# Make a copy of the template
cert = template.copy()
draw = ImageDraw.Draw(cert)
# Add the participant's name
draw.text((name_x, name_y), name, fill=text_color, font=font)
# Save the certificate
output_path = os.path.join(output_dir, f"{name}.jpg")
cert.save(output_path)
print(f"Certificate generated for: {name}")
print("All certificates have been generated.")
Then I uploaded these certificates to the Google Drive folder.
And finally, went onto mail them.
This app script was used for that purpose.
Script 👇 (Click for Code)
function sendCertificates() {
// Spreadsheet information
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Form Responses 1");
var data = sheet.getDataRange().getValues();
// Google Drive folder ID where certificates are stored
var folderId = "folder_id"; // Replace with your actual folder ID
var folder = DriveApp.getFolderById(folderId);
// Iterate over rows in the sheet
for (var i = 1; i < data.length; i++) { // Start at 1 to skip the header row
var email = data[i][1]; // B column - Email Address
var name = data[i][2]; // C column - Full Name
var subject = "Participation Certificate";
var body = "Hello " + name + ",\n\n" +
"Congratulations on successfully <<complete mail..>>";
// Search for the certificate file by name within the folder
var files = folder.getFilesByName(name + ".jpg"); // Adjust extension if needed
if (files.hasNext()) {
var file = files.next();
MailApp.sendEmail({
to: email,
subject: subject,
body: body,
attachments: [file.getAs(MimeType.JPEG)] // Adjust MimeType if using a different file format
});
Logger.log("Email sent to: " + email);
} else {
Logger.log("Certificate not found for: " + name);
}
}
}
And due to some naming inconsistencies only 73 out of the total mails were sent.
Finalizing the Task: Overcoming Naming Inconsistencies
My Failed Efforts. 👇 (Click for Code)
function deleteRowsBasedOnNames() {
// The list of names you want to keep
const namesToKeep = [];
// Convert namesToKeep to lowercase for case-insensitive comparison
const namesToKeepLower = namesToKeep.map(name => name.toLowerCase());
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Copy");
const range = sheet.getRange("C:C"); // Assuming names are in column C
const values = range.getValues();
let rowsDeleted = 0;
// Iterate over the rows and delete rows not in the list
for (let i = values.length - 1; i >= 0; i--) {
if (values[i][0]) {
const nameLower = values[i][0].toLowerCase();
if (!namesToKeepLower.includes(nameLower)) {
sheet.deleteRow(i + 1);
rowsDeleted++;
}
}
}
// Log the number of rows deleted
Logger.log(`Total rows deleted: ${rowsDeleted}`);
}
This removed the entire sheet.
Then I handpicked the ones left after a lot of programmatical errors. (I tried almost 10 iterations on the above code)
Finally using this script I mailed the rest handpicked ones.
Script 👇 (Click for Code)
function sendCertificates() {
// Spreadsheet information
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet2");
var data = sheet.getDataRange().getValues();
// Google Drive folder ID where certificates are stored
var folderId = "folder_id"; // Replace with your actual folder ID
var folder = DriveApp.getFolderById(folderId);
// Iterate over rows in the sheet
for (var i = 1; i < data.length; i++) { // Start at 1 to skip the header row
var email = data[i][1]; // B column - Email Address
var name = data[i][2].trim().replace(/\s+/g, ' '); // C column - Full Name (trimmed and spaces normalized)
var subject = "Participation Certificate ";
var body = "Hello " + name + ",\n\n" +
"Congratulations on successfully <<complete mail..>>";
// Search for the certificate file by name within the folder
var files = folder.getFiles();
var fileFound = false;
while (files.hasNext()) {
var file = files.next();
var fileName = file.getName().trim().replace(/\s+/g, ' ').toLowerCase();
if (fileName === name.toLowerCase() + ".jpg") { // Adjust extension if needed
MailApp.sendEmail({
to: email,
subject: subject,
body: body,
attachments: [file.getAs(MimeType.JPEG)] // Adjust MimeType if using a different file format
});
Logger.log("Email sent to: " + email);
fileFound = true;
break;
}
}
if (!fileFound) {
Logger.log("Certificate not found for: " + name);
}
}
}
Lessons Learned: The Importance of Flexibility in Programming
And, the Task is successfully completed.
I learned a lot along the way, especially that no single tool, technology, or language is ever enough. As a programmer, you need to be flexible and adapt to different tools and techniques.
It was a fun and productive weekend for me. How was yours?