772 lines
22 KiB
Python
772 lines
22 KiB
Python
#!/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)}")
|