The generator is one Python file, roughly 470 lines of python-pptx, and it built the final presentation for the auditing project from part 1. This post is about the two things in it worth stealing and the one bug worth laughing at.
Wiping slides without leaving corpses#
python-pptx will happily open the official template as a presentation. The sample slides then need to go, and this is where the subtlety lives:
def wipe_slides(prs):
lst = prs.slides._sldIdLst
for sldId in list(lst):
prs.part.drop_rel(sldId.get(qn("r:id"))) # drop rel too, or you get orphan parts
lst.remove(sldId)A .pptx is a zip of XML parts wired together by relationships. Removing a slide’s id from the slide list makes it disappear from the deck, but the underlying part is still referenced by a relationship from the presentation part. Skip the drop_rel line and the file still opens, which is the trap: you ship a package carrying orphaned parts, and whether anything downstream chokes on them is up to tooling you do not control. Drop the relationship and the id together and the package stays clean.
What survives the wipe is the point. Slide masters and layouts are separate parts, untouched by this loop. Every slide I add afterwards goes onto an official layout, which means the background art, the footer, the logo placement, and the typography all come from the university’s own files. Nothing to sample, nothing to eyeball, nothing to drift out of date when the brand team ships a new template. Load, wipe, rebuild.
Colors from the theme, not from the eyedropper#
The template’s theme defines its palette, so the constants in my file are transcriptions with their theme slots noted, not guesses from a color picker:
NAVY = RGBColor(0x00, 0x30, 0x57) # Tech Navy (dk1)
GOLD = RGBColor(0xB3, 0xA3, 0x69) # Tech Gold (accent1)
GREY = RGBColor(0x54, 0x58, 0x59) # Grey Matter (dk2)
BODYFONT = "Roboto"
HEADFONT = "Roboto Slab"The comments carry the important part. When a shape needs explicit coloring, the value traces back to a named slot in the official theme, so a reviewer can check it and a future me can update it.
The eight-inch hexagon gap#
Now the bug. Content slides lay out a row of hexagons, and the gap between them was computed from the full slide width:
# before (buggy): with n=2 this gives gap = 8.03 inches
gap = (13.33 - 0.9 - n * w) / max(n - 1, 1) if n > 1 else 0The formula spreads n hexagons across the entire 13.33 inch slide. With five hexagons that looks intentional. With two, the leftover width all becomes gap: 8.03 inches of it, so the two hexagons were flung to opposite edges of the slide with a hole in the middle, like a divorce rendered in vector graphics. The layout code was not wrong for the case I wrote it for. It was wrong for the case I never tested.

That render is real: for this post I re-ran the generator with the buggy formula restored and two placeholder stats, and the template did exactly what it did in June.
The fix abandons full-width justification for a fixed gap with true centering, and draws the hexagons larger when there are only one or two, since a sparse row can afford the space:
# after
w, h, y = (2.9, 2.5, 2.35) if n <= 2 else (2.2, 1.95, 2.55)
gap = 0.7
x0 = (13.33 - (n * w + gap * (n - 1))) / 2
Same slide source, current code. Justified layout distributes leftover space, and leftover space grows as items shrink in number. Any layout formula that divides remaining width by item count has this failure mode waiting at n=2 and n=1. Centered layout with a fixed gap degrades gracefully instead.
Lessons#
- Inherit branding, never imitate it. Load the official file, wipe its slides, keep its masters, and build on its layouts.
- When you remove a part from an OOXML package, remove its relationship in the same breath. A file that still opens is not evidence the package is clean.
- Hardcoded colors are debt unless each one names the theme slot it came from.
- Justified layouts fail at low item counts because leftover space has nowhere to go but the gaps. Test layout math at n=1 and n=2, not only at the count in your mockup.
That closes the series. The auditor, the harness rules, the degenerate kappa, the failed sabotage, and the deck it was all presented on. The run records stay stamped with model and date, so when someone reruns these problems against next year’s models, disagreement with my numbers will be a finding and not a contradiction.
References#
- python-pptx documentation (presentations, slide masters, and layout objects)
- Code shown above:
gt-slides/build_deck.pyin the project repo, excerpted verbatim
