Turn raw data into strategic foresight.
A multi-tenant B2B analytics platform that ingests messy POS, delivery, and spreadsheet exports,
then maps, cleans, and transforms them into clean, client-facing dashboards. Automatically.
πΏπ¦ Built for the South African market Β Β·Β π° ZAR Β Β·Β π§Ύ 15% VAT Β Β·Β π Africa/Johannesburg
- β¨ What is DataBrew?
- π¬ Feature Tour
- β‘ Tech Stack
- ποΈ Architecture
- π Quick Start (Local)
- π§ͺ Quality & Tests
- π REST API
- π Project Structure
- π Security
- π³ Deployment
Multi-branch businesses (restaurant groups, franchises, distributors) drown in data fragments. Every POS system, delivery app, and location exports its own messy CSV/Excel/PDF with its own product names and column layouts. DataBrew unifies all of it.
It ingests those raw exports, fuzzy-matches every product name to a canonical master catalog, runs the data through a three-stage ETL pipeline, and serves clean, branded, self-service dashboards to each client, with full multi-tenant isolation, role-based access, and a REST API.
π₯ Raw POS/delivery exports -> π§ ML product mapping -> π Clean client dashboards
| π’ Multi-tenant | Every row scoped to a tenant; middleware + ORM-level isolation prevents cross-tenant leaks |
| π§ ML auto-mapping | Seed-token + fuzzy matching maps raw product names to a master catalog with manual override |
| βοΈ 3-stage ETL | RawRecord -> StagingRecord -> AnalyticsRecord, materialized into a star schema |
| π Self-service BI | Branded client portal with KPIs, trends, maps, drill-downs, and exports |
| π₯ 6 roles | Superadmin, data engineer, mapper, delivery manager, client admin, client viewer |
| π REST API | Tenant-scoped, throttled DRF endpoints for programmatic access |
Each client logs into their own branded portal (logo, colours, tagline) and gets a self-service analytics workspace. No spreadsheets required.
Dashboard, Overview tab: headline KPIs (revenue, units, margin, avg price), a revenue trend chart, top products, and a revenue-by-category breakdown.
The engine room. Raw files become trustworthy analytics through a reviewable, reversible pipeline.
π₯ Imports. Upload CSV/Excel/PDF, auto-detect columns, map them to standard fields, review duplicates & quality, then approve. SHA-256 hashing catches duplicate files, and a quality score grades each import.
π§© Mapping. Every raw product name is matched to a canonical MasterProduct. The mapping
workspace shows coverage per source, pending suggestions, and unmapped items. Mapping is seed-token
based, so "Castle Lager 340ml" mapped once covers every branch and every future import.
β‘ Speed Mapper. A keyboard-first triage UI for blitzing through unmapped products: approve, reject, or skip without touching the mouse, with live progress and an undo trail.
The internal cockpit for the team running data delivery across all clients.
Delivery Dashboard. A 6-month grid of which client received data when, freshness indicators, recent activity, and an at-a-glance data-quality overview.
| π₯ Clients Every tenant, paginated, with status & source counts. ![]() |
ποΈ Delivery Overview Per-source mapping stats and refresh controls. ![]() |
β»οΈ Analytics refresh runs as a tracked async task with six phases (
starting -> deleting -> building -> curating -> snapshots -> finalizing) and live progress polling.
π§ ML Model Stats. A live view of the fuzzy-matching engine: the matching pipeline (barcode -> cross-tenant -> fuzzy name -> size/unit -> brand -> score), signal weights, engine configuration, and learning/acceptance metrics.
ποΈ Database Schema Explorer. Live introspection of every model across all apps, with row counts, relationships, and clickable foreign keys. (42 tables Β· 94 relationships in the demo dataset.)
π System Analytics. Platform-wide traffic, import volume, mapping coverage, and client breakdown.
π€ Users & Roles![]() |
βοΈ System Settings![]() |
π Audit Log![]() |
π Notifications![]() |
π Secure Login![]() |
βοΈ Legal / Docs![]() |
| Layer | Technology |
|---|---|
| π Backend | Django 5.1.4, Python 3.12, Django REST Framework |
| π Database | PostgreSQL 16 (star schema for analytics) |
| π΄ Cache / Queue | Redis 7 (with DB-cache fallback) |
| π₯οΈ Frontend | HTMX, Alpine.js, Chart.js, Leaflet (server-rendered, no SPA build step) |
| π¦ Data | pandas, numpy, openpyxl, pdfplumber, RapidFuzz (fuzzy matching) |
| π Security | Argon2, django-axes (brute-force lockout), PyJWT |
| π³ Deployment | Docker, Docker Compose, Nginx, Gunicorn, WhiteNoise, Let's Encrypt |
| π‘ Monitoring | Sentry, custom traffic analytics |
π‘ All front-end libraries are vendored locally under
static/, so there is no runtime CDN dependency: dashboards render reliably offline and aren't subject to CDN or SRI drift.
π₯ CSV / XLSX / PDF upload
|
π RawRecord ............ raw rows stored as-is, file SHA-256 hashed
|
π ColumnMap ............ maps raw columns to standard fields
|
β
StagingRecord ........ cleaned, validated, VAT-resolved
|
π§ MappingRule + ML ..... fuzzy / seed-token match to MasterProduct catalog
|
π AnalyticsRecord ...... fully mapped, analytics-ready
|
β FactSale + Dimensions star schema powering every dashboard
Every model carries a tenant ForeignKey. TenantMiddleware scopes each request and TenantManager
guarantees queries never leak across tenants, enforced at the middleware, view, and ORM layers.
| Table | Role |
|---|---|
FactSale |
π Central fact, one row per product Γ store Γ date Γ source |
DimDate |
π Date dimension: day, week, month, quarter, year |
DimProduct |
π·οΈ Product dimension, linked to the MasterProduct catalog |
DimStore |
π Store/location dimension, linked to MappedLocation |
Prerequisites: Python 3.12, PostgreSQL 16, and libmagic (file-type detection: brew install libmagic
on macOS, apt install libmagic1 on Debian/Ubuntu). Redis is optional; it falls back to the database cache.
# 1. Clone & create a virtualenv
git clone https://github.com/Nevvyboi/DataBrew.git
cd DataBrew
python3.12 -m venv venv && source venv/bin/activate # Windows: venv\Scripts\activate
pip install -r requirements.txt
# 2. Create the database + an owning user
createuser databrew_user --pwprompt # set a password when prompted
createdb -O databrew_user databrew # -O makes the new user the DB owner
# 3. Configure environment
cp .env.example .env
# - set DB_USER / DB_PASSWORD to match step 2
# - generate a SECRET_KEY: python -c "import secrets; print(secrets.token_urlsafe(50))"
# - leave REDIS_URL blank to use the database cache fallback (no Redis needed)
# 4. Migrate & seed a full demo tenant (~56k sales rows, 67 products, 8 locations)
python manage.py migrate
python manage.py createcachetable # backs the DB cache when REDIS_URL is blank
python manage.py generate_sample_data
# 5. Run
python manage.py runserverVisit http://127.0.0.1:8000 and sign in to the seeded demo tenant:
| Role | Password | |
|---|---|---|
| π€ Client Admin | [email protected] |
demo1234 |
Create an internal superadmin to explore the operations, mapping, and system areas:
python manage.py createsuperuserπ§° Set
DEBUG_TOOLBAR=0in your environment to hide the Django Debug Toolbar (e.g. for clean screenshots).
python manage.py test # tenant isolation, ML matching, VAT, ETL, API
ruff check . # lint (config in ruff.toml)The test suite focuses on the load-bearing logic: multi-tenant data isolation (the ORM
TenantManager plus the DRF API), the fuzzy product-matching engine, VAT breakdowns, seed-token
generation, the ETL transitions, and the role-permission matrix. Every push and PR runs lint, a
migrations check, and the full suite with coverage via GitHub Actions.
Tenant-scoped, authenticated, and rate-throttled (Django REST Framework). All endpoints live under /api/v1/.
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/v1/snapshots/ |
Pre-computed period snapshots (revenue/volume/cost) |
GET |
/api/v1/products/ |
Master product catalog (filter by brand) |
GET |
/api/v1/locations/ |
Mapped store locations |
GET |
/api/v1/sources/ |
Data sources |
GET |
/api/v1/kpis/?start=YYYY-MM-DD&end=YYYY-MM-DD |
Pre-computed KPIs for a date range |
Auth: IsAuthenticated + IsTenantMember (responses are automatically filtered to the caller's tenant).
DataBrew/
βββ apps/
β βββ accounts/ π€ Users, Tenants, Roles, audit logging, onboarding wizard
β βββ ingestion/ π₯ DataSource, ImportJob, RawRecord, ColumnMap (upload & parse)
β βββ mapping/ π§© MasterProduct catalog, MappingRule, ML matching, locations
β βββ pipeline/ βοΈ StagingRecord, AnalyticsRecord, ImportSnapshot (ETL)
β βββ dashboards/ π Star schema, client portal, KPIs, shared dashboards
β βββ delivery/ π Operations dashboard, async RefreshTask worker
β βββ notifications/ π AlertRule, Notification, dispatch engine
β βββ operations/ π οΈ System settings, schema viewer, ML stats, health
β βββ analytics/ π Traffic & performance analytics
β βββ api/ π REST API (DRF): serializers, permissions, throttling
β βββ sharing/ π€ Cross-tenant shared data-source access
β βββ core/ π§± Shared utilities: image serving, middleware, template tags
βββ config/ βοΈ Settings (base/development/production), URLs, WSGI
βββ templates/ π¨ Server-rendered HTML (HTMX partials, email templates)
βββ static/ πΌοΈ CSS, JS (vendored libs), brand assets, screenshots
βββ locale/ π Translations (French, Italian)
βββ nginx/ π Reverse proxy: SSL, rate limiting, security headers
βββ scripts/ π deploy.sh Β· update.sh Β· backup.sh
βββ sample_data/ π Demo CSVs for testing imports
βββ Dockerfile π³ Multi-stage production image
βββ docker-compose*.yml π³ Local & production stacks
| Feature | Detail |
|---|---|
| π Passwords | Argon2 hashing, 10+ char minimum, common-password rejection |
| π« Brute force | 5 attempts then a 1-hour lockout (django-axes), plus a login honeypot |
| π΅οΈ Enumeration | Constant-time password check prevents email enumeration |
| π‘οΈ CSRF | SameSite=Strict cookies, token on every form |
| π HTTPS | SSL-only with HSTS (1 year) in production |
| π File uploads | MIME type + extension + magic-byte validation |
| π’ Tenant isolation | Enforced at the middleware, view, and ORM layers |
| π Audit trail | Every login, logout, password change, and import is logged |
bash scripts/deploy.sh # first-time: prompts for domain/email, builds images,
# runs migrations, configures SSL (Let's Encrypt)
bash scripts/update.sh # pull, rebuild, migrate, zero-downtime restartAll containers run restart: always behind Nginx + Gunicorn, so a server reboot brings the whole
stack back online automatically.
Released under the MIT License, free to use, modify, and build on, with attribution.
Built with β and π₯ in South Africa




















