We now have the week table, which is being written / read properly #413
No reviewers
Labels
No labels
Kestra
bug
enhancement
someday
subtask
☁️ api
🎛️ infrastructure
🐞 sentry
📆 2025 Season
📝 pages
allpicks
📝 pages
picks
📝 pages
standings
🚀 performance
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
johnsturgeon/tgfp-web!413
Loading…
Reference in a new issue
No description provided.
Delete branch "phase-1-make-the-week-table-real-407"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
WeekInfointo its own module (app/models/week_info.py) tobreak the
week.py/model_helperscycle.model_helpers.pyis gone.Week.current()call sites —dependency.py,admin.pyx2,nag_players.py,scheduler.py,update_all_scores.py,game.py.active_weekanduq_week_activeplus a migration. SeeNo pointer column below. Confirm the revision drops the column, not
just the index.
app/jobs/sync_current_week.py. On an interval, derive thecurrent week from the games endpoint and get-or-create the
Weekrowfor it. Expose it on an admin route so it can be run by hand.
schedule_jobs.Week.current()and query for real.Remove the
# pylint: disable=unused-argumentwith it.Resolves #407
- [x] **Move `WeekInfo` into its own module** (`app/models/week_info.py`) to break the `week.py` / `model_helpers` cycle. `model_helpers.py` is gone. - [x] **Thread a session to the `Week.current()` call sites** — `dependency.py`, `admin.py` x2, `nag_players.py`, `scheduler.py`, `update_all_scores.py`, `game.py`. - [x] **Drop `active_week` and `uq_week_active`** plus a migration. See *No pointer column* below. Confirm the revision drops the column, not just the index. - [x] **Write `app/jobs/sync_current_week.py`.** On an interval, derive the current week from the games endpoint and get-or-create the `Week` row for it. Expose it on an admin route so it can be run by hand. - [x] **Schedule it** — on an interval, plus one run at startup before `schedule_jobs`. - [x] **Delete the hardcoded body of `Week.current()`** and query for real. Remove the `# pylint: disable=unused-argument` with it.This is a clean, well-documented refactor. I've verified the import graph (no lingering
model_helpers/current_week_info/active_weekreferences), theNflApiInfoshape (season/season_type/week_noall present), theSeasonTypeordering thatWeek.current()relies on, and the migration. Here's my review.The PR cleanly replaces the hardcoded
current_week_info()with a realWeek.current(session)reader backed by a newsync_current_weekwriter job, breaks theweek.py/model_helpersimport cycle by extractingWeekInfo, and drops the now-unusedactive_weekcolumn with a reversible migration. The design rationale is documented thoroughly in both code comments andTECH_DEBT.md, and I found no correctness regressions in the wiring — the findings below are a test gap and two smaller robustness points.Worth fixing
No test coverage for any of the new logic (
app/models/week.py:33,app/jobs/sync_current_week.py,app/uptime_kuma.py). This is a new source-of-truth writer plus a reader that raises on empty tables, and the suite runs fully offline from fixtures — so the high-value cases are all cheaply testable:Week.current()returning the max row across a season/season_type/week_no boundary,NoCurrentWeekExceptionon an empty table,sync_current_week's get-or-create idempotency (no duplicate row on a second run), and theping_kumaup/down/swallow paths. Given the whole point of the PR is that the week table is now "written / read properly," landing it with zero tests on that path is the main gap.TOCTOU race in
sync_current_week(app/jobs/sync_current_week.py:45-62). The existence check and the insert aren't atomic. The scheduled interval job and a hand-triggered/admin/job_sync_current_week(a route in the threadpool, distinct from the scheduler executor) can both pass.first() is Noneand bothINSERTthe same(season, season_type, week_no);uq_week_season_type_nothen makes the secondcommit()raiseIntegrityError. No data is corrupted (the constraint does its job), but the exception propagates throughping_kuma, which pushes a spurious "down" heartbeat and re-raises into Sentry — i.e. a false alarm on the very monitor this PR adds. CatchingIntegrityErroraround the commit and treating it as a no-op (the row now exists) would make the get-or-create actually concurrency-safe, matching what the comment on line 39-40 already claims it does.Nits
Fresh-DB availability window is wider than "brief" when Tank01 is unreachable at boot (
app/main.py:129-141,app/dependency.py:19).TECH_DEBT.mdframes theNoCurrentWeekException→ 500 window as lasting only "before the first successful sync," but because the startup sync is a date-triggered job that can fail against a provider outage, on a genuinely empty database the next attempt is the hourly interval — so every page can 500 for up to an hour, not just momentarily. This is an accepted tradeoff and only bites a first-ever boot, but the doc undersells the worst case; a sentence acknowledging the Tank01-down-at-first-boot path would be accurate.WeekInfoimported throughaward_helpers(app/jobs/award_update_all.py:20).from app.models.award_helpers import upsert_award_with_args, WeekInfoworks (award_helpers re-exports it), but now thatWeekInfois a first-classapp.modelsexport,from app.models import ..., WeekInfowould be the more direct source and match how the other touched modules were updated in this same PR.