#!/usr/bin/env python3 """ Create PowerPoint: From Tragedy to Triumph How Data Transformed Deaths into Design Changes - 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 - Darker, more somber for emotional impact DARK_NAVY = RGBColor(15, 30, 45) # Very dark blue/black ACCENT_GOLD = RGBColor(212, 175, 55) # Dignified gold SOFT_WHITE = RGBColor(250, 250, 250) DARK_GRAY = RGBColor(60, 60, 60) MED_GRAY = RGBColor(120, 120, 120) LIGHT_GRAY = RGBColor(220, 220, 220) HOPE_BLUE = RGBColor(70, 130, 180) # Steel blue for triumph sections SOMBER_RED = RGBColor(139, 69, 69) # Muted red for tragedy def add_dark_title_slide(title, subtitle=""): """Full dark title slide for emotional impact""" 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 = DARK_NAVY bg.line.fill.background() 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(56) p.font.bold = True p.font.color.rgb = SOFT_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(24) p.font.color.rgb = ACCENT_GOLD p.alignment = PP_ALIGN.CENTER return slide def add_story_intro_slide(story_num, title): """Story section divider - dark and serious""" 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 = DARK_NAVY bg.line.fill.background() # Story number num_box = slide.shapes.add_textbox( Inches(0.5), Inches(2.2), Inches(12.333), Inches(0.8) ) tf = num_box.text_frame p = tf.paragraphs[0] p.text = f"STORY {story_num}" p.font.size = Pt(20) p.font.color.rgb = ACCENT_GOLD p.alignment = PP_ALIGN.CENTER # Title title_box = slide.shapes.add_textbox( Inches(0.5), Inches(3.0), Inches(12.333), Inches(1.5) ) tf = title_box.text_frame tf.word_wrap = True p = tf.paragraphs[0] p.text = title p.font.size = Pt(44) p.font.bold = True p.font.color.rgb = SOFT_WHITE p.alignment = PP_ALIGN.CENTER return slide def add_memorial_slide(main_text, subtext=""): """Memorial-style slide for tragedy sections""" 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 = DARK_NAVY bg.line.fill.background() # Main text main_box = slide.shapes.add_textbox( Inches(1), Inches(2.5), Inches(11.333), Inches(2) ) tf = main_box.text_frame tf.word_wrap = True p = tf.paragraphs[0] p.text = main_text p.font.size = Pt(36) p.font.color.rgb = SOFT_WHITE 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(22) p.font.italic = True p.font.color.rgb = MED_GRAY p.alignment = PP_ALIGN.CENTER return slide def add_question_slide(question): """Large 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 = DARK_NAVY bg.line.fill.background() q_box = slide.shapes.add_textbox(Inches(1), Inches(2.8), Inches(11.333), Inches(2)) tf = q_box.text_frame tf.word_wrap = True p = tf.paragraphs[0] p.text = question p.font.size = Pt(40) p.font.italic = True p.font.color.rgb = ACCENT_GOLD p.alignment = PP_ALIGN.CENTER return slide def add_content_slide(title, bullets, tone="neutral"): """Content slide with tonal variation""" slide = prs.slides.add_slide(prs.slide_layouts[6]) # Background based on tone if tone == "tragedy": header_color = SOMBER_RED elif tone == "triumph": header_color = HOPE_BLUE else: header_color = DARK_NAVY # 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 = header_color 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 = SOFT_WHITE # Bullets bullet_box = slide.shapes.add_textbox( Inches(0.8), Inches(1.4), Inches(11.5), Inches(5.8) ) 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(22) p.font.color.rgb = DARK_GRAY p.space_after = Pt(14) return slide def add_data_slide(title, data_description, annotation=""): """Data revelation 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 = DARK_NAVY 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 = SOFT_WHITE # Data box placeholder data_box = slide.shapes.add_shape( MSO_SHAPE.RECTANGLE, Inches(1), Inches(1.5), Inches(11.333), Inches(4) ) data_box.fill.solid() data_box.fill.fore_color.rgb = LIGHT_GRAY data_box.line.color.rgb = DARK_NAVY data_text = slide.shapes.add_textbox( Inches(1), Inches(3), Inches(11.333), Inches(1) ) tf = data_text.text_frame p = tf.paragraphs[0] p.text = f"[{data_description}]" p.font.size = Pt(22) p.font.color.rgb = DARK_GRAY p.alignment = PP_ALIGN.CENTER if annotation: ann_box = slide.shapes.add_textbox( Inches(1), Inches(5.8), Inches(11.333), Inches(1) ) tf = ann_box.text_frame tf.word_wrap = True p = tf.paragraphs[0] p.text = annotation p.font.size = Pt(18) p.font.bold = True p.font.color.rgb = SOMBER_RED p.alignment = PP_ALIGN.CENTER return slide def add_triumph_stat_slide(stat, description): """Triumph statistic slide - hopeful tone""" slide = prs.slides.add_slide(prs.slide_layouts[6]) # Gradient-like effect with hope blue bg = slide.shapes.add_shape( MSO_SHAPE.RECTANGLE, 0, 0, prs.slide_width, prs.slide_height ) bg.fill.solid() bg.fill.fore_color.rgb = HOPE_BLUE bg.line.fill.background() stat_box = slide.shapes.add_textbox( Inches(0.5), Inches(2), Inches(12.333), Inches(2.5) ) 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 = SOFT_WHITE p.alignment = PP_ALIGN.CENTER desc_box = slide.shapes.add_textbox( Inches(0.5), Inches(4.5), Inches(12.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 = SOFT_WHITE p.alignment = PP_ALIGN.CENTER return slide def add_arc_slide(title): """Show the tragedy-to-triumph arc""" slide = prs.slides.add_slide(prs.slide_layouts[6]) title_box = slide.shapes.add_textbox( Inches(0.5), Inches(0.3), Inches(12.333), Inches(0.8) ) 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 = DARK_NAVY p.alignment = PP_ALIGN.CENTER # Arc elements elements = ["TRAGEDY", "INVESTIGATION", "DATA", "SOLUTION", "TRIUMPH"] colors = [SOMBER_RED, DARK_GRAY, DARK_NAVY, HOPE_BLUE, HOPE_BLUE] box_width = Inches(2.0) spacing = Inches(0.4) start_x = Inches(0.8) for i, (element, color) in enumerate(zip(elements, colors)): x = start_x + i * (box_width + spacing) # Box box = slide.shapes.add_shape( MSO_SHAPE.ROUNDED_RECTANGLE, x, Inches(3), box_width, Inches(1.2) ) box.fill.solid() box.fill.fore_color.rgb = color box.line.fill.background() # Text text_box = slide.shapes.add_textbox(x, Inches(3.3), box_width, Inches(0.8)) tf = text_box.text_frame p = tf.paragraphs[0] p.text = element p.font.size = Pt(16) p.font.bold = True p.font.color.rgb = SOFT_WHITE p.alignment = PP_ALIGN.CENTER # Arrow if i < len(elements) - 1: arrow = slide.shapes.add_shape( MSO_SHAPE.RIGHT_ARROW, x + box_width + Inches(0.05), Inches(3.35), spacing - Inches(0.1), Inches(0.5), ) arrow.fill.solid() arrow.fill.fore_color.rgb = MED_GRAY arrow.line.fill.background() return slide def add_final_quote_slide(quote): """Final reflective quote""" 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 = DARK_NAVY bg.line.fill.background() quote_box = slide.shapes.add_textbox( Inches(1), Inches(2.5), Inches(11.333), Inches(3) ) tf = quote_box.text_frame tf.word_wrap = True p = tf.paragraphs[0] p.text = quote p.font.size = Pt(32) p.font.italic = True p.font.color.rgb = ACCENT_GOLD p.alignment = PP_ALIGN.CENTER return slide # ============================================================================ # BUILD THE PRESENTATION # ============================================================================ # Slide 1: Title add_dark_title_slide( "From Tragedy to Triumph", "How Data Transformed Deaths into Design Changes" ) # Slide 2: Arc introduction add_arc_slide("Every Safety Standard Follows This Arc") # Slide 3: Story 1 intro add_story_intro_slide("1", "The Children Who Were\nKilled by Safety") # Slide 4: Memorial add_memorial_slide( "In memory of the 175+ people\nkilled by airbags, 1990-1997", "Most were children sitting in the front seat", ) # Slide 5: The question add_question_slide( "Why were airbags killing the people\nthey were designed to protect?" ) # Slide 6: Investigation add_content_slide( "The Investigation: Out-of-Position Testing", [ "What happens when someone isn't in the ideal position?", "What happens when they're too close to the airbag?", "What happens when they're small?", "", "Researchers used instrumented dummies:", " Small female dummies, child dummies, infant dummies", " Accelerometers and load cells capturing every millisecond", " The same sensors DTS makes today", ], ) # Slide 7: The data add_data_slide( "The Data: Fatal Forces Revealed", "Neck load trace showing 3,500 N peak vs. 1,600 N injury threshold", "More than DOUBLE the fatal threshold for a child's neck", ) # Slide 8: The solution add_content_slide( "The Engineering Solution", [ "1998: Immediate depowering (20-35% force reduction)", "Dual-stage inflators: Variable deployment force", "Occupant classification: Weight sensors detect children", "Suppression systems: Turn off for rear-facing child seats", "Multi-stage deployment: Match force to crash severity", "", "Every solution required new testing, new data, new validation", ], tone="triumph", ) # Slide 9: Triumph add_triumph_stat_slide( "50 → 0", "Airbag fatalities per year:\nFrom over 50 to essentially zero" ) # Slide 10: Story 2 intro add_story_intro_slide("2", "The Car That Was\nCheaper to Let Burn") # Slide 11: Pinto tragedy add_content_slide( "The Ford Pinto: 1970s Bestseller", [ "Small, cheap, fuel-efficient during the oil crisis", "One problem: Gas tank 9 inches from rear bumper", "", "In rear-end collisions:", " Rear crumpled into gas tank", " Tank ruptured, fuel sprayed", " A spark ignited it", "", "People burned to death inside their cars", "Estimates: 27 to several hundred deaths", ], tone="tragedy", ) # Slide 12: The memo add_memorial_slide( '"Costs: $11/car × 12.5M cars = $137M\nBenefits: 180 deaths × $200K = $36M"', "Internal Ford cost-benefit analysis. Cost exceeds benefit. Don't fix it.", ) # Slide 13: Data/solution add_content_slide( "The Data That Should Have Driven the Decision", [ "Rear impact testing showed catastrophic fuel leakage", "Tank design failed at impacts as low as 25 mph", "Ford engineers knew. They had the data.", "But data didn't make it past accounting.", "", "After the scandal, FMVSS 301 was strengthened:", " Rear impact: 50 mph", " Side impact: 20 mph", " Rollover: 360 degrees", " Fuel leakage precisely measured and limited", ], ) # Slide 14: Triumph add_triumph_stat_slide("Down 76%", "Vehicle fire deaths since 1980") # Slide 15: Story 3 intro add_story_intro_slide("3", "The Test That\nHumbled an Industry") # Slide 16: Context add_content_slide( "2010: Frontal Safety Was 'Solved'", [ "Vehicles acing federal full-frontal test", "Vehicles acing IIHS moderate overlap test", "Frontal crash deaths were down significantly", "", "But IIHS researchers noticed a pattern...", "", "People were still dying in frontal crashes", "Tree strikes. Pole strikes. Corner-to-corner.", "Crashes where only 20-25% of width was engaged", ], ) # Slide 17: The test add_content_slide( "The New Test: Small Overlap Frontal", [ "25% overlap - not 40%", "Rigid barrier - not deformable", "40 mph", "", "Designed to stress the outer edge of the structure", "The area where trees and poles were hitting", "", "2012: Testing begins", "Results shocked everyone", ], ) # Slide 18: The failures add_content_slide( "The Results: Premium Brands Failed", [ "BMW 5-Series: Marginal", "Mercedes C-Class: Marginal", "Audi A4: Marginal", "Lexus ES: Poor", "", "Only 3 of 13 midsize cars earned 'Good'", "", "Data showed catastrophic intrusion, collapsed footwells,", "HIC values exceeding limits, femur loads off the charts", ], tone="tragedy", ) # Slide 19: Response add_content_slide( "The Industry Response", [ "Within two years: Fundamental structural redesign", "Small overlap load paths added", "Extended bumper beams", "Reinforced A-pillars", "", "By 2015: Most vehicles earning 'Good'", "", "Instrumented testing proved the problem", "And proved that the redesign worked", ], tone="triumph", ) # Slide 20: Triumph add_triumph_stat_slide( "Down 8%", "Additional reduction in frontal crash deaths\nwithin 3 years of test introduction", ) # Slide 21: Story 4 intro add_story_intro_slide("4", "The Sport That\nSaved Itself") # Slide 22: Football tragedy add_memorial_slide( "32 players per year", "Killed by head and neck injuries in football, 1960s-1970s" ) # Slide 23: The problem add_content_slide( "Helmets Existed. They Just Didn't Work.", [ "No standard. No test. No validation.", "Manufacturers made claims. Coaches trusted them.", "Players died.", "", "1969: NOCSAE formed", "Mission: Create a test that actually predicts protection", "", "Drop tower. Instrumented headform. Accelerometers.", "Severity Index (SI) metric developed.", ], ) # Slide 24: The data add_data_slide( "The Data: Some Helmets Weren't Even Close", "Chart: SI values - Good helmets at 600-800, bad helmets at 1500-2000", "Pass threshold: SI < 1200. Many market leaders failed catastrophically.", ) # Slide 25: Solution add_content_slide( "The Solution: Set a Standard", [ "NOCSAE established SI < 1200 requirement", "1980: NCAA mandated compliance", "High school federations followed", "", "Helmet manufacturers had to design for performance", "Better liners. Better shells. Better protection.", "", "2016: Linear impactor test added for rotational injury", "The science advances; the standard advances", ], tone="triumph", ) # Slide 26: Triumph add_triumph_stat_slide( "32 → 3", "Football deaths per year from head/neck injuries\n90%+ reduction" ) # Slide 27: Pattern summary add_arc_slide("Four Stories, One Pattern: Data Made the Difference") # Slide 28: DTS position add_content_slide( "Where DTS Fits in This Arc", [ "We're in the middle. We're the data capture mechanism.", "", "Our accelerometers are in the dummies", "Our DAQ systems are in the crash labs", "Our calibration ensures global consistency", "", "We don't design vehicles or write regulations", "But without accurate data capture,", "none of that other work is possible", ], ) # Slide 29: Ongoing mission add_content_slide( "The Mission Continues", [ "Electric vehicles: New crash dynamics, battery safety", "Autonomous vehicles: Active safety validation", "Pedestrians & cyclists: Rising fatalities need solutions", "Military: Evolving threats, new protection needs", "Sports: Concussion science revealing rotational injury", "", "Each challenge will follow the same arc", "There will be tragedies. Then data. Then solutions.", ], ) # Slide 30: Final quote add_final_quote_slide( "Behind every safety standard is someone who didn't come home.\n\n" "Data ensures their loss wasn't in vain." ) # Slide 31: Questions add_dark_title_slide("Questions?", "Ben - Application Engineer") # ============================================================================ # Save # ============================================================================ output_file = "From_Tragedy_to_Triumph.pptx" prs.save(output_file) print(f"Presentation created: {output_file}") print(f"Total slides: {len(prs.slides)}")