# Statistics Statistics pages live under `/stats/*` (`src/frontend/src/views/StatisticsView.tsx`). Status: ✅ done, 🔲 TODO (needs backend changes, listed under each item). ## Architecture - Backend (`services/stats_service.py`) returns compact **fact rows**, not aggregates, for the per-user pages. Aggregation (months, weekdays, hours) happens in the frontend (`lib/stats.ts`) so buckets use the viewer's timezone. - The site overview is aggregated in the backend (`GET /api/admin/stats`) because it spans all users and needs files on disk. - Item names/prices come from the per-order config files (`data/configs/.json`), resolved in the backend. - Estimated totals only exist for orders whose menu items have prices. | Endpoint | Auth | Used by | |---|---|---| | `GET /api/stats/me` | any user | My Orders | | `GET /api/stats/managed` | any user (own orders only) | Order Management | | `GET /api/admin/stats` | site admin | Site Overview | ## Timeframe and insights - One page-wide timeframe filter (`range.tsx`: 1W, 1M, 3M, 1Y, YTD, All, custom) drives every number and chart, except the all-time blocks labelled as such (lunch profile, calendar, payments, funnel). - Stat tiles show the change vs. the previous period of equal length (`Delta`); "All" has no comparison. - The "Activity" panel (`TimeSeriesPanel.tsx`) draws line charts of per-period totals (days up to ~3 months, weeks up to ~13 months, months beyond; `periodBuckets` in `lib/stats.ts`) with a "Cumulative" switch (off by default) for running totals. Site-wide daily counts come from `daily` in `/api/admin/stats`. - Personal insights (lunch personality, records, adventurousness, calendar and weekday x hour heatmaps) are computed in `lib/stats.ts` from the fact rows. `community` in `/api/stats/me` (participant count, average, percentile) is the only extra aggregate, from `user_order_tokens`. - Site "active users" now means users who created an order or submission in the window (was: last profile update). The funnel and weekly retention use the same data. - **Privacy model:** My Orders only ever contains the viewer's own data plus anonymous aggregates (percentile, orders-per-participant histogram, organizer counts/shares). `/api/stats/me` returns no emails; organizers are numbered per response. Order Management is scoped to the viewer's own orders (their participants are visible, as in the order admin view). Only Site Overview (admin) sees site-wide data and names. - Not possible without new tracking: fill rate per order (no invitee list), display names (only emails exist; the local part is shown). ## My Orders (orderees) Layout: year calendar, lunch profile (all-time facts: orders, streak, average, busiest month, milestone, organizers, adventurousness, rank), activity, favourite items + payments, weekday x hour. - ✅ Participation over time (orders joined per month) and current streak (consecutive weeks) - ✅ Favourite items and categories, items repeated most (drill-down: all items, by category, or one category) - ✅ Total and average spend per order (priced menus only) - ✅ Usual order day / hour - ✅ Most frequent organizers - ✅ Payments: one table of open (accepted, unpaid) and paid orders, open first, then newest first; status filter - 🔲 How early you submit before the deadline — orders have no deadline. Backend: add `group_orders.deadline` (nullable ISO text) + create/update API + UI field; expose it in `/api/stats/me`. ## Order Management - ✅ Orders created over time; open vs. closed (closed = ordering done) - ✅ Participants per order (average, trend); repeat vs. new participants - ✅ Submission timeline: time from order creation to submission - ✅ Popular items and categories across own orders (same category drill-down) - ✅ Order volume: items and value per order - ✅ Payment status: paid / accepted-unpaid / pending - 🔲 Average time to payment — only the current `paid` flag is stored. Backend: add `submissions.paid_at` (and `accepted_at`), set on status updates, backfill NULL. - 🔲 Participants who are invited but rarely submit — there is no invite/participant list. Backend: `order_invites` table (order, email) or a per-owner contact list. - 🔲 Submission timeline relative to the deadline — needs the deadline (see above). ## Site Overview (admins) - ✅ Active contributors per week (users who created an order or submission), new registrations per week - ✅ Users with recent activity (7 / 30 days, from last known activity — approximation) - ✅ Unconfirmed accounts, pending email / user-id changes, open registration links - ✅ Orders per week and share of orders with ≥1 submission - ✅ Top order managers, most active participants - ✅ Most ordered items site-wide (same category drill-down; `items` in `/api/admin/stats` is capped at 300) - ✅ Storage usage per data folder and orphaned files - 🔲 Real daily/weekly/monthly active users — only creation timestamps are recorded. Backend: `user_profiles.last_seen_at` updated (throttled) in `get_existing_user_id`, or an `activity_log` table. - 🔲 Email volume and failures — `email_service` does not log sends. Backend: `email_log` table (kind, recipient hash, status, error, created_at) written by `email_service`.