You can automate your weekly PowerPoint reports from Excel data with 10 lines of Python — no manual formatting, no copy-pasting charts, no last-minute slide tweaks.This guide shows two approaches: python-pptx for full control, and SlideForge's API for consulting-quality output in seconds. Both connect to pandas for data extraction and work with any scheduler (cron, Airflow, n8n).
The problem: manual report decks
Every Monday morning, someone on your team opens an Excel file, copies numbers into a PowerPoint template, screenshots a chart, pastes it, adjusts the alignment, fixes the font, updates the date. For a 15-slide weekly report, this takes 1-2 hours. Every week. Forever.
The fix is a Python script that reads the data, generates the slides, and emails the deck. Run it via cron at 7 AM Monday. Your team walks into a meeting with the deck already in their inbox.
Approach 1: python-pptx (free, full control)
python-pptx gives you pixel-level control over every shape. The tradeoff: you manage all layout, fonts, colors, and alignment yourself.
pip install python-pptx pandas openpyxlimport pandas as pd
from pptx import Presentation
from pptx.util import Inches, Pt
from pptx.dml.color import RGBColor
# 1. Read data from Excel
df = pd.read_excel("weekly_metrics.xlsx", sheet_name="Summary")
# 2. Create presentation
prs = Presentation()
prs.slide_width = Inches(13.333)
prs.slide_height = Inches(7.5)
# 3. Title slide
slide = prs.slides.add_slide(prs.slide_layouts[6]) # blank
txBox = slide.shapes.add_textbox(Inches(1), Inches(2), Inches(10), Inches(2))
tf = txBox.text_frame
tf.text = "Weekly Report — Week " + str(df['week'].iloc[0])
tf.paragraphs[0].font.size = Pt(36)
tf.paragraphs[0].font.color.rgb = RGBColor(0x1E, 0x3A, 0x5F)
# 4. KPI slide — manually position each metric
slide = prs.slides.add_slide(prs.slide_layouts[6])
metrics = [
("Revenue", "$" + format(df['revenue'].iloc[0], ',.0f')),
("Clients", format(df['clients'].iloc[0], ',')),
("Margin", format(df['margin'].iloc[0], '.1f') + "%"),
]
for i, (label, value) in enumerate(metrics):
left = Inches(1 + i * 4)
txBox = slide.shapes.add_textbox(left, Inches(2), Inches(3), Inches(1))
txBox.text_frame.text = value
txBox.text_frame.paragraphs[0].font.size = Pt(32)
txBox.text_frame.paragraphs[0].font.bold = True
txBox = slide.shapes.add_textbox(left, Inches(3), Inches(3), Inches(0.5))
txBox.text_frame.text = label
txBox.text_frame.paragraphs[0].font.size = Pt(14)
# ... repeat for trend arrows, colors, alignment
# 5. Save
prs.save("weekly_report.pptx")
# That's ~60 lines for ONE slide type. A 15-slide deck = 500+ lines.This works, but maintaining 500+ lines of layout code per report is a burden. Every font change, color update, or new metric means touching Python code.
Approach 2: SlideForge API (10 lines, consulting-quality)
SlideForge handles the layout, design, and rendering. You send data, you get back a .pptx.
import pandas as pd
import requests
# 1. Read data from Excel
df = pd.read_excel("weekly_metrics.xlsx", sheet_name="Summary")
row = df.iloc[0]
# 2. Generate KPI slide via API ($0.03, <1s)
resp = requests.post(
"https://api.slideforge.dev/v1/render",
headers={"Authorization": "Bearer sf_live_YOUR_KEY"},
json={
"template": "kpi_dashboard",
"params": {
"title": "Week %d Performance" % int(row['week']),
"metrics": [
{"value": "$%s" % format(row['revenue'], ',.0f'), "label": "Revenue",
"trend": "%+.1f%%" % row['revenue_growth'], "trend_dir": "up"},
{"value": "%s" % format(int(row['clients']), ','), "label": "Clients",
"trend": "%+.1f%%" % row['client_growth'], "trend_dir": "up"},
{"value": "%.1f%%" % row['margin'], "label": "Margin",
"trend": "%+.1fpp" % row['margin_change'], "trend_dir": "down"},
],
},
"theme_id": "consulting_blue",
"chrome": {"footer": "Confidential", "kicker": "WEEKLY REVIEW"}
},
)
data = resp.json()
print("Slide ready:", data['pptx_url']) # download linkThat's one slide for $0.03. For a full deck, use the deck endpoint to generate all slides in parallel:
# Generate a 5-slide weekly report deck ($0.15-$0.25, ~3s)
resp = requests.post(
"https://api.slideforge.dev/v1/deck",
headers={"Authorization": "Bearer sf_live_YOUR_KEY"},
json={
"slides": [
{"render": {"template": "title_slide", "brief": "Weekly Report — Week 14"}},
{"render": {"template": "kpi_dashboard", "params": kpi_data}},
{"render": {"template": "bar_chart", "params": chart_data}},
{"render": {"template": "data_table", "params": table_data}},
{"render": {"template": "next_steps", "params": actions_data}},
],
"theme_id": "consulting_blue",
"title": "Weekly Report W14"
},
)
deck = resp.json()
# Poll GET /v1/jobs/<job_id> for completion -> pptx_urlScheduling the pipeline
Cron (simplest)
# Run every Monday at 7 AM
0 7 * * 1 /usr/bin/python3 /opt/reports/weekly_report.pyGitHub Actions (free for public repos)
# .github/workflows/weekly-report.yml
name: Weekly Report
on:
schedule:
- cron: '0 7 * * 1' # Monday 7 AM UTC
jobs:
generate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: pip install requests pandas openpyxl
- run: python generate_weekly_report.py
env:
SLIDEFORGE_API_KEY: ${{ secrets.SLIDEFORGE_API_KEY }}Airflow / n8n / Zapier
Any orchestrator that can run Python works. Airflow: create a DAG with a PythonOperator. n8n: use the Code node. Zapier: use the Code by Zapier step (Python).
Adding email delivery
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email import encoders
# Download the generated PPTX
pptx_bytes = requests.get(data["pptx_url"]).content
# Send via SMTP
msg = MIMEMultipart()
msg["Subject"] = "Weekly Report — Week " + str(int(row['week']))
msg["From"] = "reports@yourcompany.com"
msg["To"] = "team@yourcompany.com"
attachment = MIMEBase("application",
"vnd.openxmlformats-officedocument.presentationml.presentation")
attachment.set_payload(pptx_bytes)
encoders.encode_base64(attachment)
attachment.add_header("Content-Disposition",
"attachment; filename=weekly_report.pptx")
msg.attach(attachment)
with smtplib.SMTP("smtp.yourcompany.com", 587) as server:
server.starttls()
server.login("reports@yourcompany.com", "password")
server.send_message(msg)Cost comparison
A 15-slide weekly report using template rendering:
- python-pptx: Free, but 500+ lines of layout code. 2-4 hours initial build, ongoing maintenance.
- SlideForge templates: $0.03-$0.05/slide = $0.45-$0.75/week = $23-39/year. Zero layout code.
- Manual creation: 1-2 hours/week × $50/hr = $2,500-5,000/year. The most expensive option by far.
Browse templates for reports
50 templates cover the most common report slide types: browse the gallery. Popular for reports:
kpi_dashboard— 2-6 metric cards with trendsdata_table— structured data in rows and columnsbar_chart/stacked_bar— visual comparisonswaterfall_chart— revenue bridges, P&L walkthroughstimeline— milestones and deadlines
Get started
Sign up for $3 free credit (enough for 60-100 template slides), or read the render API docs. Need help picking templates? Read the briefs guide.