Skip to main content

scripts-strategy-powerpoint-exporter

#!/usr/bin/env python3 """

title: "Optional dependency check" component_type: script version: "1.0.0" audience: contributor status: stable summary: "Strategy PowerPoint Exporter ============================" keywords: ['analysis', 'exporter', 'powerpoint', 'review', 'strategy'] tokens: ~500 created: 2025-12-22 updated: 2025-12-22 script_name: "strategy-powerpoint-exporter.py" language: python executable: true usage: "python3 scripts/strategy-powerpoint-exporter.py [options]" python_version: "3.10+" dependencies: [] modifies_files: false network_access: false requires_auth: false

Strategy PowerPoint Exporter

Generate professional McKinsey/BCG-style presentation decks from strategy briefs. Produces PPTX files with consistent branding, data visualizations, and slide layouts.

Usage: python3 scripts/strategy-powerpoint-exporter.py --input brief.json --output deck.pptx python3 scripts/strategy-powerpoint-exporter.py --demo python3 scripts/strategy-powerpoint-exporter.py --brief-dir ./briefs --output-dir ./decks

Features: - Professional 16:9 slide layouts - SWOT matrix visualization - Competitive landscape slides - Executive summary with key metrics - Growth strategy option slides

Dependencies: pip install python-pptx

Source: Adapted from ANALYZE-REVIEW/extracted-consulting/powerpoint_exporter.py """

import argparse import json import sys from dataclasses import dataclass from pathlib import Path from typing import Dict, List, Any, Optional

Optional dependency check

try: from pptx import Presentation from pptx.util import Inches, Pt from pptx.enum.text import PP_ALIGN from pptx.dml.color import RGBColor from pptx.enum.shapes import MSO_SHAPE PPTX_AVAILABLE = True except ImportError: PPTX_AVAILABLE = False

@dataclass class SlideTheme: """Professional slide theme colors (CODITECT brand).""" primary: tuple = (37, 99, 235) # Blue #2563eb secondary: tuple = (124, 58, 237) # Purple #7c3aed accent: tuple = (16, 185, 129) # Green #10b981 text: tuple = (31, 41, 55) # Dark gray #1f2937 background: tuple = (255, 255, 255) # White neutral: tuple = (156, 163, 175) # Gray #9ca3af

class StrategyPowerPointExporter: """Export strategy briefs to professional PowerPoint presentations."""

def __init__(self, template_path: Optional[Path] = None):
if not PPTX_AVAILABLE:
raise ImportError(
"python-pptx required for PowerPoint export. "
"Install with: pip install python-pptx"
)

if template_path and template_path.exists():
self.prs = Presentation(str(template_path))
else:
self.prs = Presentation()

self.theme = SlideTheme()

def create_title_slide(
self,
title: str,
subtitle: str,
metrics: Optional[Dict[str, str]] = None
) -> None:
"""Create title slide with optional key metrics."""
slide = self.prs.slides.add_slide(self.prs.slide_layouts[6])

# Title
title_box = slide.shapes.add_textbox(
Inches(0.5), Inches(1.5), Inches(9), Inches(1)
)
tf = title_box.text_frame
tf.text = title
tf.paragraphs[0].font.size = Pt(44)
tf.paragraphs[0].font.bold = True
tf.paragraphs[0].font.color.rgb = RGBColor(*self.theme.primary)
tf.paragraphs[0].alignment = PP_ALIGN.CENTER

# Subtitle
sub_box = slide.shapes.add_textbox(
Inches(0.5), Inches(2.7), Inches(9), Inches(0.5)
)
sf = sub_box.text_frame
sf.text = subtitle
sf.paragraphs[0].font.size = Pt(18)
sf.paragraphs[0].font.color.rgb = RGBColor(*self.theme.text)
sf.paragraphs[0].alignment = PP_ALIGN.CENTER

# Metrics boxes
if metrics:
self._add_metrics_row(slide, metrics, y=3.5)

def _add_metrics_row(self, slide, metrics: Dict[str, str], y: float) -> None:
"""Add row of metric boxes."""
box_width = 2.0
spacing = 0.2
num = len(metrics)
total_width = (box_width * num) + (spacing * (num - 1))
start_x = (10 - total_width) / 2

for i, (label, value) in enumerate(metrics.items()):
x = start_x + (i * (box_width + spacing))

shape = slide.shapes.add_shape(
MSO_SHAPE.ROUNDED_RECTANGLE,
Inches(x), Inches(y), Inches(box_width), Inches(0.8)
)
shape.fill.solid()
shape.fill.fore_color.rgb = RGBColor(249, 250, 251)
shape.line.color.rgb = RGBColor(*self.theme.primary)
shape.line.width = Pt(2)

tf = shape.text_frame
tf.clear()
p = tf.paragraphs[0]
p.text = value
p.font.size = Pt(24)
p.font.bold = True
p.font.color.rgb = RGBColor(*self.theme.primary)
p.alignment = PP_ALIGN.CENTER

p2 = tf.add_paragraph()
p2.text = label
p2.font.size = Pt(10)
p2.font.color.rgb = RGBColor(*self.theme.neutral)
p2.alignment = PP_ALIGN.CENTER

def create_executive_summary_slide(self, takeaways: List[str]) -> None:
"""Create executive summary with key takeaways."""
slide = self.prs.slides.add_slide(self.prs.slide_layouts[6])
self._add_slide_title(slide, "Executive Summary")

content_box = slide.shapes.add_textbox(
Inches(0.7), Inches(1.3), Inches(8.6), Inches(3.8)
)
tf = content_box.text_frame
tf.word_wrap = True

for i, takeaway in enumerate(takeaways[:5], 1):
p = tf.paragraphs[0] if i == 1 else tf.add_paragraph()
p.text = f"{i}. {takeaway}"
p.font.size = Pt(14)
p.font.color.rgb = RGBColor(*self.theme.text)
p.space_after = Pt(10)

def create_swot_slide(self, swot: Dict[str, List[str]]) -> None:
"""Create 2x2 SWOT matrix slide."""
slide = self.prs.slides.add_slide(self.prs.slide_layouts[6])
self._add_slide_title(slide, "SWOT Analysis")

quadrants = [
('Strengths', swot.get('strengths', []), 1.0, 1.5, (220, 252, 231)),
('Weaknesses', swot.get('weaknesses', []), 5.2, 1.5, (254, 226, 226)),
('Opportunities', swot.get('opportunities', []), 1.0, 3.4, (219, 234, 254)),
('Threats', swot.get('threats', []), 5.2, 3.4, (254, 243, 199)),
]

for title, items, x, y, color in quadrants:
self._add_swot_quadrant(slide, title, items, x, y, 3.9, 1.7, color)

def _add_swot_quadrant(
self, slide, title: str, items: List[str],
x: float, y: float, w: float, h: float, bg_color: tuple
) -> None:
"""Add single SWOT quadrant."""
shape = slide.shapes.add_shape(
MSO_SHAPE.ROUNDED_RECTANGLE,
Inches(x), Inches(y), Inches(w), Inches(h)
)
shape.fill.solid()
shape.fill.fore_color.rgb = RGBColor(*bg_color)
shape.line.width = Pt(0)

# Title
tbox = slide.shapes.add_textbox(
Inches(x + 0.1), Inches(y + 0.1), Inches(w - 0.2), Inches(0.3)
)
tbox.text_frame.text = title
tbox.text_frame.paragraphs[0].font.size = Pt(14)
tbox.text_frame.paragraphs[0].font.bold = True
tbox.text_frame.paragraphs[0].font.color.rgb = RGBColor(*self.theme.text)

# Items
ibox = slide.shapes.add_textbox(
Inches(x + 0.2), Inches(y + 0.5), Inches(w - 0.4), Inches(h - 0.6)
)
itf = ibox.text_frame
itf.word_wrap = True

for i, item in enumerate(items[:4]):
p = itf.paragraphs[0] if i == 0 else itf.add_paragraph()
p.text = f"• {item[:70]}"
p.font.size = Pt(10)
p.font.color.rgb = RGBColor(*self.theme.text)

def create_competitive_slide(self, competitors: List[Dict[str, str]]) -> None:
"""Create competitive landscape slide."""
slide = self.prs.slides.add_slide(self.prs.slide_layouts[6])
self._add_slide_title(slide, "Competitive Landscape")

for i, comp in enumerate(competitors[:5]):
y = 1.5 + (i * 0.8)

shape = slide.shapes.add_shape(
MSO_SHAPE.ROUNDED_RECTANGLE,
Inches(0.8), Inches(y), Inches(8.4), Inches(0.65)
)
shape.fill.solid()
shape.fill.fore_color.rgb = RGBColor(249, 250, 251)
shape.line.color.rgb = RGBColor(*self.theme.primary)
shape.line.width = Pt(1)

# Name
nbox = slide.shapes.add_textbox(
Inches(1.0), Inches(y + 0.05), Inches(2.0), Inches(0.25)
)
nbox.text_frame.text = comp.get('name', 'Unknown')
nbox.text_frame.paragraphs[0].font.size = Pt(14)
nbox.text_frame.paragraphs[0].font.bold = True
nbox.text_frame.paragraphs[0].font.color.rgb = RGBColor(*self.theme.primary)

# Revenue
rbox = slide.shapes.add_textbox(
Inches(3.2), Inches(y + 0.05), Inches(1.2), Inches(0.25)
)
rbox.text_frame.text = comp.get('revenue', 'N/A')
rbox.text_frame.paragraphs[0].font.size = Pt(12)

# Positioning
pbox = slide.shapes.add_textbox(
Inches(4.5), Inches(y + 0.05), Inches(4.5), Inches(0.55)
)
pbox.text_frame.text = comp.get('positioning', '')[:90]
pbox.text_frame.paragraphs[0].font.size = Pt(9)
pbox.text_frame.paragraphs[0].font.color.rgb = RGBColor(*self.theme.neutral)
pbox.text_frame.word_wrap = True

def create_growth_option_slide(self, option: Dict[str, Any]) -> None:
"""Create growth strategy option slide."""
slide = self.prs.slides.add_slide(self.prs.slide_layouts[6])
num = option.get('number', '')
name = option.get('name', 'Growth Option')
self._add_slide_title(slide, f"Option {num}: {name}")

sections = [
('Where to Play', option.get('where_to_play', ''), 1.5),
('How to Win', option.get('how_to_win', ''), 2.4),
]

for title, content, y in sections:
self._add_content_section(slide, title, content, y)

# First 90 days
actions = option.get('first_90_days', [])
if actions:
actions_text = "\n".join(f"• {a}" for a in actions[:4])
self._add_content_section(slide, "First 90 Days", actions_text, 3.4)

def _add_content_section(self, slide, title: str, content: str, y: float) -> None:
"""Add titled content section."""
tbox = slide.shapes.add_textbox(
Inches(0.8), Inches(y), Inches(8.4), Inches(0.25)
)
tbox.text_frame.text = title
tbox.text_frame.paragraphs[0].font.size = Pt(12)
tbox.text_frame.paragraphs[0].font.bold = True
tbox.text_frame.paragraphs[0].font.color.rgb = RGBColor(*self.theme.primary)

cbox = slide.shapes.add_textbox(
Inches(0.8), Inches(y + 0.3), Inches(8.4), Inches(0.7)
)
cbox.text_frame.text = content
cbox.text_frame.paragraphs[0].font.size = Pt(11)
cbox.text_frame.paragraphs[0].font.color.rgb = RGBColor(*self.theme.text)
cbox.text_frame.word_wrap = True

def _add_slide_title(self, slide, title: str) -> None:
"""Add consistent slide title with underline."""
tbox = slide.shapes.add_textbox(
Inches(0.5), Inches(0.3), Inches(9), Inches(0.6)
)
tbox.text_frame.text = title
tbox.text_frame.paragraphs[0].font.size = Pt(28)
tbox.text_frame.paragraphs[0].font.bold = True
tbox.text_frame.paragraphs[0].font.color.rgb = RGBColor(*self.theme.primary)

line = slide.shapes.add_connector(
1, Inches(0.5), Inches(0.95), Inches(9.5), Inches(0.95)
)
line.line.color.rgb = RGBColor(*self.theme.primary)
line.line.width = Pt(2)

def export_from_brief(self, brief_data: Dict) -> None:
"""Create full presentation from brief data."""
params = brief_data.get('parameters', {})

# Title slide
self.create_title_slide(
title=params.get('industry', 'Strategy Brief'),
subtitle=f"{params.get('geography', 'Global')} | {params.get('time_horizon', '12-24 months')}",
metrics=brief_data.get('key_metrics')
)

# Executive summary
if 'executive_snapshot' in brief_data:
takeaways = brief_data['executive_snapshot'].get('takeaways', [])
if takeaways:
self.create_executive_summary_slide(takeaways)

# SWOT
if 'swot' in brief_data:
self.create_swot_slide(brief_data['swot'])

# Competitive landscape
if 'competitors' in brief_data:
self.create_competitive_slide(brief_data['competitors'])

# Growth options
for option in brief_data.get('growth_options', []):
self.create_growth_option_slide(option)

def save(self, output_path: Path) -> None:
"""Save presentation to file."""
self.prs.save(str(output_path))
print(f"Presentation saved: {output_path}")
print(f" Slides: {len(self.prs.slides)}")

def get_demo_data() -> Dict: """Return demo brief data.""" return { 'parameters': { 'industry': 'AI Development Tools', 'geography': 'Global', 'time_horizon': '12-24 months' }, 'key_metrics': { 'Market Size': '$43B', 'Growth Rate': '18-22%', 'Trend': 'AI Adoption' }, 'executive_snapshot': { 'takeaways': [ 'Market experiencing exceptional growth. Global market reached $43B in 2024.', 'AI transforming delivery models. 78% of developers now use AI tools weekly.', 'Persistent talent shortage. 87% of companies report developer shortages.', 'Regulatory complexity increasing. EU AI Act creates compliance requirements.', 'Offshore/nearshore remains critical. Market valued at $178.6B.' ] }, 'swot': { 'strengths': ['Established client relationships', 'Proven methodology', 'Talent pool'], 'weaknesses': ['May lag in AI adoption', 'Geographic concentration', 'Legacy processes'], 'opportunities': ['AI productivity gains (25-40%)', 'Compliance services', 'Nearshore expansion'], 'threats': ['AI disrupts labor arbitrage', 'Hyperscalers expanding', 'Low-code platforms'] }, 'competitors': [ {'name': 'Accenture', 'revenue': '$67.4B', 'positioning': 'Global leader in professional services'}, {'name': 'TCS', 'revenue': '$30.75B', 'positioning': 'India largest IT services'}, {'name': 'Cognizant', 'revenue': '$19.7B', 'positioning': 'Strong healthcare and BFSI expertise'} ], 'growth_options': [ { 'number': 1, 'name': 'AI-First Transformation', 'where_to_play': 'Existing client base; position as AI-native delivery partner', 'how_to_win': 'Differentiate through 25-40% productivity gains and faster time-to-market', 'first_90_days': [ 'Deploy AI coding assistants to 100% of teams', 'Establish baseline productivity metrics', 'Identify 3 client projects for AI-accelerated pilots' ] } ] }

def main(): parser = argparse.ArgumentParser( description="Export strategy briefs to PowerPoint", formatter_class=argparse.RawDescriptionHelpFormatter ) parser.add_argument('--input', help='Input brief JSON file') parser.add_argument('--output', help='Output PPTX file', default='strategy_brief.pptx') parser.add_argument('--demo', action='store_true', help='Generate demo presentation')

args = parser.parse_args()

if not PPTX_AVAILABLE:
print("Error: python-pptx not installed")
print("Install with: pip install python-pptx")
sys.exit(1)

if args.demo:
data = get_demo_data()
elif args.input:
data = json.loads(Path(args.input).read_text())
else:
print("Provide --input file or use --demo", file=sys.stderr)
sys.exit(1)

exporter = StrategyPowerPointExporter()
exporter.export_from_brief(data)

output_path = Path(args.output)
exporter.save(output_path)

if name == "main": main()