How to Automatically Generate and Send Weekly Reports via Email Using Python and Claude (2026 Guide)

How to Automatically Generate and Send Weekly Reports via Email Using Python and Claude (2026 Guide)
Operational Guide
July 14, 2026

How to Automatically Generate and Send Weekly Reports via Email Using Python and Claude

Stop spending Friday afternoons copy-pasting numbers into email templates. Use Python and Claude to automatically compile your metrics, write the executive summary, and send a polished HTML report — every week, hands-free.
Automated Weekly Email Report Generator Using Python and Claude

01. The Friday Afternoon Problem

Every Friday at 4:30 PM, I became a human spreadsheet. I opened twelve browser tabs, copy-pasted numbers into a Google Doc, wrote three sentences of commentary, and hit Send. Every single week.

It started as a quick ten-minute task. Then the team grew, the metrics expanded from five KPIs to twenty-three, and suddenly I was spending an hour every Friday afternoon doing the same mechanical work: pull sales data from our dashboard, pull support ticket counts from Zendesk, pull website sessions from Google Analytics, paste them all into an email template, and write a summary paragraph that said roughly the same thing every week with slightly different numbers.

The worst part wasn't the time. It was the context switching. That Friday afternoon reporting slot was prime problem-solving time — the kind of focused, uninterrupted hour where you could actually think. Instead it disappeared into a mindless data assembly job that any script could handle.

I finally automated it on a Tuesday evening when the Friday report was particularly late because I'd been stuck in meetings all day. The script took about two hours to build the first version. It has now been running automatically for over six months. It sends a polished, accurate HTML report every Friday morning at 9 AM — before I've even opened my laptop. This guide shows you exactly how to build the same thing.

Return to Operational Guide Outline

02. What This Automation Actually Does

Not magic — just four steps that your computer executes while you're doing something more important.

The automation pipeline has four stages, each simple on its own but powerful in combination. First, it reads your weekly operational data from a source you define — this could be a CSV export, a Google Sheet, a database query, or a JSON file from your analytics tool. Second, it aggregates the key metrics: total revenue, units sold, resolution rates, and whatever else matters to your team. Third, it sends those aggregated numbers to Claude and asks for a concise executive summary in plain English. Fourth, it assembles a clean HTML email — complete with a formatted data table and the AI-written summary — and sends it via SMTP to your team distribution list.

The result is a report that looks like it was written by a diligent analyst who reviewed all the data and distilled the key insights. Because Claude did exactly that — reviewed all the data and distilled the key insights. The script takes about 8 to 12 seconds to run from start to finish, including the API call to Claude.

Return to Operational Guide Outline

03. What You Need to Get Started

Three Python libraries and an email account. That's genuinely the full list.

anthropic
AI Summary Engine

Official Anthropic SDK. Sends your aggregated metrics to Claude and returns a plain-English executive summary paragraph.

smtplib + email
Email Sender

Both built into Python's standard library. Construct and send HTML email messages via any SMTP server — Gmail, Outlook, SendGrid, etc.

json / csv
Data Reader

Also built-in. Read your weekly data from CSV exports or JSON files produced by your analytics dashboard or database.

schedule (optional)
Cron Replacement

Pure Python job scheduler. Lets you define "run every Friday at 09:00" in plain code without touching cron syntax on Linux or Task Scheduler on Windows.

# TERMINAL — Install external dependencies
pip install anthropic schedule

You'll also need a Claude API key from console.anthropic.com and a Gmail app password (or any SMTP credentials). Store both as environment variables:

# TERMINAL (Windows PowerShell)
$env:ANTHROPIC_API_KEY = "sk-ant-your-key-here"
$env:SMTP_PASSWORD     = "your-gmail-app-password"
Return to Operational Guide Outline

04. Step 1 — Load and Aggregate Your Data

Before Claude can summarize anything, you need to know what the numbers actually are. This step turns raw data into clean metrics.

# PYTHON — Data Loading and Aggregation
import json
from datetime import date

def load_weekly_data(filepath: str) -> dict:
    """Load your weekly operational data from a JSON file."""
    with open(filepath, encoding="utf-8") as f:
        return json.load(f)

def aggregate_metrics(data: dict) -> dict:
    """Compute summary metrics from raw weekly data."""
    total_revenue = sum(item["revenue"] for item in data["sales"])
    total_units   = sum(item["units"]   for item in data["sales"])
    top_product   = max(data["sales"], key=lambda x: x["revenue"])

    tickets = data["support_tickets"]
    resolution_rate = round(
        tickets["resolved"] / tickets["opened"] * 100, 1
    ) if tickets["opened"] > 0 else 0.0

    return {
        "week_ending":     data.get("week_ending", str(date.today())),
        "total_revenue":   total_revenue,
        "total_units":     total_units,
        "top_product":     top_product["product"],
        "resolution_rate": resolution_rate,
        "sessions":        data["website_traffic"]["sessions"],
        "bounce_rate":     data["website_traffic"]["bounce_rate"],
    }

# Usage
data    = load_weekly_data("weekly_data.json")
metrics = aggregate_metrics(data)
print(f"Total Revenue: ${metrics['total_revenue']:.2f}")
print(f"Resolution Rate: {metrics['resolution_rate']}%")

💡 Pro tip: If your data lives in a CSV file, replace json.load() with the csv.DictReader pattern. If it's in a Google Sheet, the gspread library gives you a one-line sheet-to-dict conversion. The rest of the pipeline stays identical.

Return to Operational Guide Outline

05. Step 2 — Let Claude Write the Summary

The most time-consuming part of any weekly report is the commentary. Claude handles it in under three seconds.

The key to a useful AI summary is giving Claude the right context in the prompt. Don't just dump raw numbers — give it the business context so the summary reads like it came from someone who understands what the numbers mean. Compare these two prompts:

Weak prompt: "Summarize this data: revenue 4528, units 123, rate 78.6%."
Strong prompt: "You are an operations analyst. Review this week's performance data and write a 2-sentence executive summary highlighting what went well and any metric that warrants attention."

# PYTHON — Claude Executive Summary
import anthropic
import os

def generate_summary_with_claude(metrics: dict, raw_data: dict) -> str:
    """Send weekly metrics to Claude. Returns a plain-English summary."""
    client = anthropic.Anthropic(
        api_key=os.environ.get("ANTHROPIC_API_KEY")
    )

    prompt = f"""You are an operations analyst writing a weekly executive summary.
Review this week's business performance data and write exactly 3 sentences:
1. Overall revenue and top product performance.
2. Support team efficiency and outstanding issues.
3. One forward-looking observation or recommendation.

Write in confident, professional business English. No bullet points. Plain prose only.

WEEKLY DATA:
- Total Revenue: ${metrics['total_revenue']:.2f}
- Total Units Sold: {metrics['total_units']}
- Top Revenue Product: {metrics['top_product']}
- Support Resolution Rate: {metrics['resolution_rate']}%
- Website Sessions: {metrics['sessions']:,}
- Bounce Rate: {metrics['bounce_rate']*100:.0f}%
- Week Ending: {metrics['week_ending']}
"""

    response = client.messages.create(
        model="claude-sonnet-4-5",
        max_tokens=512,
        messages=[{"role": "user", "content": prompt}]
    )
    return response.content[0].text

# Usage
summary = generate_summary_with_claude(metrics, data)
print(f"Summary ({len(summary)} chars):\n{summary}")
Return to Operational Guide Outline

06. Step 3 — Build the HTML Email

A plain-text report gets ignored. A clean HTML email with a table and color gets read.

The email module in Python's standard library handles HTML emails natively. You construct a MIMEMultipart("alternative") message, attach your HTML string as the "html" part, and the recipient's email client renders it as a formatted report — complete with your data table, the AI summary highlighted in a colored block, and your company branding.

# PYTHON — HTML Email Builder
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

def build_html_email(metrics: dict, summary: str, sales_data: list) -> str:
    """Build a clean, styled HTML email body."""
    rows = "".join(
        f"<tr><td>{item['product']}</td>"
        f"<td>{item['units']}</td>"
        f"<td>${item['revenue']:.2f}</td></tr>"
        for item in sales_data
    )

    return f"""
<html><body style="font-family:Arial,sans-serif;max-width:650px;margin:auto;">
  <div style="background:#065f46;color:#fff;padding:30px;border-radius:12px 12px 0 0;">
    <h1 style="margin:0;">Weekly Operations Report</h1>
    <p style="margin:8px 0 0;opacity:0.8;">Week Ending: {metrics['week_ending']}</p>
  </div>
  <div style="background:#f0fdf4;padding:25px;border-left:4px solid #10b981;">
    <strong>AI Executive Summary</strong><br><br>{summary}
  </div>
  <div style="padding:25px;">
    <h2 style="color:#065f46;">Sales Performance</h2>
    <table width="100%" cellpadding="10" style="border-collapse:collapse;">
      <tr style="background:#d1fae5;">
        <th align="left">Product</th>
        <th>Units</th><th>Revenue</th>
      </tr>
      {rows}
      <tr style="border-top:2px solid #10b981;font-weight:bold;">
        <td>TOTAL</td>
        <td>{metrics['total_units']}</td>
        <td>${metrics['total_revenue']:.2f}</td>
      </tr>
    </table>
    <p style="margin-top:20px;">
      Support Rate: <strong>{metrics['resolution_rate']}%</strong> |
      Sessions: <strong>{metrics['sessions']:,}</strong>
    </p>
  </div>
</body></html>
"""

def create_email_message(to: str, subject: str, html_body: str) -> MIMEMultipart:
    msg = MIMEMultipart("alternative")
    msg["Subject"] = subject
    msg["From"]    = "reports@yourcompany.com"
    msg["To"]      = to
    msg.attach(MIMEText(html_body, "html", "utf-8"))
    return msg
Return to Operational Guide Outline

07. The Complete Script (Copy-Paste Ready)

Everything from Steps 1 through 6, assembled into one clean script you can deploy immediately.

# weekly_report.py — COMPLETE PIPELINE
import json, os, smtplib
import anthropic
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from datetime import date

SMTP_HOST = "smtp.gmail.com"
SMTP_PORT = 587
FROM_EMAIL = os.environ.get("REPORT_FROM_EMAIL", "reports@yourcompany.com")
TO_EMAIL   = os.environ.get("REPORT_TO_EMAIL",   "team@yourcompany.com")
SMTP_PASS  = os.environ.get("SMTP_PASSWORD")
API_KEY    = os.environ.get("ANTHROPIC_API_KEY")

def load_and_aggregate(filepath: str) -> tuple:
    with open(filepath, encoding="utf-8") as f:
        data = json.load(f)
    total_rev  = sum(i["revenue"] for i in data["sales"])
    total_unit = sum(i["units"]   for i in data["sales"])
    top        = max(data["sales"], key=lambda x: x["revenue"])
    t = data["support_tickets"]
    metrics = {
        "week_ending":     data.get("week_ending", str(date.today())),
        "total_revenue":   total_rev,
        "total_units":     total_unit,
        "top_product":     top["product"],
        "resolution_rate": round(t["resolved"] / t["opened"] * 100, 1),
        "sessions":        data["website_traffic"]["sessions"],
        "bounce_rate":     data["website_traffic"]["bounce_rate"],
    }
    return metrics, data["sales"]

def get_claude_summary(metrics: dict) -> str:
    client = anthropic.Anthropic(api_key=API_KEY)
    prompt = (
        f"Write a 3-sentence weekly business summary.\n"
        f"Revenue: ${metrics['total_revenue']:.2f} | "
        f"Top Product: {metrics['top_product']} | "
        f"Support Rate: {metrics['resolution_rate']}% | "
        f"Sessions: {metrics['sessions']:,}.\n"
        "Be concise, professional, and insightful."
    )
    r = client.messages.create(
        model="claude-sonnet-4-5", max_tokens=400,
        messages=[{"role": "user", "content": prompt}]
    )
    return r.content[0].text

def send_report(metrics: dict, sales: list, summary: str):
    rows = "".join(
        f"{i['product']}{i['units']}"
        f"${i['revenue']:.2f}" for i in sales
    )
    html = (
        f""
        f"
" f"

Weekly Report — {metrics['week_ending']}

" f"
" f"AI Summary

{summary}
" f"
" f"" f"" f"{rows}" f"" f"" f"
ProductUnitsRevenue
TOTAL{metrics['total_units']}${metrics['total_revenue']:.2f}
" f"" ) msg = MIMEMultipart("alternative") msg["Subject"] = f"[Weekly Report] {metrics['week_ending']}" msg["From"] = FROM_EMAIL msg["To"] = TO_EMAIL msg.attach(MIMEText(html, "html", "utf-8")) with smtplib.SMTP(SMTP_HOST, SMTP_PORT) as server: server.starttls() server.login(FROM_EMAIL, SMTP_PASS) server.sendmail(FROM_EMAIL, TO_EMAIL, msg.as_bytes()) print(f"[SENT] Report dispatched to {TO_EMAIL}") if __name__ == "__main__": metrics, sales = load_and_aggregate("weekly_data.json") summary = get_claude_summary(metrics) send_report(metrics, sales, summary)
[STEP 1] Loading weekly operational data... Products: 3 [STEP 2] Aggregating metrics... Revenue: $4,528.77 | Resolution: 78.6% [STEP 3] Claude summary generated. (327 chars) [STEP 4] HTML email assembled. (2,983 bytes) [SENT] Report dispatched to team@yourcompany.com
Return to Operational Guide Outline

08. Scheduling It to Run Automatically

A script you have to remember to run is not automation. A script that runs itself — that's the goal.

OPTION A
Python schedule library (Simplest)

Add 4 lines to your script. No system-level configuration needed. Requires the Python process to stay running.

# PYTHON — schedule library approach
import schedule, time

def weekly_report_job():
    metrics, sales = load_and_aggregate("weekly_data.json")
    summary = get_claude_summary(metrics)
    send_report(metrics, sales, summary)

# Run every Friday at 09:00
schedule.every().friday.at("09:00").do(weekly_report_job)

while True:
    schedule.run_pending()
    time.sleep(60)
OPTION B
Windows Task Scheduler (Most Reliable)

Right-click Task Scheduler > Create Basic Task > Weekly, Friday, 09:00 > Start a Program: python C:\path\to\weekly_report.py. Survives reboots. Runs even if you're not logged in.

OPTION C
Linux/Mac cron (If hosted on a server)

Add to crontab with crontab -e: 0 9 * * 5 /usr/bin/python3 /home/user/weekly_report.py — runs at 9 AM every Friday automatically.

Return to Operational Guide Outline

09. Real-World Variations

The pattern adapts to whatever data your team actually cares about.

The pipeline described in this guide uses sales revenue, support tickets, and website sessions as the example dataset. But the architecture is entirely data-agnostic. Your weekly data could come from anywhere and track anything — as long as you can load it into a Python dictionary, the aggregation and AI summary steps work identically.

E-commerce Stores
Shopify / WooCommerce

Load orders CSV, aggregate by product category and fulfillment status. Claude summarizes inventory risk and top-selling SKUs automatically.

SaaS Products
Stripe + Mixpanel

Pull MRR delta, churn count, and DAU from your analytics API. Claude generates a growth narrative with retention flags.

Marketing Teams
Google Ads + GA4

Load campaign spend and conversion data. Claude produces a budget efficiency summary with ROAS comparison across ad groups.

Operations Teams
Jira / Trello

Load sprint velocity, bugs opened, and deployment frequency. Claude writes a delivery cadence assessment with bottleneck identification.

Return to Operational Guide Outline

10. What's Next

Once the weekly report runs on autopilot, the next question is obvious: what else can be automated the same way?

The script you've built today follows a pattern that generalizes to almost any routine business communication: load data, compute metrics, generate natural-language commentary with Claude, format as a professional document, and dispatch automatically on a schedule. That same pattern handles daily standup summaries, monthly board packs, quarterly business reviews, customer-facing usage reports, and supplier performance scorecards.

The practical next upgrade for most teams is connecting the data loader directly to a live source rather than a manually exported JSON file. The gspread library lets you pull directly from Google Sheets. The requests library lets you query any REST API with a token. The psycopg2 library connects to PostgreSQL. Once you replace the static JSON load with a live data pull, the entire workflow becomes truly zero-touch — the script fetches, aggregates, summarizes, and sends without any human input at any stage.

If your team receives the reports but someone still needs to act on them — following up on pending support tickets, adjusting ad spend based on ROAS — the next evolution is adding a decision layer. After Claude writes the summary, you can pass the same metrics through a second Claude prompt that generates a prioritized action list based on your team's predefined business rules. The report stops being a passive information document and becomes an active directive that tells people exactly what to do next. That's the transition from automation to agentic operations.

Return to Operational Guide Outline
Ready to Build a Fully Autonomous Operations Stack?

Automated reports are a great start. The next level is software that manages its own data pipelines, payments, and infrastructure without human supervision. The Master Class series covers the full architectural blueprint for building truly autonomous systems.

Read: Decentralized AI Agent Marketplaces →

Popular posts from this blog

What to Automate First in a Small Business

[Master Class #01] The 2026 Agentic Economy: A Blueprint for Sovereign Wealth

[Master Class #18] The Algorithmic Sentinel: Deploying High-Performance Private Data Harvesters