Files

835 lines
23 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""
Create PowerPoint: When the Data Surprised Us
Counterintuitive Findings from the Field - 45 minute internal presentation
"""
import os
import sys
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
prs = Presentation()
prs.slide_width = Inches(13.333)
prs.slide_height = Inches(7.5)
# Color scheme - Engaging, puzzle-like feel
DEEP_PURPLE = RGBColor(48, 25, 88) # Mystery color
BRIGHT_TEAL = RGBColor(0, 168, 168) # Reveal color
QUESTION_YELLOW = RGBColor(255, 200, 0) # Question accent
DARK_GRAY = RGBColor(51, 51, 51)
MED_GRAY = RGBColor(128, 128, 128)
LIGHT_GRAY = RGBColor(235, 235, 235)
WHITE = RGBColor(255, 255, 255)
CORRECT_GREEN = RGBColor(46, 139, 87) # For good results
WRONG_RED = RGBColor(178, 34, 34) # For bad results
def add_title_slide(title, subtitle=""):
"""Title slide with mystery theme"""
slide = prs.slides.add_slide(prs.slide_layouts[6])
bg = slide.shapes.add_shape(
MSO_SHAPE.RECTANGLE, 0, 0, prs.slide_width, prs.slide_height
)
bg.fill.solid()
bg.fill.fore_color.rgb = DEEP_PURPLE
bg.line.fill.background()
# Question marks decoration
for i, pos in enumerate([(1, 1), (11, 1.5), (2, 5.5), (10, 5)]):
q = slide.shapes.add_textbox(
Inches(pos[0]), Inches(pos[1]), Inches(1.5), Inches(1.5)
)
tf = q.text_frame
p = tf.paragraphs[0]
p.text = "?"
p.font.size = Pt(72)
p.font.bold = True
p.font.color.rgb = RGBColor(80, 50, 120) # Slightly lighter purple
title_box = slide.shapes.add_textbox(
Inches(0.5), Inches(2.5), Inches(12.333), Inches(1.5)
)
tf = title_box.text_frame
p = tf.paragraphs[0]
p.text = title
p.font.size = Pt(52)
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.3), Inches(12.333), Inches(0.8)
)
tf = sub_box.text_frame
p = tf.paragraphs[0]
p.text = subtitle
p.font.size = Pt(26)
p.font.color.rgb = BRIGHT_TEAL
p.alignment = PP_ALIGN.CENTER
return slide
def add_puzzle_intro_slide(puzzle_num, title):
"""Puzzle section introduction"""
slide = prs.slides.add_slide(prs.slide_layouts[6])
bg = slide.shapes.add_shape(
MSO_SHAPE.RECTANGLE, 0, 0, prs.slide_width, prs.slide_height
)
bg.fill.solid()
bg.fill.fore_color.rgb = DEEP_PURPLE
bg.line.fill.background()
# Puzzle number
num_box = slide.shapes.add_textbox(
Inches(0.5), Inches(2.5), Inches(12.333), Inches(1)
)
tf = num_box.text_frame
p = tf.paragraphs[0]
p.text = f"PUZZLE {puzzle_num}"
p.font.size = Pt(28)
p.font.color.rgb = QUESTION_YELLOW
p.alignment = PP_ALIGN.CENTER
# Title
title_box = slide.shapes.add_textbox(
Inches(0.5), Inches(3.3), Inches(12.333), Inches(1.5)
)
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 = WHITE
p.alignment = PP_ALIGN.CENTER
return slide
def add_setup_slide(title, content_lines):
"""Setup slide for puzzle - the scenario"""
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 = DEEP_PURPLE
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
# Content
content_box = slide.shapes.add_textbox(
Inches(0.8), Inches(1.4), Inches(11.5), Inches(5.8)
)
tf = content_box.text_frame
tf.word_wrap = True
for i, line in enumerate(content_lines):
if i == 0:
p = tf.paragraphs[0]
else:
p = tf.add_paragraph()
p.text = line
p.font.size = Pt(24)
p.font.color.rgb = DARK_GRAY
p.space_after = Pt(12)
return slide
def add_guess_slide(question):
"""Interactive question slide"""
slide = prs.slides.add_slide(prs.slide_layouts[6])
bg = slide.shapes.add_shape(
MSO_SHAPE.RECTANGLE, 0, 0, prs.slide_width, prs.slide_height
)
bg.fill.solid()
bg.fill.fore_color.rgb = QUESTION_YELLOW
bg.line.fill.background()
q_box = slide.shapes.add_textbox(
Inches(1), Inches(2.5), Inches(11.333), Inches(2.5)
)
tf = q_box.text_frame
tf.word_wrap = True
p = tf.paragraphs[0]
p.text = question
p.font.size = Pt(40)
p.font.bold = True
p.font.color.rgb = DEEP_PURPLE
p.alignment = PP_ALIGN.CENTER
# "What do you think?" prompt
prompt_box = slide.shapes.add_textbox(
Inches(1), Inches(5.5), Inches(11.333), Inches(0.8)
)
tf = prompt_box.text_frame
p = tf.paragraphs[0]
p.text = "Take a guess..."
p.font.size = Pt(24)
p.font.italic = True
p.font.color.rgb = DEEP_PURPLE
p.alignment = PP_ALIGN.CENTER
return slide
def add_reveal_slide(title, reveal_data, is_surprising=True):
"""The big reveal slide"""
slide = prs.slides.add_slide(prs.slide_layouts[6])
# Header - teal for reveal
header = slide.shapes.add_shape(
MSO_SHAPE.RECTANGLE, 0, 0, prs.slide_width, Inches(1.0)
)
header.fill.solid()
header.fill.fore_color.rgb = BRIGHT_TEAL
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 = "THE REVEAL: " + title
p.font.size = Pt(28)
p.font.bold = True
p.font.color.rgb = WHITE
# Reveal content
reveal_box = slide.shapes.add_textbox(
Inches(1), Inches(1.5), Inches(11.333), Inches(5.5)
)
tf = reveal_box.text_frame
tf.word_wrap = True
for i, line in enumerate(reveal_data):
if i == 0:
p = tf.paragraphs[0]
else:
p = tf.add_paragraph()
p.text = line
p.font.size = Pt(26)
p.font.color.rgb = DARK_GRAY
p.space_after = Pt(16)
return slide
def add_comparison_slide(
title, left_title, left_data, left_result, right_title, right_data, right_result
):
"""Side-by-side comparison with results"""
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 = BRIGHT_TEAL
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(28)
p.font.bold = True
p.font.color.rgb = WHITE
# Left side
left_box = slide.shapes.add_shape(
MSO_SHAPE.ROUNDED_RECTANGLE, Inches(0.5), Inches(1.3), Inches(6), Inches(4.5)
)
left_box.fill.solid()
left_box.fill.fore_color.rgb = LIGHT_GRAY
left_box.line.color.rgb = CORRECT_GREEN if left_result == "GOOD" else WRONG_RED
left_box.line.width = Pt(4)
left_title_box = slide.shapes.add_textbox(
Inches(0.8), Inches(1.5), Inches(5.5), Inches(0.6)
)
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 = DARK_GRAY
left_data_box = slide.shapes.add_textbox(
Inches(0.8), Inches(2.2), Inches(5.5), Inches(2.8)
)
tf = left_data_box.text_frame
tf.word_wrap = True
for i, line in enumerate(left_data):
if i == 0:
p = tf.paragraphs[0]
else:
p = tf.add_paragraph()
p.text = line
p.font.size = Pt(18)
p.font.color.rgb = DARK_GRAY
p.space_after = Pt(6)
left_result_box = slide.shapes.add_textbox(
Inches(0.8), Inches(5.2), Inches(5.5), Inches(0.5)
)
tf = left_result_box.text_frame
p = tf.paragraphs[0]
p.text = left_result
p.font.size = Pt(24)
p.font.bold = True
p.font.color.rgb = CORRECT_GREEN if left_result == "GOOD" else WRONG_RED
p.alignment = PP_ALIGN.CENTER
# Right side
right_box = slide.shapes.add_shape(
MSO_SHAPE.ROUNDED_RECTANGLE, Inches(6.833), Inches(1.3), Inches(6), Inches(4.5)
)
right_box.fill.solid()
right_box.fill.fore_color.rgb = LIGHT_GRAY
right_box.line.color.rgb = CORRECT_GREEN if right_result == "GOOD" else WRONG_RED
right_box.line.width = Pt(4)
right_title_box = slide.shapes.add_textbox(
Inches(7.1), Inches(1.5), Inches(5.5), Inches(0.6)
)
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 = DARK_GRAY
right_data_box = slide.shapes.add_textbox(
Inches(7.1), Inches(2.2), Inches(5.5), Inches(2.8)
)
tf = right_data_box.text_frame
tf.word_wrap = True
for i, line in enumerate(right_data):
if i == 0:
p = tf.paragraphs[0]
else:
p = tf.add_paragraph()
p.text = line
p.font.size = Pt(18)
p.font.color.rgb = DARK_GRAY
p.space_after = Pt(6)
right_result_box = slide.shapes.add_textbox(
Inches(7.1), Inches(5.2), Inches(5.5), Inches(0.5)
)
tf = right_result_box.text_frame
p = tf.paragraphs[0]
p.text = right_result
p.font.size = Pt(24)
p.font.bold = True
p.font.color.rgb = CORRECT_GREEN if right_result == "GOOD" else WRONG_RED
p.alignment = PP_ALIGN.CENTER
return slide
def add_lesson_slide(lesson_text):
"""Key lesson from the puzzle"""
slide = prs.slides.add_slide(prs.slide_layouts[6])
bg = slide.shapes.add_shape(
MSO_SHAPE.RECTANGLE, 0, 0, prs.slide_width, prs.slide_height
)
bg.fill.solid()
bg.fill.fore_color.rgb = DEEP_PURPLE
bg.line.fill.background()
# "LESSON" label
label_box = slide.shapes.add_textbox(
Inches(0.5), Inches(2.3), Inches(12.333), Inches(0.5)
)
tf = label_box.text_frame
p = tf.paragraphs[0]
p.text = "THE LESSON"
p.font.size = Pt(18)
p.font.color.rgb = BRIGHT_TEAL
p.alignment = PP_ALIGN.CENTER
# Lesson text
lesson_box = slide.shapes.add_textbox(
Inches(1), Inches(3), Inches(11.333), Inches(2.5)
)
tf = lesson_box.text_frame
tf.word_wrap = True
p = tf.paragraphs[0]
p.text = f'"{lesson_text}"'
p.font.size = Pt(36)
p.font.italic = True
p.font.color.rgb = WHITE
p.alignment = PP_ALIGN.CENTER
return slide
def add_content_slide(title, bullets):
"""Standard content slide"""
slide = prs.slides.add_slide(prs.slide_layouts[6])
header = slide.shapes.add_shape(
MSO_SHAPE.RECTANGLE, 0, 0, prs.slide_width, Inches(1.0)
)
header.fill.solid()
header.fill.fore_color.rgb = DEEP_PURPLE
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
content_box = slide.shapes.add_textbox(
Inches(0.8), Inches(1.4), Inches(11.5), Inches(5.8)
)
tf = content_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(22)
p.font.color.rgb = DARK_GRAY
p.space_after = Pt(14)
return slide
def add_stat_slide(stat, description):
"""Big statistic reveal"""
slide = prs.slides.add_slide(prs.slide_layouts[6])
bg = slide.shapes.add_shape(
MSO_SHAPE.RECTANGLE, 0, 0, prs.slide_width, prs.slide_height
)
bg.fill.solid()
bg.fill.fore_color.rgb = BRIGHT_TEAL
bg.line.fill.background()
stat_box = slide.shapes.add_textbox(
Inches(0.5), Inches(2), Inches(12.333), Inches(2)
)
tf = stat_box.text_frame
p = tf.paragraphs[0]
p.text = stat
p.font.size = Pt(96)
p.font.bold = True
p.font.color.rgb = WHITE
p.alignment = PP_ALIGN.CENTER
desc_box = slide.shapes.add_textbox(
Inches(1), Inches(4.3), Inches(11.333), Inches(1.5)
)
tf = desc_box.text_frame
tf.word_wrap = True
p = tf.paragraphs[0]
p.text = description
p.font.size = Pt(28)
p.font.color.rgb = WHITE
p.alignment = PP_ALIGN.CENTER
return slide
# ============================================================================
# BUILD THE PRESENTATION
# ============================================================================
# Slide 1: Title
add_title_slide(
"When the Data Surprised Us", "Counterintuitive Findings from the Field"
)
# Slide 2: Rules
add_content_slide(
"The Rules of the Game",
[
"1. I'll show you a scenario",
"2. You guess what happened",
"3. I reveal the data",
"4. We discuss why it matters",
"",
"Fair warning: Some of these will mess with your head.",
"That's the point.",
],
)
# ============================================================================
# PUZZLE 1: The Beautiful Wreck
# ============================================================================
add_puzzle_intro_slide("1", "The Beautiful Wreck")
add_setup_slide(
"The Setup: Two Cars After Frontal Crash Tests",
[
"Same test. Same speed. Same barrier.",
"",
"CAR A: Destroyed. Front end pushed back almost to firewall.",
" Hood crumpled like paper. Looks catastrophic.",
"",
"CAR B: Relatively intact. Minimal visible damage.",
" Structure held. Passenger compartment looks good.",
],
)
add_guess_slide("Which car would you rather be in during a crash?")
add_comparison_slide(
"The Reveal: Appearances Deceive",
"CAR A (Destroyed)",
[
"HIC: 485",
"Chest deflection: 38mm",
"Femur load: 4.2 kN",
"",
"All measurements well within limits",
],
"GOOD",
"CAR B (Intact)",
[
"HIC: 890",
"Chest deflection: 58mm",
"Femur load: 9.1 kN",
"",
"Multiple measurements near limits",
],
"MARGINAL",
)
add_content_slide(
"Why This Happens",
[
"Crush zones are SUPPOSED to crush",
"That's their job - absorb energy by deforming",
"",
"Car A: Crumpled beautifully, extended deceleration pulse",
" More time to slow down = less force on occupant",
"",
"Car B: Too stiff, didn't crumple enough",
" Short, sharp deceleration = wall of g-forces",
"",
"Looking at the car tells you nothing about safety",
],
)
add_lesson_slide(
"Never judge a crash by looking at the car. Judge it by looking at the data."
)
# ============================================================================
# PUZZLE 2: The Survivor's Paradox
# ============================================================================
add_puzzle_intro_slide("2", "The Survivor's Paradox")
add_setup_slide(
"The Setup: The Rollover Problem",
[
"Rollovers are only 3% of crashes",
"But they account for 33% of fatalities",
"",
"The obvious solution: Stronger roofs!",
"",
"In 2009, NHTSA doubled the roof crush requirement:",
"From 1.5× vehicle weight to 3× vehicle weight",
],
)
add_guess_slide("How much did stronger roofs reduce rollover fatalities?")
add_reveal_slide(
"Essentially Unchanged",
[
"Despite DOUBLING roof strength requirements...",
"",
"Rollover fatalities: Barely moved",
"",
"How is that possible?",
"Wasn't roof crush the problem?",
],
)
add_content_slide(
"The Data Revealed the Real Problem",
[
"70%+ of rollover fatalities involve EJECTION",
"",
"Occupants weren't killed by roof crushing",
"They were killed by being thrown from the vehicle",
"",
"Once outside: Ground. Other cars. Poles.",
"The vehicle itself rolling over them.",
"",
"Roof crush was secondary. Ejection was primary.",
],
)
add_content_slide(
"The Real Solution",
[
"Side curtain airbags with rollover sensors",
"Stay inflated for 5-6 seconds through multiple rolls",
"Keep occupants INSIDE the vehicle",
"",
"Result: 41% reduction in ejection",
"",
"The data pointed to a completely different solution",
"than the intuitive one",
],
)
add_lesson_slide(
"The obvious solution isn't always the right solution. Let the data tell you what's actually happening."
)
# ============================================================================
# PUZZLE 3: The Luxury Failure
# ============================================================================
add_puzzle_intro_slide("3", "The Luxury Failure")
add_setup_slide(
"The Setup: Premium Brands",
[
"BMW. Mercedes-Benz. Audi. Lexus.",
"",
"Most expensive cars on the road",
"Most prestigious brands in the world",
"Decades of safety engineering",
"",
"All passed moderate overlap test with flying colors",
"",
"2012: IIHS introduces small overlap test",
"25% overlap instead of 40%. Rigid barrier.",
],
)
add_guess_slide("How did the luxury brands perform?")
add_content_slide(
"The Results",
[
"BMW 5-Series: MARGINAL",
"Mercedes C-Class: MARGINAL",
"Audi A4: MARGINAL",
"Lexus ES 350: POOR",
"",
"Only 3 of 13 midsize cars earned 'Good'",
"",
"The most expensive vehicles on the road",
"couldn't pass a new crash test",
],
)
add_content_slide(
"What the Data Showed",
[
"Crash forces bypassed main structural rails",
"Wheels pushed back into footwells",
"Firewalls collapsed",
"Steering columns displaced into occupants",
"",
"Dummy data: Elevated HIC, critical femur loads,",
"feet trapped in crushed metal",
"",
"Engineered for the 40% test. Vulnerable at 25%.",
],
)
add_lesson_slide(
"Engineering to pass a test is not the same as engineering for safety."
)
# ============================================================================
# PUZZLE 4: The 46.2g Man
# ============================================================================
add_puzzle_intro_slide("4", "The 46.2g Man")
add_setup_slide(
"The Setup: 1954",
[
"The jet age is dawning. Pilots flying faster than ever.",
"Many are dying - not from crashes, from ejections.",
"",
"Medical authorities believed:",
"'Humans cannot survive more than 18g of deceleration'",
"",
"If true, high-speed ejection was a death sentence.",
"",
"One researcher decided to test this assumption.",
"Not with dummies. With himself.",
],
)
add_content_slide(
"Colonel John Paul Stapp",
[
"Air Force flight surgeon",
"The bravest - or craziest - researcher in safety history",
"",
"Strapped himself into a rocket sled",
"Ran test after test, pushing higher g-forces",
"",
"December 10, 1954:",
"The ultimate test",
],
)
add_stat_slide(
"46.2g", "632 mph to zero in 1.4 seconds\nMore than 2.5× the 'fatal' threshold"
)
add_content_slide(
"He Survived",
[
"Walked away - temporarily blinded",
"Burst blood vessels in his eyes",
"But alive and conscious",
"",
"Proved humans could survive far more than believed",
"Revolutionized aviation safety",
"Made ejection seats viable at higher speeds",
"",
"Laid the foundation for automotive crash testing:",
"How do we design so occupants never exceed their limits?",
],
)
add_lesson_slide("Assumptions are not data. Test it. Measure it. Know for certain.")
# ============================================================================
# PUZZLE 5: The Airbag Arms Race
# ============================================================================
add_puzzle_intro_slide("5", "The Airbag Arms Race")
add_setup_slide(
"The Setup: Airbag History",
[
"1970: Airbags first proposed",
"1984: First automatic restraint requirements",
"1998: Airbags mandatory in all vehicles",
"2000s: 'Advanced' airbags with occupant classification",
"",
"Each generation more sophisticated:",
"More sensors. More stages. More intelligence.",
],
)
add_guess_slide(
"How much faster did airbag technology advance after mandates vs. before?"
)
add_reveal_slide(
"Regulation Created Innovation",
[
"Pre-mandate (1970-1984): Minimal innovation",
"Why invest in something not required?",
"",
"Post-mandate: Explosion of patents and technology",
"Multi-stage inflators, occupant sensors, position detection",
"Side airbags, curtain airbags, knee airbags",
"",
"The regulation created the market",
"The market drove innovation",
"The innovation saved lives",
],
)
add_stat_slide(
"~2,800",
"Lives saved by airbags per year in the U.S.\nFrom zero in 1985 to thousands today",
)
add_lesson_slide(
"Measurement isn't just about understanding what happened. It's about enabling what comes next."
)
# ============================================================================
# CLOSING
# ============================================================================
add_content_slide(
"The Meta-Lesson",
[
"Beautiful Wreck: Crush zones work - deformation is safety",
"Survivor's Paradox: Ejection, not crush, was the killer",
"Luxury Failure: We'd optimized for the wrong test",
"46.2g Man: Assumptions limited our designs",
"Airbag Arms Race: Mandates drove innovation",
"",
"In every case, data didn't just describe - it ENABLED",
"It pointed the way forward",
],
)
add_content_slide(
"Implications for DTS",
[
"Our instrumentation is the source of these surprises",
"",
"When data contradicts expectations, it's because:",
"Someone measured something that hadn't been measured",
"Or measured it more precisely",
"Or measured it in a new context",
"",
"Every sensor we design, every DAQ we build",
"is potentially the source of the next breakthrough",
],
)
add_lesson_slide(
"What assumptions are we making today that data will overturn tomorrow?"
)
# Questions slide
add_title_slide("Questions?", "Ben - Application Engineer")
# ============================================================================
# Save
# ============================================================================
output_file = "When_the_Data_Surprised_Us.pptx"
prs.save(output_file)
print(f"Presentation created: {output_file}")
print(f"Total slides: {len(prs.slides)}")