Loading...
Loading...
Use this skill when creating ANY presentation, slide deck, or .pptx file. Triggers: "зроби презентацію", "create presentation", "make slides", "pitch deck", any topic + "presentation". Generates Python code using python-pptx library. Requirements: ≥15 slides, deep research, infographics, academic design, citations on every slide, speaker notes 800–2000 words per slide.
npx skill4agent add olpex/perfect-presentation-skill perfect-presentationpython-pptx□ Core definition and scope (with authoritative source)
□ Historical background and key milestones (with years)
□ Key statistics and quantitative data (with source + year)
□ Major current trends (with evidence)
□ Key organizations, researchers, or companies involved
□ Challenges and open problems
□ Future outlook and predictions
□ At least 2 real case studies with measurable outcomes
□ Expert quotes or frameworks (cited)
□ Contrasting viewpoints or debates in the fieldOn slide (bottom footer): [Author/Org, Year] or [URL shortened]
In notes (full citation): Author, A. (Year). Title. Publisher/URL. DOI if available.[Title of Document, Organization, Year]| # | Slide Type | Content Focus |
|---|---|---|
| 1 | Title Slide | Topic, subtitle, presenter, institution, date |
| 2 | Agenda | Numbered sections with brief descriptions |
| 3 | Executive Summary | 3–4 key takeaways from the entire presentation |
| 4 | Context & Relevance | Why this topic matters now; problem statement |
| 5 | Historical Background | Timeline of key developments |
| 6 | Key Statistics | 3–4 large-format data callouts with sources |
| 7 | Core Concept I | First major deep-dive topic |
| 8 | Core Concept I — Detail | Supporting evidence, examples, data |
| 9 | Core Concept II | Second major deep-dive topic |
| 10 | Core Concept II — Infographic | Chart, diagram, or process flow |
| 11 | Current Trends | 3–5 active trends with evidence |
| 12 | Trend Data | Chart or comparison supporting trends |
| 13 | Case Study | Real-world example: context → action → result |
| 14 | Challenges & Risks | Specific obstacles with citations |
| 15 | Opportunities | Forward-looking recommendations |
| 16 | Future Outlook | Predictions, scenarios, roadmap |
| 17 | Key Takeaways | Top 5 insights from the entire deck |
| 18 | References | Full bibliography (APA or similar) |
| 19 | Q&A / Thank You | Closing slide |
For complex/technical topics: expand to 20–25 slides with additional deep-dives
# DARK elements (headers, title slides, section dividers)
DARK_BG = "1C2B3A" # Deep navy — main dark background
DARK_TEXT = "F0F4F8" # Near-white text on dark backgrounds
ACCENT = "2E86AB" # Professional blue accent
# LIGHT elements (content slides)
LIGHT_BG = "FAFBFC" # Off-white slide body
LIGHT_TEXT = "1A1A2E" # Near-black text on light backgrounds
LIGHT_MUTED = "5A6A7A" # Muted gray for captions, citations
# Highlight / callout
HIGHLIGHT = "E8F4FD" # Very pale blue for card backgrounds
RULE_COLOR = "2E86AB" # Accent lines, borders| Domain | DARK_BG | ACCENT | Use when |
|---|---|---|---|
| Medical/Health | | | Biology, medicine, environment |
| Technology/AI | | | CS, AI, engineering |
| Economics/Business | | | Finance, economics, management |
| Social Sciences | | | Psychology, sociology, law |
| Default Academic | | | General / mixed topics |
from pptx.util import Pt
from pptx.dml.color import RGBColor
# Title fonts (serif — scholarly authority)
FONT_TITLE = "Georgia" # or "Palatino Linotype"
SIZE_TITLE = Pt(36)
SIZE_H1 = Pt(28)
SIZE_H2 = Pt(22)
# Body fonts (sans-serif — clean legibility)
FONT_BODY = "Calibri" # or "Arial"
SIZE_BODY = Pt(14)
SIZE_SMALL = Pt(11)
SIZE_CITATION = Pt(9)
SIZE_STAT = Pt(52) # For large stat calloutsfrom pptx.util import Inches, Cm, Pt
SLIDE_W = Inches(13.33) # Widescreen 16:9
SLIDE_H = Inches(7.5)
MARGIN_L = Inches(0.6)
MARGIN_R = Inches(0.6)
MARGIN_TOP = Inches(0.5)
CONTENT_W = Inches(12.1) # SLIDE_W - MARGIN_L - MARGIN_Rpip install python-pptxfrom pptx import Presentation
from pptx.util import Inches, Pt, Cm
from pptx.dml.color import RGBColor
from pptx.enum.text import PP_ALIGN
from pptx.oxml.ns import qn
from pptx.util import Emu
import copy
# ── Presentation setup ────────────────────────────────────────
prs = Presentation()
prs.slide_width = Inches(13.33)
prs.slide_height = Inches(7.5)
blank_layout = prs.slide_layouts[6] # Blank layout — always use this
# ── Design tokens ─────────────────────────────────────────────
C_DARK_BG = RGBColor(0x1C, 0x2B, 0x3A)
C_DARK_TEXT = RGBColor(0xF0, 0xF4, 0xF8)
C_ACCENT = RGBColor(0x2E, 0x86, 0xAB)
C_LIGHT_BG = RGBColor(0xFA, 0xFB, 0xFC)
C_LIGHT_TEXT = RGBColor(0x1A, 0x1A, 0x2E)
C_MUTED = RGBColor(0x5A, 0x6A, 0x7A)
C_HIGHLIGHT = RGBColor(0xE8, 0xF4, 0xFD)
C_WHITE = RGBColor(0xFF, 0xFF, 0xFF)
FONT_TITLE = "Georgia"
FONT_BODY = "Calibri"
# ── Core helper: set paragraph text with full formatting ──────
def add_text_box(slide, text, left, top, width, height,
font_name=FONT_BODY, font_size=Pt(14),
bold=False, italic=False, color=None,
align=PP_ALIGN.LEFT, word_wrap=True):
txBox = slide.shapes.add_textbox(left, top, width, height)
tf = txBox.text_frame
tf.word_wrap = word_wrap
p = tf.paragraphs[0]
p.alignment = align
run = p.add_run()
run.text = text
run.font.name = font_name
run.font.size = font_size
run.font.bold = bold
run.font.italic = italic
if color:
run.font.color.rgb = color
return txBox
# ── Helper: add a filled rectangle ───────────────────────────
def add_rect(slide, left, top, width, height, fill_color, line_color=None):
shape = slide.shapes.add_shape(
1, # MSO_SHAPE_TYPE.RECTANGLE
left, top, width, height
)
shape.fill.solid()
shape.fill.fore_color.rgb = fill_color
if line_color:
shape.line.color.rgb = line_color
else:
shape.line.fill.background() # no border
return shape
# ── Helper: set slide background color ───────────────────────
def set_bg(slide, color: RGBColor):
bg = slide.background
fill = bg.fill
fill.solid()
fill.fore_color.rgb = color
# ── Helper: add speaker notes ────────────────────────────────
def add_notes(slide, notes_text: str):
"""Add speaker notes to a slide. notes_text must be 800–2000 words."""
notes_slide = slide.notes_slide
tf = notes_slide.notes_text_frame
tf.text = notes_text
# ── Helper: add citation footer ──────────────────────────────
def add_citation(slide, citation_text: str):
"""Add a small citation line at the bottom of the slide."""
add_text_box(
slide, f"Source: {citation_text}",
left=Inches(0.6), top=Inches(7.05),
width=Inches(12.1), height=Inches(0.35),
font_name=FONT_BODY, font_size=Pt(8),
italic=True, color=C_MUTED,
align=PP_ALIGN.LEFT
)
# ── Helper: add left accent bar (visual motif) ───────────────
def add_accent_bar(slide):
add_rect(slide,
left=Inches(0), top=Inches(0),
width=Inches(0.1), height=Inches(7.5),
fill_color=C_ACCENT
)
# ── Helper: add slide number ─────────────────────────────────
def add_slide_number(slide, number: int, total: int):
add_text_box(
slide, f"{number} / {total}",
left=Inches(12.0), top=Inches(7.1),
width=Inches(1.2), height=Inches(0.3),
font_name=FONT_BODY, font_size=Pt(8),
color=C_MUTED, align=PP_ALIGN.RIGHT
)def build_title_slide(prs, title, subtitle, presenter, institution, date):
slide = prs.slides.add_slide(prs.slide_layouts[6])
set_bg(slide, C_DARK_BG)
# Accent line (horizontal, centered)
add_rect(slide,
left=Inches(0.6), top=Inches(3.9),
width=Inches(12.1), height=Inches(0.04),
fill_color=C_ACCENT
)
# Main title
add_text_box(
slide, title,
left=Inches(0.8), top=Inches(1.2),
width=Inches(11.7), height=Inches(1.8),
font_name=FONT_TITLE, font_size=Pt(40),
bold=True, color=C_DARK_TEXT,
align=PP_ALIGN.LEFT
)
# Subtitle
add_text_box(
slide, subtitle,
left=Inches(0.8), top=Inches(3.1),
width=Inches(11.7), height=Inches(0.7),
font_name=FONT_BODY, font_size=Pt(18),
color=C_ACCENT, align=PP_ALIGN.LEFT
)
# Presenter + institution + date
add_text_box(
slide, f"{presenter} | {institution} | {date}",
left=Inches(0.8), top=Inches(4.1),
width=Inches(11.7), height=Inches(0.4),
font_name=FONT_BODY, font_size=Pt(12),
color=C_MUTED, align=PP_ALIGN.LEFT
)
add_notes(slide, """[TITLE SLIDE NOTES — 800+ words]
Introduce yourself and your credentials relevant to this topic. Explain why this presentation matters right now and what the audience will gain. State the central question the presentation answers. Outline the structure briefly. Set the tone: this is a rigorous but accessible exploration of [TOPIC].
Mention: How long will this take? Will there be Q&A? Where can they access the slides?
Transition: "Let's begin with an overview of what we'll cover today."
Sources for this slide: [none — title slide]""")
return slidedef build_content_slide(prs, title, bullets, citation, notes_text,
slide_num, total_slides):
"""
bullets: list of strings — each becomes one bullet point
citation: str — source shown at bottom
notes_text: str — 800–2000 words
"""
slide = prs.slides.add_slide(prs.slide_layouts[6])
set_bg(slide, C_LIGHT_BG)
add_accent_bar(slide)
# Title bar (dark background strip)
add_rect(slide,
left=Inches(0), top=Inches(0),
width=Inches(13.33), height=Inches(1.1),
fill_color=C_DARK_BG
)
add_text_box(
slide, title,
left=Inches(0.3), top=Inches(0.1),
width=Inches(12.7), height=Inches(0.9),
font_name=FONT_TITLE, font_size=Pt(26),
bold=True, color=C_DARK_TEXT,
align=PP_ALIGN.LEFT
)
# Bullet points
txBox = slide.shapes.add_textbox(
Inches(0.7), Inches(1.25),
Inches(11.9), Inches(5.5)
)
tf = txBox.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.space_after = Pt(8)
run = p.runs[0]
run.font.name = FONT_BODY
run.font.size = Pt(14)
run.font.color.rgb = C_LIGHT_TEXT
add_citation(slide, citation)
add_slide_number(slide, slide_num, total_slides)
add_notes(slide, notes_text)
return slidedef build_stats_slide(prs, title, stats, citation, notes_text,
slide_num, total_slides):
"""
stats: list of dicts:
[{"number": "87%", "label": "Adoption Rate", "context": "by 2025 (McKinsey, 2023)"}]
Max 4 stats recommended.
"""
slide = prs.slides.add_slide(prs.slide_layouts[6])
set_bg(slide, C_DARK_BG)
add_accent_bar(slide)
# Title
add_text_box(
slide, title,
left=Inches(0.3), top=Inches(0.15),
width=Inches(12.7), height=Inches(0.8),
font_name=FONT_TITLE, font_size=Pt(28),
bold=True, color=C_DARK_TEXT,
align=PP_ALIGN.LEFT
)
# Stat boxes
n = len(stats)
box_w = (12.1 - 0.3 * (n - 1)) / n
start_x = 0.6
for i, stat in enumerate(stats):
x = start_x + i * (box_w + 0.3)
# Card background
add_rect(slide,
left=Inches(x), top=Inches(1.2),
width=Inches(box_w), height=Inches(5.8),
fill_color=RGBColor(0x25, 0x3C, 0x50)
)
# Big number
add_text_box(
slide, stat["number"],
left=Inches(x + 0.1), top=Inches(1.5),
width=Inches(box_w - 0.2), height=Inches(2.0),
font_name=FONT_TITLE, font_size=Pt(56),
bold=True, color=C_ACCENT,
align=PP_ALIGN.CENTER
)
# Label
add_text_box(
slide, stat["label"],
left=Inches(x + 0.1), top=Inches(3.6),
width=Inches(box_w - 0.2), height=Inches(0.7),
font_name=FONT_BODY, font_size=Pt(15),
bold=True, color=C_DARK_TEXT,
align=PP_ALIGN.CENTER
)
# Context / source
add_text_box(
slide, stat["context"],
left=Inches(x + 0.1), top=Inches(4.4),
width=Inches(box_w - 0.2), height=Inches(0.6),
font_name=FONT_BODY, font_size=Pt(10),
italic=True, color=C_MUTED,
align=PP_ALIGN.CENTER
)
add_citation(slide, citation)
add_slide_number(slide, slide_num, total_slides)
add_notes(slide, notes_text)
return slidedef build_two_col_slide(prs, title, left_header, left_points,
right_header, right_points,
citation, notes_text, slide_num, total_slides):
slide = prs.slides.add_slide(prs.slide_layouts[6])
set_bg(slide, C_LIGHT_BG)
add_accent_bar(slide)
# Title bar
add_rect(slide,
left=Inches(0), top=Inches(0),
width=Inches(13.33), height=Inches(1.1),
fill_color=C_DARK_BG
)
add_text_box(
slide, title,
left=Inches(0.3), top=Inches(0.1),
width=Inches(12.7), height=Inches(0.9),
font_name=FONT_TITLE, font_size=Pt(26),
bold=True, color=C_DARK_TEXT
)
# Divider line between columns
add_rect(slide,
left=Inches(6.9), top=Inches(1.2),
width=Inches(0.04), height=Inches(5.8),
fill_color=C_ACCENT
)
col_w = Inches(5.9)
for col_x, header, points in [
(Inches(0.5), left_header, left_points),
(Inches(7.1), right_header, right_points),
]:
# Column header
add_text_box(
slide, header,
left=col_x, top=Inches(1.25),
width=col_w, height=Inches(0.5),
font_name=FONT_BODY, font_size=Pt(16),
bold=True, color=C_ACCENT
)
# Bullets
txBox = slide.shapes.add_textbox(col_x, Inches(1.85), col_w, Inches(5.0))
tf = txBox.text_frame
tf.word_wrap = True
for i, pt in enumerate(points):
p = tf.paragraphs[0] if i == 0 else tf.add_paragraph()
p.text = f"• {pt}"
p.space_after = Pt(7)
run = p.runs[0]
run.font.name = FONT_BODY
run.font.size = Pt(13)
run.font.color.rgb = C_LIGHT_TEXT
add_citation(slide, citation)
add_slide_number(slide, slide_num, total_slides)
add_notes(slide, notes_text)
return slidedef build_timeline_slide(prs, title, events, citation, notes_text,
slide_num, total_slides):
"""
events: [{"year": "2017", "title": "...", "desc": "..."}]
Max 6 events recommended.
"""
slide = prs.slides.add_slide(prs.slide_layouts[6])
set_bg(slide, C_LIGHT_BG)
add_accent_bar(slide)
# Title bar
add_rect(slide,
left=Inches(0), top=Inches(0),
width=Inches(13.33), height=Inches(1.1),
fill_color=C_DARK_BG
)
add_text_box(
slide, title,
left=Inches(0.3), top=Inches(0.1),
width=Inches(12.7), height=Inches(0.9),
font_name=FONT_TITLE, font_size=Pt(26),
bold=True, color=C_DARK_TEXT
)
# Horizontal timeline line
line_y = Inches(4.0)
add_rect(slide,
left=Inches(0.8), top=line_y,
width=Inches(11.7), height=Inches(0.06),
fill_color=C_ACCENT
)
n = len(events)
step = 11.7 / (n - 1) if n > 1 else 11.7
for i, ev in enumerate(events):
x = 0.8 + i * step
is_top = (i % 2 == 0)
# Dot
dot = slide.shapes.add_shape(
9, # OVAL
Inches(x - 0.12), line_y - Inches(0.12),
Inches(0.24), Inches(0.24)
)
dot.fill.solid()
dot.fill.fore_color.rgb = C_ACCENT
dot.line.fill.background()
# Year label
add_text_box(
slide, ev["year"],
left=Inches(x - 0.5), top=line_y + (Inches(0.2) if is_top else -Inches(0.55)),
width=Inches(1.0), height=Inches(0.35),
font_name=FONT_BODY, font_size=Pt(11),
bold=True, color=C_ACCENT,
align=PP_ALIGN.CENTER
)
# Event title
add_text_box(
slide, ev["title"],
left=Inches(x - 0.75),
top=line_y + (Inches(0.55) if is_top else -Inches(1.8)),
width=Inches(1.5), height=Inches(1.2),
font_name=FONT_BODY, font_size=Pt(11),
color=C_LIGHT_TEXT,
align=PP_ALIGN.CENTER
)
add_citation(slide, citation)
add_slide_number(slide, slide_num, total_slides)
add_notes(slide, notes_text)
return slidedef build_case_study_slide(prs, title, org, context, action, result,
lesson, citation, notes_text,
slide_num, total_slides):
slide = prs.slides.add_slide(prs.slide_layouts[6])
set_bg(slide, C_LIGHT_BG)
add_accent_bar(slide)
# Title bar
add_rect(slide,
left=Inches(0), top=Inches(0),
width=Inches(13.33), height=Inches(1.1),
fill_color=C_DARK_BG
)
add_text_box(
slide, title,
left=Inches(0.3), top=Inches(0.1),
width=Inches(12.7), height=Inches(0.9),
font_name=FONT_TITLE, font_size=Pt(26),
bold=True, color=C_DARK_TEXT
)
# Org badge
add_rect(slide,
left=Inches(0.5), top=Inches(1.2),
width=Inches(3.5), height=Inches(0.55),
fill_color=C_ACCENT
)
add_text_box(
slide, org,
left=Inches(0.5), top=Inches(1.2),
width=Inches(3.5), height=Inches(0.55),
font_name=FONT_BODY, font_size=Pt(14),
bold=True, color=C_WHITE,
align=PP_ALIGN.CENTER
)
sections = [
("Context", context, Inches(1.85)),
("Action", action, Inches(3.05)),
("Result", result, Inches(4.25)),
("Lesson", lesson, Inches(5.45)),
]
for label, text, y in sections:
add_rect(slide,
left=Inches(0.5), top=y,
width=Inches(1.1), height=Inches(0.9),
fill_color=C_HIGHLIGHT
)
add_text_box(
slide, label,
left=Inches(0.5), top=y + Inches(0.25),
width=Inches(1.1), height=Inches(0.4),
font_name=FONT_BODY, font_size=Pt(11),
bold=True, color=C_ACCENT,
align=PP_ALIGN.CENTER
)
add_text_box(
slide, text,
left=Inches(1.75), top=y + Inches(0.1),
width=Inches(11.0), height=Inches(0.75),
font_name=FONT_BODY, font_size=Pt(13),
color=C_LIGHT_TEXT
)
add_citation(slide, citation)
add_slide_number(slide, slide_num, total_slides)
add_notes(slide, notes_text)
return slidedef build_references_slide(prs, references, slide_num, total_slides):
"""
references: list of full citation strings (APA format recommended)
"""
slide = prs.slides.add_slide(prs.slide_layouts[6])
set_bg(slide, C_LIGHT_BG)
add_accent_bar(slide)
# Title bar
add_rect(slide,
left=Inches(0), top=Inches(0),
width=Inches(13.33), height=Inches(1.1),
fill_color=C_DARK_BG
)
add_text_box(
slide, "References",
left=Inches(0.3), top=Inches(0.1),
width=Inches(12.7), height=Inches(0.9),
font_name=FONT_TITLE, font_size=Pt(26),
bold=True, color=C_DARK_TEXT
)
txBox = slide.shapes.add_textbox(
Inches(0.7), Inches(1.25),
Inches(11.9), Inches(5.9)
)
tf = txBox.text_frame
tf.word_wrap = True
for i, ref in enumerate(references):
p = tf.paragraphs[0] if i == 0 else tf.add_paragraph()
p.text = ref
p.space_after = Pt(5)
run = p.runs[0]
run.font.name = FONT_BODY
run.font.size = Pt(11)
run.font.color.rgb = C_LIGHT_TEXT
add_slide_number(slide, slide_num, total_slides)
add_notes(slide, "This slide lists all sources cited throughout the presentation. Encourage the audience to consult these references for deeper study. Mention that the slide deck with clickable links will be shared after the session.")
return slide| Standard | Requirement |
|---|---|
| Length | 800–2000 words per slide |
| Tone | Popular-science + empathetic (accessible to beginners) |
| Content | Explain every bullet point on the slide in detail |
| Citations | Include full source citations in notes |
| Structure | Opening → Key points → Evidence → Transition |
| Transition | Every note ends with a bridge to the next slide |
SLIDE [N]: [TITLE]
─────────────────────────────────────────────────────
OPENING:
[2–3 sentences that introduce this slide's theme and connect to the previous one]
EXPLANATION OF EACH POINT:
▸ [Bullet point 1 — exact text from slide]:
[3–5 sentences explaining what this means, why it matters, and how it connects
to the broader topic. Use an analogy or real-world example for clarity.
Mention any nuance or debate in the field.]
▸ [Bullet point 2 — exact text from slide]:
[Same format: explanation + example + nuance]
▸ [Bullet point 3]:
[Same format]
KEY INSIGHT:
[1–2 sentences synthesizing what these points collectively tell us]
COMMON MISCONCEPTION (if applicable):
[Address something students often get wrong about this topic]
REAL-WORLD CONNECTION:
[A concrete example, news story, or application that makes this tangible]
SOURCES CITED ON THIS SLIDE:
- [Full APA citation for source 1]
- [Full APA citation for source 2]
- [URL if applicable]
TRANSITION:
"Having established [summary of this slide], we can now turn to [next slide topic]..."# On-slide citation (brief):
"McKinsey Global Institute, 2023"
"OECD (2022). https://doi.org/..."
"Nature, Vol. 612, 2022"
# In notes (full APA):
"McKinsey Global Institute. (2023). The state of AI in 2023. McKinsey & Company. https://www.mckinsey.com/..."
"OECD. (2022). Artificial Intelligence in Society. OECD Publishing. https://doi.org/10.1787/eedfee77-en"CONTENT
□ ≥15 slides generated
□ Every slide has real, specific content (no "Lorem ipsum" or vague text)
□ Every slide has at least one citation
□ References slide includes all cited sources
□ Topic stays focused — no off-topic tangents
DESIGN
□ Title/section slides use dark background (C_DARK_BG)
□ Content slides use light background (C_LIGHT_BG)
□ All titles use FONT_TITLE (Georgia/Palatino)
□ All body text uses FONT_BODY (Calibri/Arial)
□ Accent bar on every content slide
□ Slide numbers on all slides except title
□ No text overflow — all text boxes have sufficient height
SPEAKER NOTES
□ Every slide has notes
□ Every notes section is 800–2000 words
□ Every bullet on every slide is explained in notes
□ Full citations in notes
□ Transition phrase at end of every notes section
PYTHON CODE
□ Code runs without errors: python presentation.py
□ Output file is valid .pptx
□ All slides render correctly# presentation.py
# Topic: [TOPIC]
# Language: [LANGUAGE]
# Generated with: python-pptx
from pptx import Presentation
from pptx.util import Inches, Pt
from pptx.dml.color import RGBColor
from pptx.enum.text import PP_ALIGN
# [paste all helpers from Phase 4]
# [paste all pattern functions from Phase 5]
TOTAL_SLIDES = 19 # update to actual count
prs = Presentation()
prs.slide_width = Inches(13.33)
prs.slide_height = Inches(7.5)
# ── Slide 1: Title ────────────────────────────────────────────
build_title_slide(prs,
title="[FULL TOPIC TITLE]",
subtitle="[DESCRIPTIVE SUBTITLE]",
presenter="[NAME]",
institution="[INSTITUTION / COURSE]",
date="[DATE]"
)
# ── Slide 2: Agenda ───────────────────────────────────────────
build_content_slide(prs,
title="Agenda",
bullets=[
"1. Context & Background",
"2. Core Concepts",
"3. Current Trends & Data",
"4. Case Study",
"5. Challenges & Opportunities",
"6. Future Outlook",
"7. Key Takeaways & References"
],
citation="[no external source]",
notes_text="""[Full notes 800+ words]""",
slide_num=2, total_slides=TOTAL_SLIDES
)
# ... continue for all 15–19 slides ...
# ── Save ──────────────────────────────────────────────────────
prs.save("presentation.pptx")
print("✅ Presentation saved: presentation.pptx")# Install dependency
pip install python-pptx
# Generate presentation
python presentation.py
# Verify output
python -c "
from pptx import Presentation
prs = Presentation('presentation.pptx')
print(f'Slides: {len(prs.slides)}')
for i, slide in enumerate(prs.slides, 1):
notes = slide.notes_slide.notes_text_frame.text
print(f' Slide {i}: {len(notes)} chars in notes')
"