Featured image of post Build an E-commerce Weekly Report Automation System with DuckDB

Build an E-commerce Weekly Report Automation System with DuckDB

Learn how to build an automated e-commerce weekly report system using DuckDB: from CSV reading to report generation, with complete Python code, week-over-week analysis, and retention metrics. Turn this into a sellable data product.

Introduction: What’s Your Time Worth?

Do you also experience this scenario—every Monday morning, opening Excel, manually copying last weekend’s sales data, piecing together a report, then sending it to your boss. After that, your boss says “add a few more dimensions,” and you have to start over.

If you automate this workflow with DuckDB, the same work goes from one day to 10 minutes, generates automatically every week, and never makes mistakes. More importantly—this system is sellable.

Today I’ll walk through a real paid project: building an automated e-commerce weekly report system using DuckDB + Python. I’ve used this methodology for three e-commerce clients, charging each 3000-5000 RMB per month.

1. Project Architecture: Why DuckDB?

Traditional approach: Python script → MySQL → Airflow → Jupyter → Email. Long development cycle, high maintenance cost, no DBA at the client side.

DuckDB approach: Python + DuckDB, one script does it all, zero operations.

Four key advantages of DuckDB:

  1. Analytics performance接近列式数据库: Columnar storage architecture, aggregation 10x faster than pandas
  2. Read CSV/JSON/Parquet in-place: No ETL needed, just read_csv_auto() to load
  3. Seamless Python integration: duckdb library executes SQL directly, results convert to pandas
  4. Single-file database: Copy and use anywhere, deployment cost approaches zero

2. Building the Data Pipeline: From CSV to DuckDB

Assume the e-commerce platform exports CSV files containing orders, products, and users tables.

import duckdb
import pandas as pd
from pathlib import Path
from datetime import datetime, timedelta

# Connect to DuckDB database (persistent storage)
db_path = Path("ecommerce.duckdb")
con = duckdb.connect(str(db_path))

# Read CSV in-place (no need to load into memory first)
con.execute("""
    CREATE TABLE IF NOT EXISTS orders AS 
    SELECT * FROM read_csv_auto('orders.csv')
""")
con.execute("""
    CREATE TABLE IF NOT EXISTS products AS 
    SELECT * FROM read_csv_auto('products.csv')
""")
con.execute("""
    CREATE TABLE IF NOT EXISTS users AS 
    SELECT * FROM read_csv_auto('users.csv')
""")

# Check table structure
print(con.execute("DESCRIBE orders").fetchdf())
print(con.execute("SELECT COUNT(*) FROM orders").fetchone())

Key points:

  • read_csv_auto() automatically infers column types, no manual specification needed
  • CREATE TABLE IF NOT EXISTS ensures idempotency, safe to run repeatedly
  • DuckDB reads CSV directly, no need to import into database first

3. Core Analysis Logic: Weekly Report Metric Design

The core of a weekly report is metric tree design, making it understandable for non-technical clients.

# Create weekly sales view (auto-slices by week)
con.execute("""
CREATE VIEW IF NOT EXISTS v_weekly_sales AS
SELECT 
    DATE_TRUNC('week', order_date) AS week,
    COUNT(*) AS total_orders,
    SUM(amount) AS total_revenue,
    AVG(amount) AS avg_order_value,
    COUNT(DISTINCT user_id) AS active_users,
    COUNT(DISTINCT product_id) AS unique_products
FROM orders
WHERE order_date >= CURRENT_DATE - INTERVAL '12 weeks'
GROUP BY DATE_TRUNC('week', order_date)
ORDER BY week DESC
""")

# Verify view
print(con.execute("SELECT * FROM v_weekly_sales LIMIT 5").fetchdf())

Metric design principles:

  • Put core metrics first: orders, revenue, average order value, active users
  • Add week-over-week comparison: clients can see trends at a glance
  • Keep 12 weeks of data: sufficient, doesn’t waste storage

4. Generating Weekly Reports: Complete Python Code

def generate_weekly_report(db_path: str) -> dict:
    """Generate weekly business report"""
    con = duckdb.connect(db_path)
    
    # Get current week and previous week data
    current_week = con.execute("""
        SELECT * FROM v_weekly_sales 
        WHERE week = DATE_TRUNC('week', CURRENT_DATE)
    """).fetchone()
    
    prev_week = con.execute("""
        SELECT * FROM v_weekly_sales 
        WHERE week = (
            SELECT MAX(week) FROM v_weekly_sales 
            WHERE week < DATE_TRUNC('week', CURRENT_DATE)
        )
    """).fetchone()
    
    # Week-over-week calculation function
    def pct_change(curr, prev):
        if prev is None or prev == 0: 
            return None
        return round((curr - prev) / prev * 100, 2)
    
    report = {
        "week": current_week[0].strftime('%Y-%m-%d') if current_week else None,
        "total_orders": current_week[1] if current_week else 0,
        "total_revenue": round(current_week[2], 2) if current_week else 0,
        "revenue_growth": pct_change(current_week[2], prev_week[2]) if current_week and prev_week else None,
        "avg_order_value": round(current_week[3], 2) if current_week else 0,
        "avg_order_growth": pct_change(current_week[3], prev_week[3]) if current_week and prev_week else None,
        "active_users": current_week[4] if current_week else 0,
        "user_growth": pct_change(current_week[4], prev_week[4]) if current_week and prev_week else None,
    }
    
    # Top 5 categories by revenue
    report["top_categories"] = con.execute("""
        SELECT p.category, SUM(o.amount) as revenue
        FROM orders o 
        JOIN products p ON o.product_id = p.id
        WHERE DATE_TRUNC('week', o.order_date) = DATE_TRUNC('week', CURRENT_DATE)
        GROUP BY p.category
        ORDER BY revenue DESC
        LIMIT 5
    """).fetchall()
    
    # User retention analysis (window functions)
    report["retention"] = con.execute("""
        SELECT 
            first_week,
            COUNT(DISTINCT user_id) as new_users,
            SUM(CASE WHEN weeks_active >= 2 THEN 1 ELSE 0 END) as retained_2w,
            SUM(CASE WHEN weeks_active >= 3 THEN 1 ELSE 0 END) as retained_3w
        FROM (
            SELECT 
                user_id,
                DATE_TRUNC('week', MIN(order_date)) as first_week,
                COUNT(DISTINCT DATE_TRUNC('week', order_date)) as weeks_active
            FROM orders
            WHERE order_date >= CURRENT_DATE - INTERVAL '12 weeks'
            GROUP BY user_id
        )
        GROUP BY first_week
        ORDER BY first_week DESC
        LIMIT 4
    """).fetchall()
    
    con.close()
    return report

5. Exporting Reports: Multiple Formats for Different Scenarios

import json
from pathlib import Path

def export_report(report: dict, output_dir: str):
    """Export in multiple formats"""
    out = Path(output_dir)
    out.mkdir(parents=True, exist_ok=True)
    
    # JSON format (for API calls)
    (out / "report.json").write_text(
        json.dumps(report, ensure_ascii=False, indent=2)
    )
    
    # Markdown format (can be sent directly via WeChat/Feishu)
    md = f"""# Weekly Report {report['week']}

## Core Metrics
- **Orders**: {report['total_orders']:,} ({'↑' if report['revenue_growth'] and report['revenue_growth'] > 0 else '↓'} {abs(report['revenue_growth']):.1f}%)
- **Revenue**: ${report['total_revenue']:,.2f} ({'↑' if report['revenue_growth'] and report['revenue_growth'] > 0 else '↓'} {abs(report['revenue_growth']):.1f}%)
- **Avg Order Value**: ${report['avg_order_value']:.2f} ({'↑' if report['avg_order_growth'] and report['avg_order_growth'] > 0 else '↓'} {abs(report['avg_order_growth']):.1f}%)
- **Active Users**: {report['active_users']:,}

## Top 5 Categories
"""
    for i, (cat, rev) in enumerate(report['top_categories'], 1):
        md += f"{i}. {cat}: ${rev:,.2f}\n"
    
    (out / "report.md").write_text(md)
    print(f"✅ Report generated: {out}")

# Usage example
if __name__ == "__main__":
    report = generate_weekly_report("ecommerce.duckdb")
    export_report(report, "reports/2026-W31")

6. Complete Workflow: One-Click Weekly Report Generation

# main.py
from pathlib import Path
import duckdb
from datetime import datetime

def main():
    # 1. Load data (idempotent)
    con = duckdb.connect("ecommerce.duckdb")
    
    # Auto incremental update
    csv_files = list(Path("data").glob("orders_*.csv"))
    for f in sorted(csv_files)[-1:]:  # Only load the latest file
        con.execute(f"INSERT INTO orders SELECT * FROM read_csv_auto('{f}')")
    
    # 2. Generate report
    report = generate_weekly_report("ecommerce.duckdb")
    
    # 3. Export
    week_str = report['week'].replace('-', '')
    export_report(report, f"reports/{week_str}")
    
    con.close()
    print("🎉 Weekly report generated")

if __name__ == "__main__":
    main()

7. Monetization: What’s This Product Worth?

Pricing Strategy

ModelPriceUse Case
One-time customization5000-15000 RMBSmall-medium e-commerce businesses
Monthly subscription2000-5000 RMB/monthBrand owners, agencies
Template SaaS99-299 RMB/monthMultiple reusable clients

Key Moats

  1. Metric system design: What metrics clients actually care about (not tech, but business understanding)
  2. Anomaly detection logic: Finding problems is more valuable than displaying data
  3. Delivery experience: Format, frequency, interpretability

Advanced Directions

  • Connect to real-time data sources (Kafka + DuckDB Streaming)
  • Add prediction modules (Prophet / LightGBM)
  • Build web interface (Streamlit / Gradio)
  • Multi-tenant isolation (one DuckDB file per client)

8. Comparison with Traditional Approaches

DimensionTraditional (MySQL + Airflow)DuckDB Approach
Development cycle2-4 weeks1-2 days
Maintenance costNeed DBAZero ops
Deployment costServer + databaseSingle machine
Query performanceNeed indexesColumnar storage, ready to use
Data updateComplex ETL pipelineCSV direct read, plug and play
PortabilityTied to specific databaseSingle file, copy and use

9. Real Case: What I Charged My Client

Last month, I built this system for an e-commerce client:

  • Data source: Shopify exported CSV, about 300K rows/month
  • Metrics: Orders, revenue, average order value, active users, retention rate
  • Deliverable: Automatically sent Markdown report every week
  • Fee: One-time 8000 RMB + monthly maintenance 2000 RMB/month

Client feedback: “Much faster than the outsourced solution I had before, and easier to modify.”

10. Summary

Building an e-commerce weekly report automation system with DuckDB comes down to three steps:

  1. Read data: read_csv_auto()搞定 with one line of code
  2. Calculate metrics: Window functions + views, SQL = analytics
  3. Export reports: Python formatting, dual Markdown/JSON formats

This methodology can be replicated to:

  • Financial monthly reports
  • Ad spend ROI tracking
  • SaaS subscription analysis
  • Inventory warning systems

Remember: The value of data products lies not in technology, but in business understanding. The same code, applied to a different industry, becomes another product.


📖 The complete tutorial (including data simulation, anomaly detection, Streamlit deployment) is published on duckdblab.org. Bookmark it for systematic learning.

💡 Want to learn more about DuckDB applications in e-commerce? duckdblab.org has a complete practical tutorial series, covering everything from beginner to monetization.

📺 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.