Case Study — Business Intelligence Platform

From Stripe Webhook
to Executive Dashboard.
Automated.

How Evan's List built a real-time Python BI platform for a D2C kitchenware brand — live Stripe order sync, multi-dashboard analytics, inventory management, and printing ops visibility. Zero manual entry.

4
Dashboards
0
Manual Entry
Live
Stripe Sync
0
Duplicate Orders

Before & After

Running a D2C product business without real-time data means flying blind on inventory, production, and revenue.

Before

  • Orders tracked manually in spreadsheets — error-prone and stale
  • No visibility into printing queue or material consumption
  • Inventory counts updated by hand after each production run
  • Revenue analysis required exporting CSVs from Stripe dashboard
  • Duplicate order risk when re-checking Stripe manually
  • No centralized view of sales, inventory, and production together
  • No historical trend data for demand forecasting

After LiddyDash

  • Stripe orders sync automatically via webhook — zero manual entry
  • Printing dashboard shows queue, filament usage, and job status
  • Inventory updates in real time as orders are fulfilled
  • Revenue and sales analytics in interactive Plotly charts
  • Idempotent Stripe sync — duplicate orders are impossible
  • Single app, 4 dashboards, one source of truth
  • Date-range filtering and trend analysis built in

Data Pipeline

Stripe webhooks trigger order ingestion. A single SQLite source of truth feeds all four dashboards.

💳
Stripe Orders / Payments
🔔
Webhook Event Trigger
🐍
Python Ingest + Dedup
🗄️
SQLite Single Source
📊
Plotly Dash 4 Dashboards
☁️
PythonAnywhere Always On
# Idempotent Stripe sync — safe to run multiple times
def sync_stripe_order(stripe_order):
  # Check if already imported by source + order ID
  if database.check_order_exists("Stripe", stripe_order.id):
    return False  # Skip — already recorded
  # New order — record with source_channel + source_order_id
  return database.record_sale(
    source_channel="Stripe",
    source_order_id=stripe_order.id, # Stripe's unique ID
    ...
  )

4 Views, One App

Each dashboard is a separate Plotly Dash module registered to the main app router.

💰

Sales Dashboard

Revenue trends, order volume, customer breakdown, and product performance — all in interactive Plotly charts. Date-range filter lets you zoom from daily to all-time. Highlights top SKUs and revenue by channel (Stripe, manual, etc.).

LiveData
Multi-ChannelRevenue
Date FilterBuilt In
📦

Inventory Dashboard

Current stock levels, reorder alerts, and consumption rate per SKU. Tracks both raw materials (filament spools) and finished goods. Calculates units-remaining based on production yield data, not guess work.

Real-timeStock Levels
ReorderAlerts
YieldCalculations
🖨️

Printing Operations Dashboard

Production queue management — jobs in progress, completed, and queued. Filament usage per job calculated from known yield data (83.97g/unit for PC). Throughput trends help forecast production capacity and material procurement.

QueueManagement
83.97g/unitYield Tracked
CapacityForecasting
🗃️

Archive & Data Management

Historical order archive with full-text search and export. Data quality tools — deduplication checks, source attribution reports, and reconciliation between Stripe records and internal database. Supports CSV export for external reporting.

Full HistorySearchable
CSV ExportAvailable
DedupChecks

Idempotent Stripe Sync

Running the sync script daily — or multiple times per day — never produces duplicate orders.

🔑

source_channel + source_order_id

Every order record stores the source channel ("Stripe", "Manual", "Etsy") and the external order ID. Before inserting, the database checks this composite key — if it exists, the import is skipped silently.

🔄

Designed for Daily Runs

The sync script is designed to be run on a schedule (cron or manual). It looks back N days, pulls all Stripe orders in the window, and inserts only net-new records. Safe to run hourly if needed.

🧹

Multi-Channel Ready

The same duplicate-prevention schema works for any future channel — Etsy, WooCommerce, manual entry. Each channel gets its own source_channel value and native order ID. No data model changes needed to add a channel.


Platform Stats

📊
4
Interactive Dashboards
Sales · Inventory · Printing · Archive
0
Duplicate Orders
Idempotent sync design
✍️
0
Manual Order Entry
Fully webhook-driven
📦
3+
Source Channels
Stripe, manual, expandable
🐍
Dash 3
Framework
Plotly Dash + Bootstrap
☁️
Always
Uptime
PythonAnywhere + gunicorn

Build Timeline

Click any item for details.

Phase 1 — Data Model & Database

🗄️ SQLite Schema + database.py

Designed the database schema with multi-channel sales tracking, inventory tables, and production records. Built the database.py module with all CRUD operations and the duplicate-prevention logic.

Python SQLite Schema Design
Click for details
Schema decisions:
  • sales table: source_channel + source_order_id composite key for deduplication
  • products table: SKU, material type, yield per unit, current stock
  • printing_jobs table: job status, filament used, start/end time, linked product
  • SQLite chosen for zero server overhead — PythonAnywhere free tier compatible
  • database.py exposes clean Python functions — no raw SQL in dashboard code
  • initialize_database() creates all tables if they don't exist — safe to run on deploy
Phase 2 — Stripe Integration

💳 Stripe API + Webhook Sync

Built the Stripe sync layer — polling Stripe's API for recent orders, extracting product details, and inserting with duplicate prevention. Designed to run safely on a daily cron schedule.

Stripe API Python Webhook
Click for details
Integration highlights:
  • sync_all_stripe_orders(days_back=7) — configurable lookback window
  • Extracts charge ID, payment intent ID, customer name/email, product, and amount
  • check_order_exists("Stripe", order_id) called before every insert — O(1) index lookup
  • Stripe API key stored in environment variable — never hardcoded
  • Full sync summary printed: N new, N skipped, N errors
  • STRIPE_API_INTEGRATION.md documents the full integration for future maintainers
Phase 3 — Dashboard Modules

📊 4 Plotly Dash Dashboard Modules

Built each dashboard as a self-contained module with its own layout function and callback registration. The main app.py registers all modules and handles URL routing between views.

Plotly Dash 3 Dash Bootstrap Callbacks
Click for details
Architecture pattern:
  • Each dashboard: get_[name]_layout() + register_[name]_callbacks(app)
  • app.py calls all register functions at startup — no per-request overhead
  • Dark mode toggle stored in dcc.Store — persists client-side across tab navigation
  • dcc.Store + dcc.Location handle app-level state without page reloads
  • suppress_callback_exceptions=True allows modular callback registration
  • Pandas DataFrames used for all data transformation before passing to Plotly
Phase 4 — Deployment

☁️ PythonAnywhere + gunicorn

Deployed to PythonAnywhere with gunicorn WSGI, synced via a deploy script. The sync-to-pythonanywhere.sh script handles file transfer and app reload in one command.

PythonAnywhere gunicorn Shell Scripts
Click for details
Deployment details:
  • gunicorn serves the Flask/Dash app — production-grade, handles concurrent requests
  • sync-to-pythonanywhere.sh — one-command deploy from local dev to live
  • run-dev.sh — spins up local development server for testing
  • requirements.txt pinned to specific versions — reproducible deployments
  • SQLite database file persists on PythonAnywhere — no migration needed on redeploy

Tech Stack

🐍 Python 3 📊 Plotly Dash 3 🎨 Dash Bootstrap Components 📈 Plotly (charts) 🐼 Pandas 🗄️ SQLite 💳 Stripe API 🔔 Stripe Webhooks 🌶️ Flask (WSGI) ⚙️ gunicorn ☁️ PythonAnywhere 🔒 Environment Variables (secrets) 🧠 GitHub Copilot (Claude Sonnet 4.6)

Need real-time business intelligence?

Evan's List builds Python data platforms that connect your payment processor, inventory, and operations into one live view.