129 lines
4.3 KiB
Python
129 lines
4.3 KiB
Python
from dataclasses import asdict, dataclass, fields
|
|
from typing import Any, Final
|
|
|
|
MISSING: Final[Any] = object()
|
|
|
|
@dataclass
|
|
class TeamTrackerValues:
|
|
"""Schema for all Team Tracker sensor attributes."""
|
|
# Core Metadata
|
|
state: str | None = MISSING
|
|
sport: str | None = MISSING
|
|
sport_path: str | None = MISSING
|
|
league: str | None = MISSING
|
|
league_path: str | None = MISSING
|
|
league_logo: str | None = MISSING
|
|
league_name: str | None = MISSING
|
|
season: str | None = MISSING
|
|
|
|
# Event Details
|
|
team_abbr: str | None = MISSING
|
|
opponent_abbr: str | None = MISSING
|
|
event_id: str | None = MISSING
|
|
event_name: str | None = MISSING
|
|
event_url: str | None = MISSING
|
|
event_stream: str | None = MISSING
|
|
date: str | None = MISSING
|
|
kickoff_in: str | None = MISSING
|
|
series_summary: str | None = MISSING
|
|
venue: str | None = MISSING
|
|
location: str | None = MISSING
|
|
tv_network: str | None = MISSING
|
|
odds: str | None = MISSING
|
|
overunder: str | None = MISSING
|
|
|
|
# Team Data
|
|
team_name: str | None = MISSING
|
|
team_long_name: str | None = MISSING
|
|
team_id: str | None = MISSING
|
|
team_record: str | None = MISSING
|
|
team_rank: str | None = MISSING
|
|
team_conference_id: str | None = MISSING
|
|
team_homeaway: str | None = MISSING
|
|
team_logo: str | None = MISSING
|
|
team_url: str | None = MISSING
|
|
team_stream: str | None = MISSING
|
|
team_colors: list[str] | None = MISSING
|
|
team_score: str | None = MISSING
|
|
team_win_probability: float| None= MISSING
|
|
team_winner: bool | None = MISSING
|
|
team_timeouts: int | None = MISSING
|
|
|
|
# Opponent Data
|
|
opponent_name: str | None = MISSING
|
|
opponent_long_name: str | None = MISSING
|
|
opponent_id: str | None = MISSING
|
|
opponent_record: str | None = MISSING
|
|
opponent_rank: str | None = MISSING
|
|
opponent_conference_id: str | None = MISSING
|
|
opponent_homeaway: str | None = MISSING
|
|
opponent_logo: str | None = MISSING
|
|
opponent_url: str | None = MISSING
|
|
opponent_stream: str | None = MISSING
|
|
opponent_colors: list[str] | None = MISSING
|
|
opponent_score: str | None = MISSING
|
|
opponent_win_probability: float | None = MISSING
|
|
opponent_winner: bool | None = MISSING
|
|
opponent_timeouts: int | None = MISSING
|
|
|
|
# Timing / Legacy Names
|
|
quarter: str | None = MISSING
|
|
clock: str | None = MISSING
|
|
possession: str | None = MISSING
|
|
last_play: str | None = MISSING
|
|
down_distance_text: str | None = MISSING
|
|
|
|
# Baseball Specific
|
|
outs: int | None = MISSING
|
|
balls: int | None = MISSING
|
|
strikes: int | None = MISSING
|
|
on_first: bool | None = MISSING
|
|
on_second: bool | None = MISSING
|
|
on_third: bool | None = MISSING
|
|
|
|
# Soccer/Hockey
|
|
team_shots_on_target: int | None = MISSING
|
|
team_total_shots: int | None = MISSING
|
|
opponent_shots_on_target: int | None = MISSING
|
|
opponent_total_shots: int | None = MISSING
|
|
|
|
# Volleyball
|
|
team_sets_won: str | None = MISSING
|
|
opponent_sets_won: str | None = MISSING
|
|
|
|
# System/API Metadata
|
|
last_update: str | None = MISSING
|
|
api_message: str | None = MISSING
|
|
api_url: str | None = MISSING
|
|
private_fast_refresh: bool = False
|
|
|
|
@classmethod
|
|
def from_dict(cls, values_dict: dict[str, Any]) -> "TeamTrackerValues":
|
|
"""Initialize dataclass from a dictionary, ignoring extra keys."""
|
|
# Get the names of all valid fields in this dataclass
|
|
valid_fields = {f.name for f in fields(cls)}
|
|
|
|
# Filter the input dict to only include valid keys
|
|
filtered_dict = {k: v for k, v in values_dict.items() if k in valid_fields}
|
|
|
|
return cls(**filtered_dict)
|
|
|
|
def to_dict(self) -> dict[str, Any]:
|
|
"""Convert to dict, but only include fields that were actually set."""
|
|
return {
|
|
k: v for k, v in asdict(self).items()
|
|
if v is not MISSING
|
|
}
|
|
|
|
def to_dict_all_attr(self) -> dict[str, Any]:
|
|
"""Convert properties to a dictionary, translating MISSING sentinels to None."""
|
|
# A robust check that traps the sentinel even if it was deep-copied
|
|
return {
|
|
f.name: (
|
|
None
|
|
if getattr(self, f.name) is MISSING or type(getattr(self, f.name)).__name__ == 'object'
|
|
else getattr(self, f.name)
|
|
)
|
|
for f in fields(self)
|
|
}
|