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.
134 lines
4.3 KiB
Python
134 lines
4.3 KiB
Python
"""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()
|