-
Notifications
You must be signed in to change notification settings - Fork 0
Add timeout_type field to /live endpoint + client field crosswalk #7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,78 @@ | ||
| # Client Field Crosswalk (Basic) | ||
|
|
||
| This document maps the client’s requested scoreboard display values against the current `GET /live` response. | ||
|
|
||
| Scope is intentionally basic and focused on what is needed for a first pass display. | ||
|
|
||
| ## 1) Requested fields vs `GET /live` | ||
|
|
||
| | Client requested value | Available in `GET /live` | Field(s) | | ||
| |---|---|---| | ||
| | Period | Yes | `period` | | ||
| | Jam | Yes | `jam` | | ||
| | Period Clock | Yes | `period_clock_ms` | | ||
| | Jam Clock | Yes | `jam_clock_ms` | | ||
| | Home Score | Yes | `team1.score` | | ||
| | Away Score | Yes | `team2.score` | | ||
| | Home Jam Score | Yes | `team1.jam_score` | | ||
| | Away Jam Score | Yes | `team2.jam_score` | | ||
| | Home PP Timer | Yes (via skater box timer) | `team1.jammer.in_box`, `team1.jammer.box_time_remaining_s` | | ||
| | Away PP Timer | Yes (via skater box timer) | `team2.jammer.in_box`, `team2.jammer.box_time_remaining_s` | | ||
| | Home Player Tracker (1-5) | Yes | `team1.jammer`, `team1.pivot`, `team1.blocker1`, `team1.blocker2`, `team1.blocker3` | | ||
| | Away Player Tracker (1-5) | Yes | `team2.jammer`, `team2.pivot`, `team2.blocker1`, `team2.blocker2`, `team2.blocker3` | | ||
|
|
||
| Notes: | ||
| - Player tracker rows include skater `name`, `number`, and penalty-box state (`in_box`, `box_time_remaining_s`). | ||
| - PP timer can be rendered from jammer box state/timer for each team. | ||
|
|
||
| ## 2) Key values currently missing from `GET /live` | ||
|
|
||
| The following are not currently mapped into `GET /live` and should be added for the production broadcast display: | ||
|
|
||
| 1. Lead jammer skater (explicit display field) | ||
| - Today, lead can be inferred via `team1.lead` / `team2.lead` plus jammer identity, but there is no explicit top-level “lead jammer skater” value. | ||
|
|
||
| 2. Timeout state and type | ||
| - Team timeout | ||
| - Official timeout | ||
| - Official review | ||
|
|
||
| 3. Timeout ownership and counters | ||
| - Which team called timeout/review | ||
| - Team timeouts remaining per team | ||
| - Official review availability/status per team | ||
|
|
||
| 4. Timeout/review timer behavior | ||
| - Team timeout clock (fixed 60s) | ||
| - Official timeout timing (variable duration) | ||
| - Official review timing (variable duration) | ||
|
|
||
| 5. Post-timeout to jam-start phase | ||
| - A display flag/timer for the window between timeout end and next jam start. | ||
|
|
||
| ## 3) Rules/operations notes to confirm with league | ||
|
|
||
| - Team timeouts: each team has three total, but event policy should confirm whether tracked per game or displayed per half in this production. | ||
| - Official review: one per team per half, and may be retained if successful (once). | ||
| - Official timeout and official review durations are not fixed. | ||
|
|
||
| These rules are officiating policy context. The API should still expose neutral fields (type, owner, running, remaining, total/used/remaining) so the display can apply event-specific presentation. | ||
|
|
||
| ## 4) Minimal overview fields for immediate implementation | ||
|
|
||
| For the fastest basic display, include this overview set: | ||
|
|
||
| - State: `period`, `jam`, `game_state`, `in_jam`, `jam_running` | ||
| - Clocks: `period_clock_ms`, `jam_clock_ms` | ||
| - Scores: `team1.score`, `team2.score`, `team1.jam_score`, `team2.jam_score` | ||
| - Lead and star pass indicators: `team1.lead`, `team2.lead`, `team1.star_pass`, `team2.star_pass` | ||
| - Player tracker: all five positions for both teams (name, number, in-box, box timer) | ||
| - Timeout package (to add): timeout type, owner, running flag, remaining ms (when fixed), team timeout counts, official review status, post-timeout phase indicator | ||
|
|
||
| ## 5) Follow-up implementation plan | ||
|
|
||
| 1. Use `GET /raw` during a live timeout/review sequence to capture exact CRG key names. | ||
| 2. Add timeout/review fields to `models.py` (`LiveState` and/or `TeamState`). | ||
| 3. Map those keys in `client.py` field maps. | ||
| 4. Add tests for team timeout, official timeout, official review, and post-timeout phase. | ||
| 5. Update display UI to render timeout/review banner and counters. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -207,14 +207,40 @@ def _team(self, n: int) -> TeamState: | |
| ) | ||
| return TeamState(**flat_fields, **positions) | ||
|
|
||
| @staticmethod | ||
| def _normalize_timeout_type(game_state: Optional[str], jam_running: Optional[bool]) -> Optional[str]: | ||
| """Normalize timeout/review from game state; clear once jam is running again.""" | ||
| if jam_running: | ||
| return None | ||
| if not game_state: | ||
| return None | ||
|
|
||
| state = game_state.strip().lower() | ||
| if "official review" in state: | ||
| return "official_review" | ||
| if "official timeout" in state: | ||
| return "official_timeout" | ||
| if "team timeout" in state: | ||
| return "team_timeout" | ||
| if "timeout" in state: | ||
| return "timeout" | ||
| if "review" in state: | ||
| return "official_review" | ||
| return None | ||
|
Comment on lines
+218
to
+229
|
||
|
|
||
| def get_live_state(self) -> LiveState: | ||
| """Build and return a LiveState model from current raw state.""" | ||
| game_fields = { | ||
| field: self._get(suffix, typ) | ||
| for field, (suffix, typ) in GAME_FIELD_MAP.items() | ||
| } | ||
| timeout_type = self._normalize_timeout_type( | ||
| game_state=game_fields.get("game_state"), | ||
| jam_running=game_fields.get("jam_running"), | ||
| ) | ||
| return LiveState( | ||
| **game_fields, | ||
| timeout_type=timeout_type, | ||
| team1=self._team(1), | ||
| team2=self._team(2), | ||
| ) | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -87,6 +87,12 @@ async def test_live_jammer_names(app_client): | |||||||
| assert data["team2"]["jammer"]["name"] == "Lightning Bolt" | ||||||||
|
|
||||||||
|
|
||||||||
| async def test_live_includes_timeout_type_field(app_client): | ||||||||
| resp = await app_client.get("/live") | ||||||||
| data = resp.json() | ||||||||
| assert "timeout_type" in data | ||||||||
|
||||||||
| assert "timeout_type" in data | |
| assert "timeout_type" in data | |
| assert data["timeout_type"] is None |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This crosswalk claims “Timeout state and type” are currently missing from
GET /live, but this PR addstimeout_type. Please update this section (and the later “Timeout package (to add)” bullet) to reflect thattimeout_typeis now available, while ownership/counters/timers may still be missing.