Add GET /stats endpoint + redeemed_item column for gamification #16

Closed
opened 2026-03-16 22:21:25 +00:00 by forgejo_admin · 0 comments
Contributor

Lineage

plan-mcd-tracker → Phase 7 (SvelteKit + Capacitor) — backend prereqs

Repo

forgejo_admin/mcd-tracker-api

User Story

As a user viewing my home dashboard or history page
I want to see my lifetime stats (codes redeemed, level, XP) and record what menu item I got when redeeming
So that the app celebrates my usage and tracks my preferences over time

Context

The frontend playground (Phase 6) added gamification UX: an XP banner on the home page ("Level 3 — BOGO Hunter · 12 codes redeemed"), a lifetime stats bar on the history page (scanned/earned/redeemed/expired counts), and a "What did you get?" chip picker on the redemption success screen (Big Mac, McChicken, etc.).

The frontend needs two backend changes before it can wire these up:

  1. A GET /stats endpoint returning lifetime counts and level info
  2. The existing PATCH /codes/{id}/redeem endpoint needs to accept an optional redeemed_item field

The 5-day expiry (expires_at on CouponUsage) already exists in the schema — no changes needed there.

File Targets

Files to modify:

  • src/mcd_tracker_api/models.py — add redeemed_item column to CouponUsage
  • src/mcd_tracker_api/routes/locations.py — update redeem endpoint to accept redeemed_item, add GET /stats endpoint
  • src/mcd_tracker_api/schemas.py (or wherever Pydantic models live) — add redeemed_item to redeem request/response schemas, add StatsResponse schema
  • alembic/versions/ — new migration adding redeemed_item column

Files NOT to touch:

  • src/mcd_tracker_api/auth.py — no auth changes
  • src/mcd_tracker_api/config.py — no config changes

Acceptance Criteria

  • GET /stats returns JSON: { "total_scanned": int, "total_earned": int, "total_redeemed": int, "total_expired": int, "current_level": int, "level_name": str, "xp_progress": float }
  • Level calculation: Level 1 (0-5 redeemed), Level 2 (6-15), Level 3 (16-30), Level 4 (31-50), Level 5 (51+)
  • Level names: "BOGO Beginner", "Free Food Fan", "BOGO Hunter", "Coupon Legend", "BOGO Master"
  • xp_progress is a float 0.0-1.0 representing progress within current level
  • PATCH /codes/{id}/redeem accepts optional body { "redeemed_item": "Big Mac" } — string, nullable
  • Redeeming without redeemed_item still works (backward compatible)
  • redeemed_item appears in code response objects
  • GET /stats requires auth (returns stats for the authenticated user only)
  • Alembic migration runs cleanly on existing data (column is nullable, no data loss)

Test Expectations

  • Test GET /stats with 0 codes → Level 1, all zeros
  • Test GET /stats with 6 redeemed → Level 2, correct xp_progress
  • Test PATCH /codes/{id}/redeem with redeemed_item → item saved
  • Test PATCH /codes/{id}/redeem without body → still works, redeemed_item is null
  • Test GET /stats counts expired codes correctly (codes past expires_at and not redeemed)
  • Run: pytest tests/ -v

Constraints

  • Follow existing route patterns in routes/locations.py
  • Use existing get_current_user dependency for auth
  • Level thresholds and names should be constants, not hardcoded in the query
  • redeemed_item is a simple nullable String column, not a foreign key (structured item tracking is Phase 14)

Checklist

  • PR opened
  • Tests pass
  • Alembic migration included
  • No unrelated changes
  • project-mcd-tracker — parent project
  • phase-mcd-tracker-13-gamification — the phase this feeds into (XP system)
### Lineage `plan-mcd-tracker` → Phase 7 (SvelteKit + Capacitor) — backend prereqs ### Repo `forgejo_admin/mcd-tracker-api` ### User Story As a user viewing my home dashboard or history page I want to see my lifetime stats (codes redeemed, level, XP) and record what menu item I got when redeeming So that the app celebrates my usage and tracks my preferences over time ### Context The frontend playground (Phase 6) added gamification UX: an XP banner on the home page ("Level 3 — BOGO Hunter · 12 codes redeemed"), a lifetime stats bar on the history page (scanned/earned/redeemed/expired counts), and a "What did you get?" chip picker on the redemption success screen (Big Mac, McChicken, etc.). The frontend needs two backend changes before it can wire these up: 1. A `GET /stats` endpoint returning lifetime counts and level info 2. The existing `PATCH /codes/{id}/redeem` endpoint needs to accept an optional `redeemed_item` field The 5-day expiry (`expires_at` on CouponUsage) already exists in the schema — no changes needed there. ### File Targets Files to modify: - `src/mcd_tracker_api/models.py` — add `redeemed_item` column to CouponUsage - `src/mcd_tracker_api/routes/locations.py` — update redeem endpoint to accept `redeemed_item`, add `GET /stats` endpoint - `src/mcd_tracker_api/schemas.py` (or wherever Pydantic models live) — add `redeemed_item` to redeem request/response schemas, add StatsResponse schema - `alembic/versions/` — new migration adding `redeemed_item` column Files NOT to touch: - `src/mcd_tracker_api/auth.py` — no auth changes - `src/mcd_tracker_api/config.py` — no config changes ### Acceptance Criteria - [ ] `GET /stats` returns JSON: `{ "total_scanned": int, "total_earned": int, "total_redeemed": int, "total_expired": int, "current_level": int, "level_name": str, "xp_progress": float }` - [ ] Level calculation: Level 1 (0-5 redeemed), Level 2 (6-15), Level 3 (16-30), Level 4 (31-50), Level 5 (51+) - [ ] Level names: "BOGO Beginner", "Free Food Fan", "BOGO Hunter", "Coupon Legend", "BOGO Master" - [ ] `xp_progress` is a float 0.0-1.0 representing progress within current level - [ ] `PATCH /codes/{id}/redeem` accepts optional body `{ "redeemed_item": "Big Mac" }` — string, nullable - [ ] Redeeming without `redeemed_item` still works (backward compatible) - [ ] `redeemed_item` appears in code response objects - [ ] `GET /stats` requires auth (returns stats for the authenticated user only) - [ ] Alembic migration runs cleanly on existing data (column is nullable, no data loss) ### Test Expectations - [ ] Test `GET /stats` with 0 codes → Level 1, all zeros - [ ] Test `GET /stats` with 6 redeemed → Level 2, correct xp_progress - [ ] Test `PATCH /codes/{id}/redeem` with `redeemed_item` → item saved - [ ] Test `PATCH /codes/{id}/redeem` without body → still works, `redeemed_item` is null - [ ] Test `GET /stats` counts expired codes correctly (codes past `expires_at` and not redeemed) - Run: `pytest tests/ -v` ### Constraints - Follow existing route patterns in `routes/locations.py` - Use existing `get_current_user` dependency for auth - Level thresholds and names should be constants, not hardcoded in the query - `redeemed_item` is a simple nullable String column, not a foreign key (structured item tracking is Phase 14) ### Checklist - [ ] PR opened - [ ] Tests pass - [ ] Alembic migration included - [ ] No unrelated changes ### Related - `project-mcd-tracker` — parent project - `phase-mcd-tracker-13-gamification` — the phase this feeds into (XP system)
Commenting is not possible because the repository is archived.
No labels
No milestone
No project
No assignees
1 participant
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
ldraney/mcd-tracker-api#16
No description provided.