A full-stack promo analytics app: Upload a CSV → validate schema → compute category/SKU promotion uplift → save runs → export results.
Repo goal: clean, reproducible analytics workflow with a production-style API and test coverage.
- What it does
- Tech Stack
- Current Features
- Repository Structure
- CSV Data Contract
- Quickstart (Backend)
- Run Tests
- API Reference
- Metrics
- Roadmap
- Notes on Persistence
- License
- Author
- Creates analysis runs (tracked in SQLite)
- Accepts CSV uploads and validates required fields
- Computes promotion uplift metrics (baseline vs promo rows)
- Persists analysis results and exposes them via API endpoints
- Includes automated tests (pytest) for the full run → upload → analyze workflow
Backend
- FastAPI (REST API)
- Pandas (data parsing + metrics)
- SQLAlchemy + SQLite (run tracking + results)
- Pytest (tests)
- Ruff (linting)
Frontend
- Planned: React + TypeScript + Vite (GitHub Pages)
- Status: Coming next
- Create a run
- List recent runs
- Fetch a run by ID
- Upload CSV for a run (
multipart/form-data) - Validates schema and rejects invalid files
- Analyze uploaded data and persist results
- Fetch stored results later
- Request logging middleware
- Environment-configurable CORS
- Test suite validates routes and core workflows
Category-Promotion-Analysis/
backend/
app/
api/ # API routes
core/ # config + logging
db/ # SQLAlchemy session/models
schemas/ # Pydantic response models
services/ # ingest + analyze logic
tests/ # pytest tests
pyproject.toml
sample_data/
sample_promos.csv
README.md
LICENSE
.gitignore
date(date/datetime)category(string)units(number)revenue(number)
promo_idORis_promo(0/1)
skudiscount_pct
Sample file:
sample_data/sample_promos.csv
conda create -n promo-analytics python=3.11 -y
conda activate promo-analyticsFrom the repo root:
cd backend
pip install -e ".[dev]"If you prefer not to conda activate, you can run:
cd backend
conda run -n promo-analytics python -m pip install -e ".[dev]"cd backend
uvicorn app.main:app --reload --port 8000Open:
- Health: http://127.0.0.1:8000/health
- API docs (Swagger): http://127.0.0.1:8000/docs
From the repo root:
cd backend
pytestOr with conda run:
cd backend
conda run -n promo-analytics python -m pytestBase URL (local): http://127.0.0.1:8000
GET /health→{ "status": "ok" }
POST /runs→ create a runGET /runs→ list recent runsGET /runs/{run_id}→ get run metadata
POST /runs/{run_id}/upload(multipart/form-data)
Create a run:
curl -X POST "http://127.0.0.1:8000/runs"Upload CSV:
curl -X POST "http://127.0.0.1:8000/runs/<RUN_ID>/upload" \
-F "file=@sample_data/sample_promos.csv"POST /runs/{run_id}/analyze→ compute and persist resultsGET /runs/{run_id}/results→ fetch stored results
curl -X POST "http://127.0.0.1:8000/runs/<RUN_ID>/analyze"
curl -X GET "http://127.0.0.1:8000/runs/<RUN_ID>/results"Current analysis computes uplift by category based on promo vs baseline rows:
units_base,units_promo,units_uplift,units_uplift_pctrevenue_base,revenue_promo,revenue_uplift,revenue_uplift_pct
Definitions:
- Baseline rows: non-promo rows
- Promo rows:
is_promo == 1ORpromo_idpresent
- React + TypeScript + Vite UI
- File upload page → run detail view
- KPI cards + category table + filters
- Export results (CSV/JSON)
- Promo comparison view
- Anomaly flags / outlier detection
- Postgres for persistent runs in production
- GitHub Actions CI (lint + tests) + deploy frontend to GitHub Pages
- Local dev uses SQLite (
cpa.dbunderbackend/by default). - In tests, we use an isolated temporary SQLite database created/reset for deterministic runs.
MIT License. See LICENSE.
Venkata Naga
GitHub: https://github.com/venkatasivanaga