Adds four JSON shadow routes to app.py: /api/version, /api/adults, /api/juniors, /api/payments
Small _unwrap_view_model_for_api() helper expands pre-serialised JSON string fields and renames month_labels_json → month_labels, raw_payments_json → raw_payments to match the Go wire contract (builders and templates untouched)
Adds 4 smoke tests in test_app.py asserting top-level key sets match the Go API schemas and that unwrapped fields are objects, not strings
Test plan
make test — 31 tests pass (19 pre-existing + 4 new API tests + 8 others)
make web-py → curl -s localhost:5001/api/adults | jq 'keys' — 14 keys, no _json suffix
/adults, /juniors, /payments HTML pages still render normally
Notes
Raw transaction row shapes in raw_payments / unmatched will diverge from Go's RawTransaction struct (column-header keys vs snake_case). This is the expected M5.4 finding — do not fix here.
## Summary
- Adds four JSON shadow routes to `app.py`: `/api/version`, `/api/adults`, `/api/juniors`, `/api/payments`
- Small `_unwrap_view_model_for_api()` helper expands pre-serialised JSON string fields and renames `month_labels_json` → `month_labels`, `raw_payments_json` → `raw_payments` to match the Go wire contract (builders and templates untouched)
- Adds 4 smoke tests in `test_app.py` asserting top-level key sets match the Go API schemas and that unwrapped fields are objects, not strings
## Test plan
- [x] `make test` — 31 tests pass (19 pre-existing + 4 new API tests + 8 others)
- [ ] `make web-py` → `curl -s localhost:5001/api/adults | jq 'keys'` — 14 keys, no `_json` suffix
- [ ] `curl -s localhost:5001/api/adults | jq '.member_data | type, .month_labels | type, .raw_payments | type'` → all `"object"`
- [ ] `/adults`, `/juniors`, `/payments` HTML pages still render normally
## Notes
Raw transaction row shapes in `raw_payments` / `unmatched` will diverge from Go's `RawTransaction` struct (column-header keys vs snake_case). This is the expected M5.4 finding — do not fix here.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Four new JSON routes mirror the Go /api/* handlers so the M5.4 parity
tool can diff them: /api/version, /api/adults, /api/juniors,
/api/payments. A small _unwrap_view_model_for_api() helper in app.py
expands the three pre-serialised JSON strings in the view-model dicts
and renames month_labels_json → month_labels and
raw_payments_json → raw_payments to match the Go wire contract.
Tests in test_app.py assert top-level key sets match the Go API schema
and that member_data, month_labels, raw_payments are objects not strings.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
app.py:/api/version,/api/adults,/api/juniors,/api/payments_unwrap_view_model_for_api()helper expands pre-serialised JSON string fields and renamesmonth_labels_json→month_labels,raw_payments_json→raw_paymentsto match the Go wire contract (builders and templates untouched)test_app.pyasserting top-level key sets match the Go API schemas and that unwrapped fields are objects, not stringsTest plan
make test— 31 tests pass (19 pre-existing + 4 new API tests + 8 others)make web-py→curl -s localhost:5001/api/adults | jq 'keys'— 14 keys, no_jsonsuffixcurl -s localhost:5001/api/adults | jq '.member_data | type, .month_labels | type, .raw_payments | type'→ all"object"/adults,/juniors,/paymentsHTML pages still render normallyNotes
Raw transaction row shapes in
raw_payments/unmatchedwill diverge from Go'sRawTransactionstruct (column-header keys vs snake_case). This is the expected M5.4 finding — do not fix here.🤖 Generated with Claude Code