many revisions, getting closer
This commit is contained in:
771
The_Stories_Our_Data_Tells/create_presentation.py
Normal file
771
The_Stories_Our_Data_Tells/create_presentation.py
Normal file
@@ -0,0 +1,771 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Create PowerPoint: The Stories Our Data Tells
|
||||
How DTS Helps Save Lives - 45 minute internal presentation
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Add parent directory to path for shared assets
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
from pptx import Presentation
|
||||
from pptx.util import Inches, Pt
|
||||
from pptx.dml.color import RGBColor
|
||||
from pptx.enum.text import PP_ALIGN, MSO_ANCHOR
|
||||
from pptx.enum.shapes import MSO_SHAPE
|
||||
|
||||
# Create presentation with widescreen dimensions
|
||||
prs = Presentation()
|
||||
prs.slide_width = Inches(13.333)
|
||||
prs.slide_height = Inches(7.5)
|
||||
|
||||
# Color scheme - DTS-inspired professional blue
|
||||
DARK_BLUE = RGBColor(0, 48, 87) # Deep navy
|
||||
ACCENT_BLUE = RGBColor(0, 120, 174) # Bright blue
|
||||
LIGHT_BLUE = RGBColor(173, 216, 230) # Light accent
|
||||
DARK_GRAY = RGBColor(51, 51, 51)
|
||||
LIGHT_GRAY = RGBColor(245, 245, 245)
|
||||
WHITE = RGBColor(255, 255, 255)
|
||||
ACCENT_ORANGE = RGBColor(230, 126, 34)
|
||||
HIGHLIGHT_RED = RGBColor(192, 57, 43)
|
||||
|
||||
|
||||
def add_title_slide(title, subtitle=""):
|
||||
"""Full-bleed title slide"""
|
||||
slide = prs.slides.add_slide(prs.slide_layouts[6])
|
||||
|
||||
# Dark blue background
|
||||
bg = slide.shapes.add_shape(
|
||||
MSO_SHAPE.RECTANGLE, 0, 0, prs.slide_width, prs.slide_height
|
||||
)
|
||||
bg.fill.solid()
|
||||
bg.fill.fore_color.rgb = DARK_BLUE
|
||||
bg.line.fill.background()
|
||||
|
||||
# Title
|
||||
title_box = slide.shapes.add_textbox(
|
||||
Inches(0.5), Inches(2.8), Inches(12.333), Inches(1.5)
|
||||
)
|
||||
tf = title_box.text_frame
|
||||
p = tf.paragraphs[0]
|
||||
p.text = title
|
||||
p.font.size = Pt(60)
|
||||
p.font.bold = True
|
||||
p.font.color.rgb = WHITE
|
||||
p.alignment = PP_ALIGN.CENTER
|
||||
|
||||
if subtitle:
|
||||
sub_box = slide.shapes.add_textbox(
|
||||
Inches(0.5), Inches(4.5), Inches(12.333), Inches(0.8)
|
||||
)
|
||||
tf = sub_box.text_frame
|
||||
p = tf.paragraphs[0]
|
||||
p.text = subtitle
|
||||
p.font.size = Pt(28)
|
||||
p.font.color.rgb = LIGHT_BLUE
|
||||
p.alignment = PP_ALIGN.CENTER
|
||||
|
||||
return slide
|
||||
|
||||
|
||||
def add_section_slide(title, subtitle=""):
|
||||
"""Section divider with accent bar"""
|
||||
slide = prs.slides.add_slide(prs.slide_layouts[6])
|
||||
|
||||
# Left accent bar
|
||||
bar = slide.shapes.add_shape(
|
||||
MSO_SHAPE.RECTANGLE, 0, 0, Inches(0.4), prs.slide_height
|
||||
)
|
||||
bar.fill.solid()
|
||||
bar.fill.fore_color.rgb = ACCENT_BLUE
|
||||
bar.line.fill.background()
|
||||
|
||||
# Section title
|
||||
title_box = slide.shapes.add_textbox(Inches(1), Inches(2.5), Inches(11), Inches(2))
|
||||
tf = title_box.text_frame
|
||||
p = tf.paragraphs[0]
|
||||
p.text = title
|
||||
p.font.size = Pt(48)
|
||||
p.font.bold = True
|
||||
p.font.color.rgb = DARK_BLUE
|
||||
|
||||
if subtitle:
|
||||
p = tf.add_paragraph()
|
||||
p.text = subtitle
|
||||
p.font.size = Pt(24)
|
||||
p.font.color.rgb = DARK_GRAY
|
||||
|
||||
return slide
|
||||
|
||||
|
||||
def add_stat_slide(number, description, context=""):
|
||||
"""Big number impact slide"""
|
||||
slide = prs.slides.add_slide(prs.slide_layouts[6])
|
||||
|
||||
# Number
|
||||
num_box = slide.shapes.add_textbox(
|
||||
Inches(0.5), Inches(2), Inches(12.333), Inches(2.5)
|
||||
)
|
||||
tf = num_box.text_frame
|
||||
p = tf.paragraphs[0]
|
||||
p.text = number
|
||||
p.font.size = Pt(144)
|
||||
p.font.bold = True
|
||||
p.font.color.rgb = ACCENT_BLUE
|
||||
p.alignment = PP_ALIGN.CENTER
|
||||
|
||||
# Description
|
||||
desc_box = slide.shapes.add_textbox(
|
||||
Inches(0.5), Inches(4.5), Inches(12.333), Inches(1)
|
||||
)
|
||||
tf = desc_box.text_frame
|
||||
p = tf.paragraphs[0]
|
||||
p.text = description
|
||||
p.font.size = Pt(32)
|
||||
p.font.color.rgb = DARK_GRAY
|
||||
p.alignment = PP_ALIGN.CENTER
|
||||
|
||||
if context:
|
||||
ctx_box = slide.shapes.add_textbox(
|
||||
Inches(1), Inches(5.8), Inches(11.333), Inches(1)
|
||||
)
|
||||
tf = ctx_box.text_frame
|
||||
p = tf.paragraphs[0]
|
||||
p.text = context
|
||||
p.font.size = Pt(18)
|
||||
p.font.italic = True
|
||||
p.font.color.rgb = DARK_GRAY
|
||||
p.alignment = PP_ALIGN.CENTER
|
||||
|
||||
return slide
|
||||
|
||||
|
||||
def add_quote_slide(quote, attribution=""):
|
||||
"""Quote slide with large text"""
|
||||
slide = prs.slides.add_slide(prs.slide_layouts[6])
|
||||
|
||||
# Quote
|
||||
quote_box = slide.shapes.add_textbox(
|
||||
Inches(1), Inches(2), Inches(11.333), Inches(3)
|
||||
)
|
||||
tf = quote_box.text_frame
|
||||
tf.word_wrap = True
|
||||
p = tf.paragraphs[0]
|
||||
p.text = f'"{quote}"'
|
||||
p.font.size = Pt(36)
|
||||
p.font.italic = True
|
||||
p.font.color.rgb = DARK_BLUE
|
||||
p.alignment = PP_ALIGN.CENTER
|
||||
|
||||
if attribution:
|
||||
attr_box = slide.shapes.add_textbox(
|
||||
Inches(1), Inches(5.5), Inches(11.333), Inches(0.5)
|
||||
)
|
||||
tf = attr_box.text_frame
|
||||
p = tf.paragraphs[0]
|
||||
p.text = f"- {attribution}"
|
||||
p.font.size = Pt(20)
|
||||
p.font.color.rgb = DARK_GRAY
|
||||
p.alignment = PP_ALIGN.CENTER
|
||||
|
||||
return slide
|
||||
|
||||
|
||||
def add_content_slide(title, bullets, subtitle=""):
|
||||
"""Standard content slide with bullets"""
|
||||
slide = prs.slides.add_slide(prs.slide_layouts[6])
|
||||
|
||||
# Header bar
|
||||
header = slide.shapes.add_shape(
|
||||
MSO_SHAPE.RECTANGLE, 0, 0, prs.slide_width, Inches(1.2)
|
||||
)
|
||||
header.fill.solid()
|
||||
header.fill.fore_color.rgb = DARK_BLUE
|
||||
header.line.fill.background()
|
||||
|
||||
# Title
|
||||
title_box = slide.shapes.add_textbox(
|
||||
Inches(0.5), Inches(0.25), Inches(12), Inches(0.9)
|
||||
)
|
||||
tf = title_box.text_frame
|
||||
p = tf.paragraphs[0]
|
||||
p.text = title
|
||||
p.font.size = Pt(36)
|
||||
p.font.bold = True
|
||||
p.font.color.rgb = WHITE
|
||||
|
||||
# Subtitle in header
|
||||
if subtitle:
|
||||
sub_box = slide.shapes.add_textbox(
|
||||
Inches(0.5), Inches(0.75), Inches(12), Inches(0.4)
|
||||
)
|
||||
tf = sub_box.text_frame
|
||||
p = tf.paragraphs[0]
|
||||
p.text = subtitle
|
||||
p.font.size = Pt(16)
|
||||
p.font.color.rgb = LIGHT_BLUE
|
||||
|
||||
# Bullets
|
||||
bullet_box = slide.shapes.add_textbox(
|
||||
Inches(0.8), Inches(1.6), Inches(11.5), Inches(5.5)
|
||||
)
|
||||
tf = bullet_box.text_frame
|
||||
tf.word_wrap = True
|
||||
|
||||
for i, bullet in enumerate(bullets):
|
||||
if i == 0:
|
||||
p = tf.paragraphs[0]
|
||||
else:
|
||||
p = tf.add_paragraph()
|
||||
p.text = f" {bullet}"
|
||||
p.font.size = Pt(24)
|
||||
p.font.color.rgb = DARK_GRAY
|
||||
p.space_after = Pt(18)
|
||||
|
||||
return slide
|
||||
|
||||
|
||||
def add_two_column_slide(title, left_title, left_items, right_title, right_items):
|
||||
"""Two column comparison slide"""
|
||||
slide = prs.slides.add_slide(prs.slide_layouts[6])
|
||||
|
||||
# Header
|
||||
header = slide.shapes.add_shape(
|
||||
MSO_SHAPE.RECTANGLE, 0, 0, prs.slide_width, Inches(1.0)
|
||||
)
|
||||
header.fill.solid()
|
||||
header.fill.fore_color.rgb = DARK_BLUE
|
||||
header.line.fill.background()
|
||||
|
||||
title_box = slide.shapes.add_textbox(
|
||||
Inches(0.5), Inches(0.2), Inches(12), Inches(0.7)
|
||||
)
|
||||
tf = title_box.text_frame
|
||||
p = tf.paragraphs[0]
|
||||
p.text = title
|
||||
p.font.size = Pt(32)
|
||||
p.font.bold = True
|
||||
p.font.color.rgb = WHITE
|
||||
|
||||
# Left column title
|
||||
left_title_box = slide.shapes.add_textbox(
|
||||
Inches(0.5), Inches(1.3), Inches(6), Inches(0.5)
|
||||
)
|
||||
tf = left_title_box.text_frame
|
||||
p = tf.paragraphs[0]
|
||||
p.text = left_title
|
||||
p.font.size = Pt(22)
|
||||
p.font.bold = True
|
||||
p.font.color.rgb = ACCENT_BLUE
|
||||
|
||||
# Left items
|
||||
left_box = slide.shapes.add_textbox(
|
||||
Inches(0.5), Inches(1.9), Inches(5.8), Inches(5)
|
||||
)
|
||||
tf = left_box.text_frame
|
||||
tf.word_wrap = True
|
||||
for i, item in enumerate(left_items):
|
||||
if i == 0:
|
||||
p = tf.paragraphs[0]
|
||||
else:
|
||||
p = tf.add_paragraph()
|
||||
p.text = f" {item}"
|
||||
p.font.size = Pt(18)
|
||||
p.font.color.rgb = DARK_GRAY
|
||||
p.space_after = Pt(10)
|
||||
|
||||
# Divider line
|
||||
divider = slide.shapes.add_shape(
|
||||
MSO_SHAPE.RECTANGLE, Inches(6.5), Inches(1.3), Inches(0.02), Inches(5.5)
|
||||
)
|
||||
divider.fill.solid()
|
||||
divider.fill.fore_color.rgb = LIGHT_BLUE
|
||||
divider.line.fill.background()
|
||||
|
||||
# Right column title
|
||||
right_title_box = slide.shapes.add_textbox(
|
||||
Inches(6.8), Inches(1.3), Inches(6), Inches(0.5)
|
||||
)
|
||||
tf = right_title_box.text_frame
|
||||
p = tf.paragraphs[0]
|
||||
p.text = right_title
|
||||
p.font.size = Pt(22)
|
||||
p.font.bold = True
|
||||
p.font.color.rgb = ACCENT_BLUE
|
||||
|
||||
# Right items
|
||||
right_box = slide.shapes.add_textbox(Inches(6.8), Inches(1.9), Inches(6), Inches(5))
|
||||
tf = right_box.text_frame
|
||||
tf.word_wrap = True
|
||||
for i, item in enumerate(right_items):
|
||||
if i == 0:
|
||||
p = tf.paragraphs[0]
|
||||
else:
|
||||
p = tf.add_paragraph()
|
||||
p.text = f" {item}"
|
||||
p.font.size = Pt(18)
|
||||
p.font.color.rgb = DARK_GRAY
|
||||
p.space_after = Pt(10)
|
||||
|
||||
return slide
|
||||
|
||||
|
||||
def add_story_headline_slide(headline, subtext=""):
|
||||
"""Newspaper-style headline slide"""
|
||||
slide = prs.slides.add_slide(prs.slide_layouts[6])
|
||||
|
||||
# Light gray "newspaper" background
|
||||
bg = slide.shapes.add_shape(
|
||||
MSO_SHAPE.RECTANGLE, Inches(0.5), Inches(1.5), Inches(12.333), Inches(4.5)
|
||||
)
|
||||
bg.fill.solid()
|
||||
bg.fill.fore_color.rgb = LIGHT_GRAY
|
||||
bg.line.color.rgb = DARK_GRAY
|
||||
|
||||
# Headline
|
||||
headline_box = slide.shapes.add_textbox(
|
||||
Inches(1), Inches(2.5), Inches(11.333), Inches(2)
|
||||
)
|
||||
tf = headline_box.text_frame
|
||||
tf.word_wrap = True
|
||||
p = tf.paragraphs[0]
|
||||
p.text = headline
|
||||
p.font.size = Pt(44)
|
||||
p.font.bold = True
|
||||
p.font.color.rgb = DARK_GRAY
|
||||
p.alignment = PP_ALIGN.CENTER
|
||||
|
||||
if subtext:
|
||||
sub_box = slide.shapes.add_textbox(
|
||||
Inches(1), Inches(4.8), Inches(11.333), Inches(0.8)
|
||||
)
|
||||
tf = sub_box.text_frame
|
||||
p = tf.paragraphs[0]
|
||||
p.text = subtext
|
||||
p.font.size = Pt(24)
|
||||
p.font.color.rgb = DARK_GRAY
|
||||
p.alignment = PP_ALIGN.CENTER
|
||||
|
||||
return slide
|
||||
|
||||
|
||||
def add_graph_placeholder_slide(title, graph_description, caption=""):
|
||||
"""Slide with placeholder for graph/chart"""
|
||||
slide = prs.slides.add_slide(prs.slide_layouts[6])
|
||||
|
||||
# Header
|
||||
header = slide.shapes.add_shape(
|
||||
MSO_SHAPE.RECTANGLE, 0, 0, prs.slide_width, Inches(1.0)
|
||||
)
|
||||
header.fill.solid()
|
||||
header.fill.fore_color.rgb = DARK_BLUE
|
||||
header.line.fill.background()
|
||||
|
||||
title_box = slide.shapes.add_textbox(
|
||||
Inches(0.5), Inches(0.2), Inches(12), Inches(0.7)
|
||||
)
|
||||
tf = title_box.text_frame
|
||||
p = tf.paragraphs[0]
|
||||
p.text = title
|
||||
p.font.size = Pt(32)
|
||||
p.font.bold = True
|
||||
p.font.color.rgb = WHITE
|
||||
|
||||
# Graph placeholder
|
||||
graph = slide.shapes.add_shape(
|
||||
MSO_SHAPE.RECTANGLE, Inches(1), Inches(1.5), Inches(11.333), Inches(4.5)
|
||||
)
|
||||
graph.fill.solid()
|
||||
graph.fill.fore_color.rgb = LIGHT_GRAY
|
||||
graph.line.color.rgb = ACCENT_BLUE
|
||||
|
||||
# Graph description
|
||||
desc_box = slide.shapes.add_textbox(
|
||||
Inches(1), Inches(3.3), Inches(11.333), Inches(1)
|
||||
)
|
||||
tf = desc_box.text_frame
|
||||
p = tf.paragraphs[0]
|
||||
p.text = f"[{graph_description}]"
|
||||
p.font.size = Pt(24)
|
||||
p.font.color.rgb = DARK_GRAY
|
||||
p.alignment = PP_ALIGN.CENTER
|
||||
|
||||
if caption:
|
||||
cap_box = slide.shapes.add_textbox(
|
||||
Inches(1), Inches(6.3), Inches(11.333), Inches(0.8)
|
||||
)
|
||||
tf = cap_box.text_frame
|
||||
p = tf.paragraphs[0]
|
||||
p.text = caption
|
||||
p.font.size = Pt(18)
|
||||
p.font.italic = True
|
||||
p.font.color.rgb = DARK_GRAY
|
||||
p.alignment = PP_ALIGN.CENTER
|
||||
|
||||
return slide
|
||||
|
||||
|
||||
def add_connection_slide(title, connections):
|
||||
"""Show flow/connection between concepts"""
|
||||
slide = prs.slides.add_slide(prs.slide_layouts[6])
|
||||
|
||||
# Header
|
||||
header = slide.shapes.add_shape(
|
||||
MSO_SHAPE.RECTANGLE, 0, 0, prs.slide_width, Inches(1.0)
|
||||
)
|
||||
header.fill.solid()
|
||||
header.fill.fore_color.rgb = DARK_BLUE
|
||||
header.line.fill.background()
|
||||
|
||||
title_box = slide.shapes.add_textbox(
|
||||
Inches(0.5), Inches(0.2), Inches(12), Inches(0.7)
|
||||
)
|
||||
tf = title_box.text_frame
|
||||
p = tf.paragraphs[0]
|
||||
p.text = title
|
||||
p.font.size = Pt(32)
|
||||
p.font.bold = True
|
||||
p.font.color.rgb = WHITE
|
||||
|
||||
# Connection boxes
|
||||
num_items = len(connections)
|
||||
box_width = Inches(2.2)
|
||||
spacing = (prs.slide_width - box_width * num_items) / (num_items + 1)
|
||||
|
||||
for i, item in enumerate(connections):
|
||||
x = spacing + i * (box_width + spacing)
|
||||
|
||||
# Box
|
||||
box = slide.shapes.add_shape(
|
||||
MSO_SHAPE.ROUNDED_RECTANGLE, x, Inches(3), box_width, Inches(1.5)
|
||||
)
|
||||
box.fill.solid()
|
||||
box.fill.fore_color.rgb = ACCENT_BLUE if i % 2 == 0 else DARK_BLUE
|
||||
box.line.fill.background()
|
||||
|
||||
# Text
|
||||
text_box = slide.shapes.add_textbox(x, Inches(3.3), box_width, Inches(1))
|
||||
tf = text_box.text_frame
|
||||
tf.word_wrap = True
|
||||
p = tf.paragraphs[0]
|
||||
p.text = item
|
||||
p.font.size = Pt(16)
|
||||
p.font.bold = True
|
||||
p.font.color.rgb = WHITE
|
||||
p.alignment = PP_ALIGN.CENTER
|
||||
|
||||
# Arrow (except for last)
|
||||
if i < num_items - 1:
|
||||
arrow = slide.shapes.add_shape(
|
||||
MSO_SHAPE.RIGHT_ARROW,
|
||||
x + box_width + Inches(0.1),
|
||||
Inches(3.5),
|
||||
spacing - Inches(0.2),
|
||||
Inches(0.5),
|
||||
)
|
||||
arrow.fill.solid()
|
||||
arrow.fill.fore_color.rgb = LIGHT_BLUE
|
||||
arrow.line.fill.background()
|
||||
|
||||
return slide
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# BUILD THE PRESENTATION
|
||||
# ============================================================================
|
||||
|
||||
# Slide 1: Title
|
||||
add_title_slide("The Stories Our Data Tells", "How DTS Helps Save Lives")
|
||||
|
||||
# Slide 2: The Number That Matters
|
||||
add_stat_slide(
|
||||
"117,000",
|
||||
"Lives saved every year in the U.S. alone",
|
||||
"Compared to 1970 fatality rates - a 77% reduction per mile driven",
|
||||
)
|
||||
|
||||
# Slide 3: Quote - Crashed Car
|
||||
add_quote_slide(
|
||||
"A crashed car is destroyed in milliseconds. The data is what remains.",
|
||||
"The foundation of safety engineering",
|
||||
)
|
||||
|
||||
# Slide 4: Section - Part 1
|
||||
add_section_slide("PART 1", "The Data Behind the Headlines")
|
||||
|
||||
# Slide 5: Story 1 - Airbag Headline
|
||||
add_story_headline_slide(
|
||||
"Airbag Kills Child in Minor Crash",
|
||||
"1990s Airbag Crisis - 175+ deaths from safety devices",
|
||||
)
|
||||
|
||||
# Slide 6: What the Data Revealed
|
||||
add_content_slide(
|
||||
"What the Data Revealed",
|
||||
[
|
||||
"Early airbags designed for one scenario only:",
|
||||
" Unbelted 50th percentile male in 30 mph crash",
|
||||
"Inflation speed: ~200 mph",
|
||||
"Actual occupants hit: Children, small women, close-sitters",
|
||||
"Neck loads in small dummies: Off the charts",
|
||||
"Data from out-of-position testing showed fatal forces",
|
||||
],
|
||||
"Instrumented testing exposed the deadly design flaw",
|
||||
)
|
||||
|
||||
# Slide 7: Engineering Response
|
||||
add_content_slide(
|
||||
"The Engineering Response",
|
||||
[
|
||||
"1998: Immediate depowering (20-35% force reduction)",
|
||||
"Dual-stage inflators: Variable deployment force",
|
||||
"Occupant classification: Weight sensors in seats",
|
||||
"Suppression systems: Turn off for rear-facing child seats",
|
||||
"Multi-stage deployment: Match force to crash severity",
|
||||
"Result: Deaths dropped from 50+/year to near zero",
|
||||
],
|
||||
"Every solution required new testing with instrumented dummies",
|
||||
)
|
||||
|
||||
# Slide 8: DTS Connection - Airbags
|
||||
add_content_slide(
|
||||
"The DTS Connection",
|
||||
[
|
||||
"OOP testing requires precise measurements in small packages",
|
||||
"Forces on child dummy necks",
|
||||
"Acceleration of infant heads",
|
||||
"Chest deflection of 5th percentile female",
|
||||
"DTS accelerometers in CRABI infant dummy",
|
||||
"DTS DAQ captures millisecond-by-millisecond events",
|
||||
],
|
||||
"Without this data: No depowering. No dual-stage. More dead children.",
|
||||
)
|
||||
|
||||
# Slide 9: Story 2 - Small Overlap
|
||||
add_story_headline_slide(
|
||||
"Luxury Cars Fail New Crash Test",
|
||||
"2012 Small Overlap Shock - Premium brands exposed",
|
||||
)
|
||||
|
||||
# Slide 10: The Results
|
||||
add_two_column_slide(
|
||||
"The 2012 Small Overlap Results",
|
||||
"FAILED (Marginal/Poor)",
|
||||
[
|
||||
"BMW 5-Series: Marginal",
|
||||
"Mercedes C-Class: Marginal",
|
||||
"Audi A4: Marginal",
|
||||
"Lexus ES: Poor",
|
||||
"Volkswagen Passat: Marginal",
|
||||
"",
|
||||
"Only 3 of 13 midsize cars",
|
||||
"earned 'Good' rating",
|
||||
],
|
||||
"WHY THEY FAILED",
|
||||
[
|
||||
"Structure bypassed main rails",
|
||||
"Wheels pushed into footwell",
|
||||
"Firewalls collapsed",
|
||||
"Steering columns displaced",
|
||||
"Feet trapped in crushed metal",
|
||||
"",
|
||||
"The dummies told the story:",
|
||||
"HIC exceeded limits, leg loads critical",
|
||||
],
|
||||
)
|
||||
|
||||
# Slide 11: Industry Response
|
||||
add_content_slide(
|
||||
"The Industry Response",
|
||||
[
|
||||
"Fundamental redesign of front structures",
|
||||
"Small overlap-specific load paths added",
|
||||
"Extended bumper beams",
|
||||
"Reinforced A-pillars",
|
||||
"Modified wheel well geometry",
|
||||
"By 2015: Most vehicles earning 'Good' ratings",
|
||||
],
|
||||
"Every redesign validated with crash testing and instrumented dummies",
|
||||
)
|
||||
|
||||
# Slide 12: Story 3 - Football
|
||||
add_story_headline_slide(
|
||||
"The Sport That Was Killing Players",
|
||||
"1960s-70s: 32 football deaths per year from head injuries",
|
||||
)
|
||||
|
||||
# Slide 13: NOCSAE Solution
|
||||
add_content_slide(
|
||||
"The NOCSAE Solution",
|
||||
[
|
||||
"1969: National Operating Committee formed",
|
||||
"Developed the drop test: Helmeted headform from 60 inches",
|
||||
"Instrumented with accelerometers",
|
||||
"Metric: Severity Index (SI) < 1200",
|
||||
"Simple. Repeatable. Science-based.",
|
||||
"Standard mandated in 1980",
|
||||
],
|
||||
"A measurement standard that would actually prevent deaths",
|
||||
)
|
||||
|
||||
# Slide 14: Football Results
|
||||
add_graph_placeholder_slide(
|
||||
"Football Fatalities: Before and After",
|
||||
"Graph: Deaths per year - 32 (1970) dropping to 8 (1980) to 3 (2020)",
|
||||
"90% reduction in fatalities - from helmets validated with instrumented testing",
|
||||
)
|
||||
|
||||
# Slide 15: Section - Part 2
|
||||
add_section_slide("PART 2", "Customers You Didn't Know You Had")
|
||||
|
||||
# Slide 16: Surprising Applications Overview
|
||||
add_content_slide(
|
||||
"DTS Equipment: Beyond Automotive",
|
||||
[
|
||||
"Military: WIAMan dummy for IED underbody blast",
|
||||
"Space: Astronaut protection during landing",
|
||||
"Theme Parks: Roller coaster acceleration limits",
|
||||
"Medical Devices: Implant impact testing",
|
||||
"Sports: Helmet certification across all sports",
|
||||
"Research: University biomechanics labs worldwide",
|
||||
],
|
||||
"Same measurement principles - different life-saving applications",
|
||||
)
|
||||
|
||||
# Slide 17: WIAMan
|
||||
add_content_slide(
|
||||
"Military: The WIAMan Dummy",
|
||||
[
|
||||
"IEDs send blast forces up through vehicle floors",
|
||||
"Traditional dummies designed for horizontal impact",
|
||||
"WIAMan: Warrior Injury Assessment Manikin",
|
||||
"Specifically designed for underbody blast",
|
||||
"DTS partnership: Instrumentation for lumbar spine loads",
|
||||
"Protecting soldiers' backs from being broken",
|
||||
],
|
||||
"Not automotive - but the same mission: Use data to save lives",
|
||||
)
|
||||
|
||||
# Slide 18: Common Thread
|
||||
add_connection_slide(
|
||||
"The Common Thread",
|
||||
[
|
||||
"Dynamic Event",
|
||||
"Precise Measurement",
|
||||
"Data Analysis",
|
||||
"Engineering Decision",
|
||||
"Lives Saved",
|
||||
],
|
||||
)
|
||||
|
||||
# Slide 19: Section - Part 3
|
||||
add_section_slide("PART 3", "Good Data Enables Good Decisions")
|
||||
|
||||
# Slide 20: Good vs Bad Data
|
||||
add_two_column_slide(
|
||||
"Data Quality: Why It Matters",
|
||||
"GOOD DATA",
|
||||
[
|
||||
"Clean pre-trigger baseline",
|
||||
"Appropriate CFC filtering",
|
||||
"Clear signal, no saturation",
|
||||
"Proper sensor range",
|
||||
"No zero shift after impact",
|
||||
"No cable artifacts",
|
||||
"",
|
||||
"= Data you can trust",
|
||||
"= Good engineering decisions",
|
||||
],
|
||||
"BAD DATA",
|
||||
[
|
||||
"Saturated signal (overranged)",
|
||||
"Excessive noise/interference",
|
||||
"Zero shift (baseline moved)",
|
||||
"Cable yanked = false spike",
|
||||
"Resonance artifacts",
|
||||
"Missing channels",
|
||||
"",
|
||||
"= Data you cannot trust",
|
||||
"= Dangerous decisions",
|
||||
],
|
||||
)
|
||||
|
||||
# Slide 21: Your Decisions Matter
|
||||
add_content_slide(
|
||||
"Why This Matters for Your Work",
|
||||
[
|
||||
"Sensor range selection: Will customers saturate?",
|
||||
"Mounting design: Will they get resonance artifacts?",
|
||||
"Calibration procedures: What's the accuracy baseline?",
|
||||
"Cable design: Will it survive the test?",
|
||||
"Software algorithms: Is filtering correct?",
|
||||
"",
|
||||
"Your decisions affect every test run with our equipment",
|
||||
],
|
||||
"These aren't abstract - they determine if engineers can trust the data",
|
||||
)
|
||||
|
||||
# Slide 22: Ripple Effect
|
||||
add_connection_slide(
|
||||
"The Ripple Effect of Your Work",
|
||||
[
|
||||
"DTS Product",
|
||||
"Customer Test",
|
||||
"Vehicle Design",
|
||||
"1M Vehicles Built",
|
||||
"Millions Protected",
|
||||
],
|
||||
)
|
||||
|
||||
# Slide 23: Section - Closing
|
||||
add_section_slide("CLOSING", "Remember the Why")
|
||||
|
||||
# Slide 24: Real Customers
|
||||
add_content_slide(
|
||||
"The Real Customers",
|
||||
[
|
||||
"Not the test lab",
|
||||
"Not the automotive OEM",
|
||||
"Not the government regulator",
|
||||
"",
|
||||
"The real customers are the people who will sit",
|
||||
"in vehicles designed using our data",
|
||||
"",
|
||||
"They don't know our name - but their lives depend on us",
|
||||
],
|
||||
"Families. Children. Commuters. Soldiers. Athletes.",
|
||||
)
|
||||
|
||||
# Slide 25: Work Isn't Done
|
||||
add_content_slide(
|
||||
"The Work Isn't Done",
|
||||
[
|
||||
"Electric vehicles: Battery integrity, high-voltage safety",
|
||||
"Autonomous vehicles: Active safety validation",
|
||||
"Pedestrians & cyclists: Rising fatalities need solutions",
|
||||
"Military: Evolving threats require new testing",
|
||||
"Sports: Brain injury mechanisms still being discovered",
|
||||
"",
|
||||
"Every challenge will require new instrumentation, new data",
|
||||
],
|
||||
"New challenges for the next generation of DTS products",
|
||||
)
|
||||
|
||||
# Slide 26: Final Quote
|
||||
add_quote_slide(
|
||||
"Every channel of data tells a story. Make sure it's accurate enough to save a life."
|
||||
)
|
||||
|
||||
# Slide 27: Questions
|
||||
add_title_slide("Questions?", "Ben - Application Engineer | [email]")
|
||||
|
||||
# ============================================================================
|
||||
# Save the presentation
|
||||
# ============================================================================
|
||||
|
||||
output_file = "The_Stories_Our_Data_Tells.pptx"
|
||||
prs.save(output_file)
|
||||
print(f"Presentation created: {output_file}")
|
||||
print(f"Total slides: {len(prs.slides)}")
|
||||
Reference in New Issue
Block a user