Seed UNS hierarchy across 6 sites; rebrand outputs to SCADA IT/OT with ZB template

Lands per-site UNS subtree files (Warsaw West/North, Shannon, Galway, TMT,
Ponce) seeded from OpenText facility docs — Warsaw split confirmed as
numbered = legacy Zimmer = West, lettered = legacy Biomet = North. Renames
project framing from "Shopfloor IT/OT" to "SCADA IT/OT" for accuracy.
Extracts a ZB-branded PowerPoint template from example_powerpoint.pptx and
wires it into the outputs pipeline. Trims deck from 18 to 16 slides
(BOBJ->Power BI transferred to another team, Non-Goals and Asks dropped);
goal-state BOBJ analysis pruned to a stub.
This commit is contained in:
Joseph Doherty
2026-04-30 10:54:49 -04:00
parent 98bf2d0da4
commit ebc76e9315
46 changed files with 2554 additions and 122 deletions

133
templates/build_template.py Normal file
View File

@@ -0,0 +1,133 @@
"""Build the Zimmer Biomet PowerPoint template from extracted assets.
Reads `assets/cover-background.jpeg` and `assets/bottom-border.jpg`,
emits `zb-template.pptx` with two slides demonstrating the cover and
content layouts. Re-run this script whenever assets change.
"""
from pptx import Presentation
from pptx.util import Inches, Pt, Emu
from pptx.dml.color import RGBColor
from pptx.enum.text import PP_ALIGN
import os
HERE = os.path.dirname(os.path.abspath(__file__))
ASSET = os.path.join(HERE, "assets")
OUT = os.path.join(HERE, "zb-template.pptx")
# Slide-size constants in EMU (English Metric Units; 914400 EMU = 1 inch)
SLIDE_W = Inches(13.33)
SLIDE_H = Inches(7.5)
# Brand colors from the example deck's slideLayout22 / slideLayout33 XML
COVER_TITLE_COLOR = RGBColor(0xF4, 0xF4, 0xF4) # near-white for cover title over dark bg
CONTENT_TITLE_COLOR = RGBColor(0x00, 0x66, 0xB3) # Zimmer blue
SLIDE_NUMBER_COLOR = RGBColor(0x2B, 0x2A, 0x80) # dark navy/purple
CONFIDENTIAL_COLOR = RGBColor(0x40, 0x40, 0x40) # dark gray on the white margin above the border
CONFIDENTIAL_TEXT = "CONFIDENTIAL FOR ZIMMER BIOMET INTERNAL USE ONLY"
def add_textbox(slide, left, top, width, height, text, *, font_size=12,
color=None, bold=False, align=None, font_name="Arial"):
tx = slide.shapes.add_textbox(left, top, width, height)
tf = tx.text_frame
tf.margin_left = tf.margin_right = tf.margin_top = tf.margin_bottom = 0
p = tf.paragraphs[0]
if align is not None:
p.alignment = align
r = p.add_run()
r.text = text
r.font.name = font_name
r.font.size = Pt(font_size)
r.font.bold = bold
if color is not None:
r.font.color.rgb = color
return tx
def build_cover(slide):
"""Cover slide: full-bleed background image + title in upper-left, white text."""
slide.shapes.add_picture(
os.path.join(ASSET, "cover-background.jpeg"),
left=0, top=0, width=SLIDE_W, height=SLIDE_H,
)
add_textbox(
slide,
left=Inches(0.92), top=Inches(1.38),
width=Inches(6.0), height=Inches(1.89),
text="Deck Title",
font_size=28, bold=True, color=COVER_TITLE_COLOR,
)
add_textbox(
slide,
left=Inches(0.92), top=Inches(3.30),
width=Inches(6.0), height=Inches(0.5),
text="Subtitle / date / author",
font_size=14, color=COVER_TITLE_COLOR,
)
def build_content(slide, *, title="Section title", page_number="2"):
"""Content slide: title at top, content area, bottom border + slide number + confidentiality text."""
add_textbox(
slide,
left=Inches(0.92), top=Inches(0.40),
width=Inches(11.5), height=Inches(0.85),
text=title,
font_size=28, bold=True, color=CONTENT_TITLE_COLOR,
)
placeholder = add_textbox(
slide,
left=Inches(0.92), top=Inches(1.50),
width=Inches(11.5), height=Inches(4.80),
text=("Body content goes here.\n"
"• Bullet one\n"
"• Bullet two\n"
"• Bullet three"),
font_size=18, color=RGBColor(0x33, 0x33, 0x33),
)
placeholder.text_frame.word_wrap = True
slide.shapes.add_picture(
os.path.join(ASSET, "bottom-border.jpg"),
left=Inches(0.01), top=Inches(6.76),
width=Inches(13.32), height=Inches(0.74),
)
add_textbox(
slide,
left=Inches(0.29), top=Inches(6.55),
width=Inches(8.0), height=Inches(0.20),
text=CONFIDENTIAL_TEXT,
font_size=8, color=CONFIDENTIAL_COLOR,
)
add_textbox(
slide,
left=Inches(12.85), top=Inches(6.55),
width=Inches(0.40), height=Inches(0.20),
text=page_number,
font_size=10, color=SLIDE_NUMBER_COLOR, align=PP_ALIGN.RIGHT,
)
def main():
prs = Presentation()
prs.slide_width = SLIDE_W
prs.slide_height = SLIDE_H
blank = prs.slide_layouts[6]
cover = prs.slides.add_slide(blank)
build_cover(cover)
content = prs.slides.add_slide(blank)
build_content(content, title="Content slide example", page_number="2")
second_content = prs.slides.add_slide(blank)
build_content(second_content, title="Another content slide", page_number="3")
prs.save(OUT)
print(f"Wrote {OUT}")
if __name__ == "__main__":
main()