Commit Graph

19 Commits

Author SHA1 Message Date
josh e908139323 fix: pin playoff card to its own gameNumber so the header doesn't tick forward after a final
CI / Lint (push) Successful in 10s
CI / Test (push) Successful in 10s
CI / Build & Push (push) Successful in 23s
series_state derives game_number from topSeedWins + bottomSeedWins + 1, which becomes the *next* game's number once the API advances seriesStatus post-final. The card for the just-finished game would then read "Game N+1 of 7". Prefer the raw gameNumber on the game payload, falling back to the derived value when it's missing.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-19 18:41:04 -04:00
josh 0f1c558493 style: tighten playoff card copy — uniform Game X of 7 header, leaner blurbs
CI / Lint (push) Successful in 10s
CI / Test (push) Successful in 11s
CI / Build & Push (push) Successful in 23s
Top row now always reads "Game N of 7" (round is already shown by the R1/R2/CONF FINAL/CUP FINAL badge). Bottom blurb drops the trailing period on "Series opener" and uses "{Team} lead X-Y" (plural verb, no "— Game N" suffix) when a leader is established.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-19 18:36:11 -04:00
josh a88e2edef0 fix: anchor Day N to each round's first game instead of lazy first sighting
CI / Lint (push) Successful in 5s
CI / Test (push) Successful in 9s
CI / Build & Push (push) Successful in 19s
The banner read "Day 1 of ~60" on day 2 of the playoffs because the old
anchor recorded whatever date we first polled a playoff game as Day 1.
Now round start dates come from /v1/schedule/playoff-series, so Day N
is authoritative and resets at each round boundary. Drops the noisy
"of ~60" denominator.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-19 13:03:08 -04:00
josh 930247b32f style: apply ruff format and fix lint issues in playoff modules
CI / Lint (push) Successful in 8s
CI / Test (push) Successful in 10s
CI / Build & Push (push) Successful in 22s
- Rename single-letter `l` loop variables in bracket_view to satisfy E741
- Drop unused `json` import from test_playoff_cache (F401/F811)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-19 12:48:42 -04:00
josh ebe770fecd feat: make scoreboard playoff-aware with banner, bracket, and series drill-down
CI / Lint (push) Failing after 8s
CI / Test (push) Has been skipped
CI / Build & Push (push) Has been skipped
Turn a regular-season-looking Tuesday into a full playoff experience:

- Playoff banner with round + day + series + elimination counts, gold/silver
  Cup theme toggled by body.playoff-mode
- Series context on each playoff card: round chip, series score, stake badges
  (GAME 7, CLINCHER, PIVOTAL), and one-line blurb
- Game 7s pin to a new Spotlight section above Live
- Playoff OT renders with SUDDEN DEATH badge and pulsing gold border
- Client-side OT notifications via bell button in the banner
- New /series/<id> drill-down with headline, next-game, and game-by-game history
- New /bracket page: 7-column desktop grid, accordion on mobile
- Day N banner count auto-anchors on first playoff scoreboard hit
- SQLite cache for bracket + per-series schedules, stale-on-failure up to 24h

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-19 12:47:31 -04:00
josh e0db8f0859 refactor: recalibrate hype scoring to deflate gauge and add momentum signals
CI / Lint (push) Successful in 7s
CI / Test (push) Successful in 6s
CI / Build & Push (push) Successful in 15s
Unify overlapping late-P3 bonuses into a single score-state lookup, add
high-scoring and goal-spike signals, and tighten every component ceiling so
filling the hype bar is reserved for genuinely rare moments.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-19 11:26:07 -04:00
josh 61202b2a70 feat: sort scheduled games by start time instead of hype
Pre-game listings are more intuitive in chronological order than ranked
by pregame importance. LIVE and FINAL keep sorting by Priority desc.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-19 11:05:35 -04:00
josh 7784eaf9ce feat: overhaul hype score algorithm with 9 hockey-driven improvements
CI / Lint (push) Successful in 6s
CI / Test (push) Successful in 6s
CI / Build & Push (push) Successful in 16s
- Empty net detection: pulled goalie triggers +150-250 bonus, stacks with PP
- Playoff series importance: Game 7 Cup Final = 200, elimination games scale
  by round; fallback of 100 when series data unavailable
- Period-aware score differential: 2-goal deficit penalty DECREASES in final
  2 min of P3 (goalie-pull zone), 3+ goal games get harsher penalties late
- Persistent comeback narrative: tracks max deficit, sustained bonus for 2+
  goal recoveries instead of one-shot spike (0-3 to 3-3 = 150 persistent)
- Shootout special handling: flat base 550 with no time component; ranks below
  dramatic close P3 games (skills competition, not hockey)
- Multi-man advantage: parses situationCode for 5v3/4v3, applies 1.6x PP mult
- Non-linear time priority: elapsed^1.5 curve weights final minutes more
- Matchup multiplier rebalance: P1/P2 from 2.0/1.65 to 1.5, tiebreaker not
  dominant factor
- Frontend gauge max raised from 700 to 1000 with adjusted color thresholds

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-31 19:02:43 -04:00
josh 6c098850f5 fix: use truthy check for intermission filter, add route test
CI / Lint (push) Successful in 7s
CI / Test (push) Successful in 6s
CI / Build & Push (push) Successful in 19s
`is True` strict identity fails if the NHL API returns an integer 1
instead of a JSON boolean. A truthy check is safe here since the
Intermission field is always False/0 for non-intermission live games.

Also adds a test that verifies intermission games are separated from
live games in the /scoreboard response.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 19:57:00 -04:00
josh 8e1c455ded fix: correct NHL API situation structure for power play detection
CI / Lint (push) Successful in 6s
CI / Test (push) Successful in 6s
CI / Build & Push (push) Successful in 22s
The NHL API nests situationDescriptions under situation.homeTeam /
situation.awayTeam, not at the top level. The old flat-structure
lookup always returned an empty list, silently breaking both the
PP indicator on the frontend and the PP bonus in the hype score.
Updated get_power_play_info, the _priority_components PP check,
and all test fixtures to match the real API shape.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 19:37:31 -04:00
josh bf39bb6bd5 style: apply ruff formatting
CI / Lint (push) Successful in 6s
CI / Test (push) Successful in 7s
CI / Build & Push (push) Successful in 18s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 18:41:10 -04:00
josh 47a8c34215 feat: game importance factor in hype scoring
CI / Lint (push) Failing after 6s
CI / Test (push) Has been skipped
CI / Build & Push (push) Has been skipped
Adds calculate_game_importance() that boosts Priority for high-stakes
regular-season matchups based on season progress (sharp ramp after game
55), playoff bubble proximity (wildcard rank ~17-19 = max relevance),
and divisional/conference rivalry (1.4x/1.2x multipliers). Max bonus
150 pts applied to both LIVE and PRE games; playoff and FINAL games
are unaffected. Extends standings schema with division, conference,
games_played, and wildcard_sequence fields fetched from the NHL API.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 18:39:55 -04:00
josh e2d2c7dd97 feat: overhaul hype scoring algorithm
CI / Lint (push) Successful in 6s
CI / Test (push) Successful in 6s
CI / Build & Push (push) Successful in 15s
- Period base: playoff OT escalates indefinitely (P4=600, P5=750…),
  reg season P4=5-min OT (600), P5=shootout (700)
- Time priority range increased (max ~300 vs old ~120), calibrated
  to period length so 5-min reg season OT reads correctly
- Matchup multiplier inverted: higher period = less weight (any OT
  is exciting regardless of teams)
- Replace unconditional score_total with closeness bonus: rewards
  tight games regardless of goal volume (5-4 == 1-0 at same diff)
- Power play bonus: 30 (P1/P2) → 50/100/150 (P3 by time) → 200 (OT)
- Comeback bonus: one-time pulse (+50/75/100 by period) when trailing
  team scores to within 2 goals; keyed on team names, clears after
  firing, skips intermission

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 14:46:10 -04:00
josh 3169d1a1ff fix: resolve 4 logic bugs found in code review
CI / Lint (push) Successful in 5s
CI / Test (push) Successful in 5s
CI / Build & Push (push) Successful in 17s
- utc_to_eastern: use zoneinfo instead of hardcoded EDT offset (-4)
  so start times are correct in both EST and EDT
- standings: fetch before truncate so a failed API call doesn't wipe
  existing standings data
- routes: call parse_games() once per request instead of three times
- scheduler: wrap run_pending() in try/except so an unhandled exception
  doesn't kill the background thread

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 14:06:45 -04:00
josh 9ad563ed3f feat: add shots on goal bar to live game cards, clean up gitignore
CI / Lint (push) Successful in 5s
CI / Test (push) Successful in 5s
CI / Build & Push (push) Successful in 13s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 13:19:59 -04:00
josh def491a4d4 test: add full test suite with 100% coverage across all modules
CI / Lint (push) Successful in 6s
CI / Test (push) Successful in 7s
CI / Build & Push (push) Successful in 15s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 13:17:20 -04:00
josh dd5ac945bd refactor: rename functions across codebase for clarity
CI / Lint (push) Successful in 7s
CI / Test (push) Successful in 5s
CI / Build & Push (push) Successful in 14s
2026-03-29 10:21:01 -04:00
josh a4dc7dff52 refactor: flatten app/scoreboard/ subpackage and rename files for clarity
CI / Lint (push) Successful in 5s
CI / Test (push) Successful in 6s
CI / Build & Push (push) Successful in 16s
2026-03-29 10:16:35 -04:00
josh 3994943757 good luck
CI / Lint (push) Successful in 58s
CI / Test (push) Successful in 8s
CI / Build & Push (push) Failing after 1m33s
2026-03-29 09:20:21 -04:00