Featured image of post Build an Automated Weekly Report System with DuckDB: From Script to SaaS

Build an Automated Weekly Report System with DuckDB: From Script to SaaS

Step-by-step guide to building an automated weekly report system with DuckDB + Python: from data ingestion and SQL analysis to Telegram delivery. Includes full code, monetization paths, and SaaS architecture.

Background: Weekly reports waste data professionals’ time every week

Every data team writes weekly reports. You manually export data, create charts, and compile insights — spending 2-3 hours per week. If you do this for multiple business lines, the time doubles.

We’ll build an fully automated weekly report system using DuckDB + Python: runs automatically every morning, produces reports, and sends them to Telegram. You save time AND can sell this capability to other teams.

Automated Weekly Report Architecture


Step 1: Prepare Test Data

Assume you have sales data in CSV format:

date,product,region,revenue
2024-01-01,iPhone,East,120000
2024-01-01,iPad,North,85000

Create a virtual CSV for testing:

import os
os.makedirs('/tmp/duckdb_weekly', exist_ok=True)

with open('/tmp/duckdb_weekly/sales.csv', 'w') as f:
    import random
    products = ['iPhone', 'iPad', 'MacBook', 'AirPods']
    regions = ['East', 'North', 'South', 'West']
    f.write('date,product,region,revenue\n')
    for day in range(1, 32):
        for _ in range(50):
            f.write(f'2024-01-{day:02d},{random.choice(products)},{random.choice(regions)},{random.randint(5000, 50000)}\n')
print("Data generated")

Step 2: Core Query — Generate the Weekly Report

DuckDB’s SQL syntax solves 80% of the problem directly:

import duckdb
from datetime import datetime, timedelta

conn = duckdb.connect()
conn.execute("CREATE TABLE sales AS SELECT * FROM read_csv('/tmp/duckdb_weekly/sales.csv')")

end_date = datetime(2024, 1, 31)
start_date = end_date - timedelta(days=6)

weekly_report = conn.execute("""
    SELECT 
        DATE(date) AS report_date,
        product,
        region,
        SUM(revenue) AS total_revenue,
        COUNT(*) AS order_count,
        AVG(revenue) AS avg_order_value
    FROM sales
    WHERE date BETWEEN ? AND ?
    GROUP BY 1, 2, 3
    ORDER BY total_revenue DESC
""", str(start_date.date()), str(end_date.date())).fetchdf()

print(weekly_report.head(10))

The output of this query is the core data for your weekly report: revenue, order count, and average order value grouped by product and region.


Step 3: Generate Visualizations

import pandas as pd
import matplotlib.pyplot as plt

product_summary = weekly_report.groupby('product')['total_revenue'].sum().sort_values(ascending=False)

plt.figure(figsize=(10, 6))
product_summary.plot(kind='bar', color='steelblue')
plt.title('Weekly Revenue by Product')
plt.ylabel('Revenue (CNY)')
plt.xlabel('Product')
plt.tight_layout()
plt.savefig('/tmp/duckdb_weekly/chart.png', dpi=150)
print("Chart generated")

Step 4: Package as a Telegram Message

import telebot

BOT_TOKEN='***'
CHAT_ID = 'YOUR_CHAT_ID'

bot = telebot.TeleBot(BOT_TOKEN)

message = f"""
📊 Weekly Report Generated (2024-01-25 to 2024-01-31)

🏆 Top Products:
{weekly_report.head(3).to_string(index=False)}

📈 Total Revenue: {weekly_report['total_revenue'].sum():,.0f} CNY
📦 Total Orders: {weekly_report['order_count'].sum():,}
💰 Avg Order Value: {weekly_report['avg_order_value'].mean():,.0f} CNY

[View Full Report Attachment]
"""

bot.send_photo(CHAT_ID, open('/tmp/duckdb_weekly/chart.png', 'rb'), caption=message)
print("Report sent to Telegram")

Step 5: Automated Scheduling

Use cron to run automatically every Monday at 8 AM:

crontab -e

# Add this line (runs daily at 8:00 AM)
0 8 * * * cd /root && python3 weekly_report.py >> /tmp/duckdb_weekly/cron.log 2>&1

Or use Python’s APScheduler library for more flexibility:

from apscheduler.schedulers.background import BackgroundScheduler
import time

def run_weekly():
    print(f"[{datetime.now()}] Starting weekly report...")
    # Call the generation logic above
    print("Weekly report generated")

scheduler = BackgroundScheduler()
scheduler.add_job(run_weekly, 'cron', day_of_week='mon-fri', hour=8, minute=0)
scheduler.start()

while True:
    time.sleep(60)

Advanced: Multi-Source Data + Parquet Caching

In production, your data isn’t just one CSV. DuckDB supports reading Parquet, SQLite, and even remote HTTP endpoints directly:

# Read Parquet (pre-processed data, queries respond in milliseconds)
conn.execute("CREATE TABLE sales_pq AS SELECT * FROM read_parquet('/data/sales/*.parquet')")

# Read remote CSV (via httpfs extension)
conn.execute("INSTALL httpfs; LOAD httpfs;")
conn.execute("CREATE TABLE remote_sales AS SELECT * FROM read_csv_auto('https://api.example.com/sales.csv')")

# Cross-table JOIN
conn.execute("""
    SELECT s.product, s.region, SUM(s.revenue) as revenue
    FROM sales_pq s
    JOIN remote_sales r ON s.order_id = r.id
    GROUP BY 1, 2
""")

Advanced: Web Dashboard with Streamlit

import streamlit as st
import duckdb

st.title('Real-time Weekly Report Dashboard')
df = duckdb.query("SELECT * FROM read_csv('sales.csv')").df()
st.dataframe(df)
st.line_chart(df.set_index('date')['revenue'])

Monetization Paths: From Tool to Product

  1. Internal Tool: Use it yourself → saves time → 2 CNY/hour × 52 weeks = 100K+ CNY/year saved
  2. SaaS Product: Multi-tenant service, 99 CNY/month per company → 100 companies = 120K CNY/year
  3. Outsourcing Service: Build for SMEs, charge 2,000-5,000 CNY per project
  4. Open Source引流: Open-source the code on GitHub, monetize via consulting and training

Traditional Approach vs DuckDB Approach

Traditional approach requires MySQL + Airflow + Jupyter + email services — long development cycle, high maintenance cost. DuckDB approach: one script, zero operations. Columnar storage makes aggregation 10x faster than pandas, and no database service installation needed.


The complete runnable code and project template are published at duckdblab.org, with more detailed steps and additional cases. Learn more DuckDB practical experience → duckdblab.org

📺 Watch video tutorials → Olap Studio YouTube

Subscribe for more DuckDB & AI automation tutorials

Built with Hugo
Theme Stack designed by Jimmy

⚠️ This site is an independent community project, not affiliated with, endorsed by, or sponsored by the DuckDB Foundation or official DuckDB project.

"DuckDB" is a registered trademark of the DuckDB Foundation. This site uses the name solely for factual description purposes.

All content is for educational and community promotion purposes only and does not constitute any commercial service.