bro - Fix the awards data structure, and have a proper admin page for creating and managing awards #420

Open
opened 2026-08-24 13:58:59 +02:00 by johnsturgeon · 1 comment
Owner

Short version: the award_id linkage isn't the fragile part — init_award_table upserts by slug and updates in place, so ids are stable across restarts. Three real weaknesses, though:

  1. uq_playeraward_player_award_week doesn't fire for 3 of the 4 awards. game_id is in the constraint and is NULL for everything except in_your_face, and Postgres treats NULLs as distinct — verified:

    INSERT INTO t VALUES (1,1,NULL);  -- twice
    SELECT count(*) -> 2
    

    So the only thing preventing duplicate awards is the SELECT-then-insert in upsert_award_with_args, with no backstop if two award syncs overlap.

  2. Renaming a slug orphans history. The upsert keys on slug, so editing "in_your_face" in AWARD_DEFINITIONS inserts a new award row and leaves every existing PlayerAward pointing at the old one — with no error. The slug is the identity, and it's also the field most likely to get tidied up someday.

  3. AwardSlug and AWARD_DEFINITIONS must agree, and nothing checks. A definition whose slug isn't in the enum raises ValueError at startup; an enum member with no definition raises NoResultFound from Award.get_by_slug during award sync, hours later.

Minor: init_award_table never removes awards dropped from the definitions, and upsert_award_with_args commits on every call rather than once per sync.

Short version: the `award_id` linkage isn't the fragile part — `init_award_table` upserts *by slug* and updates in place, so ids are stable across restarts. Three real weaknesses, though: 1. **`uq_playeraward_player_award_week` doesn't fire for 3 of the 4 awards.** `game_id` is in the constraint and is NULL for everything except `in_your_face`, and Postgres treats NULLs as distinct — verified: ```sql INSERT INTO t VALUES (1,1,NULL); -- twice SELECT count(*) -> 2 ``` So the only thing preventing duplicate awards is the `SELECT`-then-insert in `upsert_award_with_args`, with no backstop if two award syncs overlap. 2. **Renaming a slug orphans history.** The upsert keys on `slug`, so editing `"in_your_face"` in `AWARD_DEFINITIONS` inserts a *new* `award` row and leaves every existing `PlayerAward` pointing at the old one — with no error. The slug is the identity, and it's also the field most likely to get tidied up someday. 3. **`AwardSlug` and `AWARD_DEFINITIONS` must agree, and nothing checks.** A definition whose slug isn't in the enum raises `ValueError` at startup; an enum member with no definition raises `NoResultFound` from `Award.get_by_slug` during award sync, hours later. Minor: `init_award_table` never removes awards dropped from the definitions, and `upsert_award_with_args` commits on every call rather than once per sync.
johnsturgeon changed title from Fix the awards data structure, and have a proper admin page for creating and managing awards to bro - Fix the awards data structure, and have a proper admin page for creating and managing awards 2026-08-24 14:01:13 +02:00
Author
Owner

For posterity here is the old add_job



# This is intentionally left here, as dead code with the idea that it will
# move moved after #420 (bro) is implemented
def schedule_award_updates():
    pacific = timezone("America/Los_Angeles")
    trigger = CronTrigger(day_of_week="tue", hour=5, minute=30, timezone=pacific)
    job_scheduler.add_job(
        "app.jobs.award_update_all:scheduled_update_all_awards",
        trigger=trigger,
        id="update_awards",
        misfire_grace_time=MISFIRE_GRACE_WEEKLY,
        replace_existing=True,
    )

For posterity here is the old add_job ```python # This is intentionally left here, as dead code with the idea that it will # move moved after #420 (bro) is implemented def schedule_award_updates(): pacific = timezone("America/Los_Angeles") trigger = CronTrigger(day_of_week="tue", hour=5, minute=30, timezone=pacific) job_scheduler.add_job( "app.jobs.award_update_all:scheduled_update_all_awards", trigger=trigger, id="update_awards", misfire_grace_time=MISFIRE_GRACE_WEEKLY, replace_existing=True, ) ```
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
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
johnsturgeon/tgfp-web#420
No description provided.