Files
hockey_new/hockey_data/models.py
2026-08-19 15:08:39 +03:00

1185 lines
62 KiB
Python

from __future__ import annotations
from datetime import date, datetime
from typing import Optional
from sqlalchemy import (
Boolean,
Date,
DateTime,
ForeignKey,
Float,
Index,
Integer,
String,
Text,
UniqueConstraint,
)
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
class Base(DeclarativeBase):
pass
class Tournament(Base):
__tablename__ = "hockey_tournaments"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
external_id: Mapped[str] = mapped_column(String(64), nullable=False, unique=True)
level: Mapped[str] = mapped_column(String(32), nullable=False, default="")
name_ru: Mapped[str] = mapped_column(Text, nullable=False, default="")
name_en: Mapped[str] = mapped_column(Text, nullable=False, default="")
common_name_ru: Mapped[str] = mapped_column(Text, nullable=False, default="")
common_name_en: Mapped[str] = mapped_column(Text, nullable=False, default="")
season_part: Mapped[str] = mapped_column(String(32), nullable=False, default="")
season: Mapped[str] = mapped_column(String(32), nullable=False, default="")
start_date: Mapped[Optional[date]] = mapped_column(Date, nullable=True)
end_date: Mapped[Optional[date]] = mapped_column(Date, nullable=True)
round_code: Mapped[str] = mapped_column(String(16), nullable=False, default="")
game_id_supported: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
raw_xml: Mapped[str] = mapped_column(Text, nullable=False, default="")
synced_at: Mapped[datetime] = mapped_column(
DateTime, nullable=False, default=datetime.utcnow
)
__table_args__ = (
Index("ix_hockey_tournaments_level", "level"),
Index("ix_hockey_tournaments_season", "season"),
Index("ix_hockey_tournaments_dates", "start_date", "end_date"),
Index("ix_hockey_tournaments_stage", "season_part"),
)
class TournamentStanding(Base):
__tablename__ = "hockey_tournament_standings"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
tournament_external_id: Mapped[str] = mapped_column(
String(64), ForeignKey("hockey_tournaments.external_id"),
nullable=False, unique=True,
)
source_endpoint: Mapped[str] = mapped_column(Text, nullable=False, default="")
raw_payload: Mapped[str] = mapped_column(Text, nullable=False, default="")
synced_at: Mapped[datetime] = mapped_column(
DateTime, nullable=False, default=datetime.utcnow
)
class TournamentStatisticResource(Base):
"""Optional tournament XML such as powerplay or rank."""
__tablename__ = "hockey_tournament_statistic_resources"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
tournament_external_id: Mapped[str] = mapped_column(
String(64), ForeignKey("hockey_tournaments.external_id"), nullable=False
)
resource_type: Mapped[str] = mapped_column(String(32), nullable=False)
source_endpoint: Mapped[str] = mapped_column(Text, nullable=False, default="")
raw_payload: Mapped[str] = mapped_column(Text, nullable=False, default="")
available: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
status_code: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
error_message: Mapped[str] = mapped_column(Text, nullable=False, default="")
checked_at: Mapped[datetime] = mapped_column(
DateTime, nullable=False, default=datetime.utcnow
)
synced_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True)
__table_args__ = (
UniqueConstraint(
"tournament_external_id",
"resource_type",
name="uq_hockey_tournament_stat_resource",
),
Index(
"ix_hockey_tournament_stat_resource_lookup",
"tournament_external_id",
"resource_type",
),
)
class TournamentStandingRow(Base):
"""Normalized team row from standings-{tournament}.xml."""
__tablename__ = "hockey_tournament_standing_rows"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
tournament_external_id: Mapped[str] = mapped_column(String(64), nullable=False)
variant_id: Mapped[str] = mapped_column(String(32), nullable=False, default="league")
group_id: Mapped[str] = mapped_column(String(96), nullable=False, default="")
group_name_ru: Mapped[str] = mapped_column(Text, nullable=False, default="")
group_name_en: Mapped[str] = mapped_column(Text, nullable=False, default="")
standings_type: Mapped[str] = mapped_column(String(64), nullable=False, default="")
conference: Mapped[str] = mapped_column(String(64), nullable=False, default="")
playoff_places: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
team_key: Mapped[str] = mapped_column(String(128), nullable=False)
team_entry_external_id: Mapped[str] = mapped_column(String(96), nullable=False, default="")
club_external_id: Mapped[str] = mapped_column(String(96), nullable=False, default="")
team_name_ru: Mapped[str] = mapped_column(Text, nullable=False, default="")
team_name_en: Mapped[str] = mapped_column(Text, nullable=False, default="")
rank: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
games: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
wins: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
overtime_wins: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
shootout_wins: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
shootout_losses: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
overtime_losses: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
losses: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
points: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
points_pct: Mapped[float] = mapped_column(Float, nullable=False, default=0.0)
goals_for: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
goals_against: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
penalty_minutes: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
opponent_penalty_minutes: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
playoffs_in: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
playoffs_out: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
raw_payload: Mapped[str] = mapped_column(Text, nullable=False, default="")
synced_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=datetime.utcnow)
__table_args__ = (
UniqueConstraint(
"tournament_external_id", "variant_id", "group_id", "team_key",
name="uq_hockey_standing_row",
),
Index("ix_hockey_standing_rows_tournament", "tournament_external_id"),
Index("ix_hockey_standing_rows_team", "tournament_external_id", "team_key"),
)
class TournamentStatisticRow(Base):
"""Normalized row from optional powerplay/rank tournament feeds."""
__tablename__ = "hockey_tournament_statistic_rows"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
tournament_external_id: Mapped[str] = mapped_column(String(64), nullable=False)
resource_type: Mapped[str] = mapped_column(String(32), nullable=False)
section_id: Mapped[str] = mapped_column(String(128), nullable=False, default="")
section_name_ru: Mapped[str] = mapped_column(Text, nullable=False, default="")
section_name_en: Mapped[str] = mapped_column(Text, nullable=False, default="")
period_ru: Mapped[str] = mapped_column(String(128), nullable=False, default="")
period_en: Mapped[str] = mapped_column(String(128), nullable=False, default="")
row_key: Mapped[str] = mapped_column(String(192), nullable=False)
row_external_id: Mapped[str] = mapped_column(String(96), nullable=False, default="")
entity_type: Mapped[str] = mapped_column(String(32), nullable=False, default="unknown")
player_external_id: Mapped[str] = mapped_column(String(96), nullable=False, default="")
team_external_id: Mapped[str] = mapped_column(String(96), nullable=False, default="")
rank: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
number: Mapped[str] = mapped_column(String(32), nullable=False, default="")
name_ru: Mapped[str] = mapped_column(Text, nullable=False, default="")
name_en: Mapped[str] = mapped_column(Text, nullable=False, default="")
team_name_ru: Mapped[str] = mapped_column(Text, nullable=False, default="")
team_name_en: Mapped[str] = mapped_column(Text, nullable=False, default="")
games: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
goals: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
assists: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
points: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
penalty_minutes: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
power_play_goals: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
power_play_assists: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
power_play_points: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
power_play_opportunities: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
power_play_pct: Mapped[float] = mapped_column(Float, nullable=False, default=0.0)
short_handed_goals: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
wins: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
losses: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
save_pct: Mapped[float] = mapped_column(Float, nullable=False, default=0.0)
goals_against_average: Mapped[float] = mapped_column(Float, nullable=False, default=0.0)
time_on_ice: Mapped[str] = mapped_column(String(64), nullable=False, default="")
values_json: Mapped[str] = mapped_column(Text, nullable=False, default="{}")
raw_payload: Mapped[str] = mapped_column(Text, nullable=False, default="")
synced_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=datetime.utcnow)
__table_args__ = (
UniqueConstraint(
"tournament_external_id", "resource_type", "section_id", "row_key",
name="uq_hockey_tournament_statistic_row",
),
Index("ix_hockey_tournament_stat_rows_lookup", "tournament_external_id", "resource_type"),
Index("ix_hockey_tournament_stat_rows_player", "player_external_id"),
Index("ix_hockey_tournament_stat_rows_team", "team_external_id"),
)
class TeamSeasonStatistic(Base):
"""One normalized season aggregate per tournament team."""
__tablename__ = "hockey_team_season_statistics"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
tournament_external_id: Mapped[str] = mapped_column(String(64), nullable=False)
team_key: Mapped[str] = mapped_column(String(128), nullable=False)
team_entry_external_id: Mapped[str] = mapped_column(String(96), nullable=False, default="")
club_external_id: Mapped[str] = mapped_column(String(96), nullable=False, default="")
team_name_ru: Mapped[str] = mapped_column(Text, nullable=False, default="")
team_name_en: Mapped[str] = mapped_column(Text, nullable=False, default="")
rank: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
games: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
wins: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
overtime_wins: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
shootout_wins: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
shootout_losses: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
overtime_losses: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
losses: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
points: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
points_pct: Mapped[float] = mapped_column(Float, nullable=False, default=0.0)
goals_for: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
goals_against: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
penalty_minutes: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
opponent_penalty_minutes: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
power_play_goals: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
power_play_opportunities: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
power_play_pct: Mapped[float] = mapped_column(Float, nullable=False, default=0.0)
power_play_values_json: Mapped[str] = mapped_column(Text, nullable=False, default="{}")
standings_raw_payload: Mapped[str] = mapped_column(Text, nullable=False, default="")
powerplay_raw_payload: Mapped[str] = mapped_column(Text, nullable=False, default="")
synced_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=datetime.utcnow)
__table_args__ = (
UniqueConstraint("tournament_external_id", "team_key", name="uq_hockey_team_season_stat"),
Index("ix_hockey_team_season_stats_tournament", "tournament_external_id"),
)
class Team(Base):
__tablename__ = "hockey_teams"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
external_id: Mapped[str] = mapped_column(String(64), nullable=False, unique=True)
name_ru: Mapped[str] = mapped_column(Text, nullable=False, default="")
name_en: Mapped[str] = mapped_column(Text, nullable=False, default="")
short_name_ru: Mapped[str] = mapped_column(String(255), nullable=False, default="")
short_name_en: Mapped[str] = mapped_column(String(255), nullable=False, default="")
city_ru: Mapped[str] = mapped_column(String(255), nullable=False, default="")
city_en: Mapped[str] = mapped_column(String(255), nullable=False, default="")
logo_url: Mapped[str] = mapped_column(Text, nullable=False, default="")
raw_payload: Mapped[str] = mapped_column(Text, nullable=False, default="")
synced_at: Mapped[datetime] = mapped_column(
DateTime, nullable=False, default=datetime.utcnow
)
class TeamLeague(Base):
"""League membership used by the editable team directory."""
__tablename__ = "hockey_team_leagues"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
team_id: Mapped[int] = mapped_column(
Integer, ForeignKey("hockey_teams.id", ondelete="CASCADE"), nullable=False
)
league_key: Mapped[str] = mapped_column(String(32), nullable=False)
tournament_external_id: Mapped[str] = mapped_column(
String(64), nullable=False, default=""
)
source: Mapped[str] = mapped_column(String(32), nullable=False, default="stat2tv")
active: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
synced_at: Mapped[datetime] = mapped_column(
DateTime, nullable=False, default=datetime.utcnow
)
__table_args__ = (
UniqueConstraint(
"team_id",
"league_key",
name="uq_hockey_team_league_team_level",
),
Index("ix_hockey_team_leagues_level", "league_key"),
Index(
"ix_hockey_team_leagues_tournament",
"tournament_external_id",
),
)
class PenaltyType(Base):
"""Global bilingual infraction directory shared by every hockey league."""
__tablename__ = "hockey_penalty_types"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
code: Mapped[str] = mapped_column(String(64), nullable=False, unique=True)
name_ru: Mapped[str] = mapped_column(Text, nullable=False, default="")
name_en: Mapped[str] = mapped_column(Text, nullable=False, default="")
default_preset: Mapped[str] = mapped_column(
String(32), nullable=False, default="2"
)
team_penalty: Mapped[bool] = mapped_column(
Boolean, nullable=False, default=False
)
sort_order: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
active: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
created_at: Mapped[datetime] = mapped_column(
DateTime, nullable=False, default=datetime.utcnow
)
updated_at: Mapped[datetime] = mapped_column(
DateTime, nullable=False, default=datetime.utcnow
)
__table_args__ = (
Index("ix_hockey_penalty_types_active_order", "active", "sort_order"),
)
class Country(Base):
__tablename__ = "hockey_countries"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
external_id: Mapped[str] = mapped_column(String(64), nullable=False, unique=True)
iso2: Mapped[str] = mapped_column(String(2), nullable=False, default="")
iso3: Mapped[str] = mapped_column(String(3), nullable=False, default="")
name_ru: Mapped[str] = mapped_column(String(255), nullable=False, default="")
name_en: Mapped[str] = mapped_column(String(255), nullable=False, default="")
flag_emoji: Mapped[str] = mapped_column(String(16), nullable=False, default="")
active: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
raw_payload: Mapped[str] = mapped_column(Text, nullable=False, default="")
synced_at: Mapped[datetime] = mapped_column(
DateTime, nullable=False, default=datetime.utcnow
)
__table_args__ = (
Index("ix_hockey_countries_iso2", "iso2"),
Index("ix_hockey_countries_iso3", "iso3"),
Index("ix_hockey_countries_name_ru", "name_ru"),
)
class Player(Base):
__tablename__ = "hockey_players"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
external_id: Mapped[str] = mapped_column(String(64), nullable=False, unique=True)
first_name_ru: Mapped[str] = mapped_column(String(255), nullable=False, default="")
first_name_en: Mapped[str] = mapped_column(String(255), nullable=False, default="")
last_name_ru: Mapped[str] = mapped_column(String(255), nullable=False, default="")
last_name_en: Mapped[str] = mapped_column(String(255), nullable=False, default="")
full_name_ru: Mapped[str] = mapped_column(Text, nullable=False, default="")
full_name_en: Mapped[str] = mapped_column(Text, nullable=False, default="")
position_ru: Mapped[str] = mapped_column(String(128), nullable=False, default="")
position_en: Mapped[str] = mapped_column(String(128), nullable=False, default="")
birth_date: Mapped[Optional[date]] = mapped_column(Date, nullable=True)
height_cm: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
weight_kg: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
stick: Mapped[str] = mapped_column(String(32), nullable=False, default="")
country_external_id: Mapped[str] = mapped_column(String(64), nullable=False, default="")
country_code: Mapped[str] = mapped_column(String(8), nullable=False, default="")
active: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
photo_url: Mapped[str] = mapped_column(Text, nullable=False, default="")
raw_payload: Mapped[str] = mapped_column(Text, nullable=False, default="")
synced_at: Mapped[datetime] = mapped_column(
DateTime, nullable=False, default=datetime.utcnow
)
class PlayerTournament(Base):
__tablename__ = "hockey_player_tournaments"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
player_id: Mapped[int] = mapped_column(
Integer, ForeignKey("hockey_players.id", ondelete="CASCADE"), nullable=False
)
tournament_external_id: Mapped[str] = mapped_column(String(64), nullable=False)
club_external_id: Mapped[str] = mapped_column(String(64), nullable=False, default="")
team_entry_external_id: Mapped[str] = mapped_column(String(64), nullable=False, default="")
team_name_ru: Mapped[str] = mapped_column(String(255), nullable=False, default="")
team_name_en: Mapped[str] = mapped_column(String(255), nullable=False, default="")
jersey_number: Mapped[str] = mapped_column(String(16), nullable=False, default="")
source: Mapped[str] = mapped_column(String(32), nullable=False, default="stat2tv")
active: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
synced_at: Mapped[datetime] = mapped_column(
DateTime, nullable=False, default=datetime.utcnow
)
__table_args__ = (
UniqueConstraint(
"player_id", "tournament_external_id", "team_entry_external_id",
name="uq_hockey_player_tournament_entry",
),
Index("ix_hockey_player_tournaments_tournament", "tournament_external_id"),
)
class PlayerSeasonStatistic(Base):
"""Normalized season totals from players-{tournament}.xml."""
__tablename__ = "hockey_player_season_statistics"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
player_id: Mapped[int] = mapped_column(Integer, ForeignKey("hockey_players.id", ondelete="CASCADE"), nullable=False)
player_external_id: Mapped[str] = mapped_column(String(64), nullable=False)
tournament_external_id: Mapped[str] = mapped_column(String(64), nullable=False)
team_entry_external_id: Mapped[str] = mapped_column(String(96), nullable=False, default="")
club_external_id: Mapped[str] = mapped_column(String(96), nullable=False, default="")
team_name_ru: Mapped[str] = mapped_column(Text, nullable=False, default="")
team_name_en: Mapped[str] = mapped_column(Text, nullable=False, default="")
jersey_number: Mapped[str] = mapped_column(String(16), nullable=False, default="")
role: Mapped[str] = mapped_column(String(32), nullable=False, default="")
games: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
goals: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
assists: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
points: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
penalty_minutes: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
plus_minus: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
plus: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
minus: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
power_play_goals: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
short_handed_goals: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
game_winning_goals: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
even_strength_goals: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
overtime_goals: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
shots: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
shot_pct: Mapped[float] = mapped_column(Float, nullable=False, default=0.0)
faceoffs: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
faceoffs_won: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
faceoff_pct: Mapped[float] = mapped_column(Float, nullable=False, default=0.0)
takeaways: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
interceptions: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
hits: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
blocked_shots: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
average_time_on_ice: Mapped[str] = mapped_column(String(64), nullable=False, default="")
average_shifts: Mapped[float] = mapped_column(Float, nullable=False, default=0.0)
average_shots: Mapped[float] = mapped_column(Float, nullable=False, default=0.0)
average_time_on_attack: Mapped[str] = mapped_column(String(64), nullable=False, default="")
average_even_strength_time: Mapped[str] = mapped_column(String(64), nullable=False, default="")
average_power_play_time: Mapped[str] = mapped_column(String(64), nullable=False, default="")
average_short_handed_time: Mapped[str] = mapped_column(String(64), nullable=False, default="")
wins: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
losses: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
draws: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
overtime_losses: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
shots_against: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
goals_against: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
saves: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
save_pct: Mapped[float] = mapped_column(Float, nullable=False, default=0.0)
goals_against_average: Mapped[float] = mapped_column(Float, nullable=False, default=0.0)
shutouts: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
time_on_ice: Mapped[str] = mapped_column(String(64), nullable=False, default="")
shootouts: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
shootout_goals_against: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
shootout_save_pct: Mapped[float] = mapped_column(Float, nullable=False, default=0.0)
raw_payload: Mapped[str] = mapped_column(Text, nullable=False, default="")
synced_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=datetime.utcnow)
__table_args__ = (
UniqueConstraint("player_id", "tournament_external_id", "team_entry_external_id", name="uq_hockey_player_season_stat"),
Index("ix_hockey_player_season_stats_tournament", "tournament_external_id"),
Index("ix_hockey_player_season_stats_player", "player_external_id"),
)
class Coach(Base):
__tablename__ = "hockey_coaches"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
external_id: Mapped[str] = mapped_column(String(64), nullable=False, unique=True)
first_name_ru: Mapped[str] = mapped_column(String(255), nullable=False, default="")
first_name_en: Mapped[str] = mapped_column(String(255), nullable=False, default="")
last_name_ru: Mapped[str] = mapped_column(String(255), nullable=False, default="")
last_name_en: Mapped[str] = mapped_column(String(255), nullable=False, default="")
full_name_ru: Mapped[str] = mapped_column(Text, nullable=False, default="")
full_name_en: Mapped[str] = mapped_column(Text, nullable=False, default="")
birth_date: Mapped[Optional[date]] = mapped_column(Date, nullable=True)
country_external_id: Mapped[str] = mapped_column(String(64), nullable=False, default="")
country_code: Mapped[str] = mapped_column(String(8), nullable=False, default="")
photo_url: Mapped[str] = mapped_column(Text, nullable=False, default="")
active: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
raw_payload: Mapped[str] = mapped_column(Text, nullable=False, default="")
synced_at: Mapped[datetime] = mapped_column(
DateTime, nullable=False, default=datetime.utcnow
)
class CoachTournament(Base):
__tablename__ = "hockey_coach_tournaments"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
coach_id: Mapped[int] = mapped_column(
Integer, ForeignKey("hockey_coaches.id", ondelete="CASCADE"), nullable=False
)
tournament_external_id: Mapped[str] = mapped_column(String(64), nullable=False)
team_external_id: Mapped[str] = mapped_column(String(64), nullable=False, default="")
team_name_ru: Mapped[str] = mapped_column(String(255), nullable=False, default="")
team_name_en: Mapped[str] = mapped_column(String(255), nullable=False, default="")
role_ru: Mapped[str] = mapped_column(String(128), nullable=False, default="Главный тренер")
role_en: Mapped[str] = mapped_column(String(128), nullable=False, default="Head coach")
source: Mapped[str] = mapped_column(String(32), nullable=False, default="stat2tv")
active: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
synced_at: Mapped[datetime] = mapped_column(
DateTime, nullable=False, default=datetime.utcnow
)
__table_args__ = (
UniqueConstraint(
"coach_id", "tournament_external_id", "team_external_id",
name="uq_hockey_coach_tournament_team",
),
Index("ix_hockey_coach_tournaments_tournament", "tournament_external_id"),
)
class Game(Base):
__tablename__ = "hockey_games"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
external_id: Mapped[str] = mapped_column(String(96), nullable=False, unique=True)
tournament_external_id: Mapped[str] = mapped_column(
String(64), ForeignKey("hockey_tournaments.external_id"), nullable=False
)
tournament_type: Mapped[str] = mapped_column(String(32), nullable=False, default="")
game_number: Mapped[str] = mapped_column(String(64), nullable=False, default="")
round_number: Mapped[str] = mapped_column(String(64), nullable=False, default="")
round_name_ru: Mapped[str] = mapped_column(Text, nullable=False, default="")
round_name_en: Mapped[str] = mapped_column(Text, nullable=False, default="")
day_of_week: Mapped[str] = mapped_column(String(16), nullable=False, default="")
changes: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
game_date: Mapped[Optional[date]] = mapped_column(Date, nullable=True)
start_time: Mapped[str] = mapped_column(String(32), nullable=False, default="")
start_datetime_raw: Mapped[str] = mapped_column(String(128), nullable=False, default="")
status: Mapped[str] = mapped_column(String(64), nullable=False, default="scheduled")
approved: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
arena_external_id: Mapped[str] = mapped_column(String(96), nullable=False, default="")
arena_ru: Mapped[str] = mapped_column(Text, nullable=False, default="")
arena_en: Mapped[str] = mapped_column(Text, nullable=False, default="")
arena_city_ru: Mapped[str] = mapped_column(Text, nullable=False, default="")
arena_city_en: Mapped[str] = mapped_column(Text, nullable=False, default="")
# teama / teamb are stable club ids; homeId / visitorId identify
# the tournament-specific team entry.
home_team_external_id: Mapped[str] = mapped_column(String(96), nullable=False, default="")
away_team_external_id: Mapped[str] = mapped_column(String(96), nullable=False, default="")
home_entry_external_id: Mapped[str] = mapped_column(String(96), nullable=False, default="")
away_entry_external_id: Mapped[str] = mapped_column(String(96), nullable=False, default="")
home_team_name_ru: Mapped[str] = mapped_column(Text, nullable=False, default="")
home_team_name_en: Mapped[str] = mapped_column(Text, nullable=False, default="")
home_team_short_ru: Mapped[str] = mapped_column(String(64), nullable=False, default="")
home_team_short_en: Mapped[str] = mapped_column(String(64), nullable=False, default="")
away_team_name_ru: Mapped[str] = mapped_column(Text, nullable=False, default="")
away_team_name_en: Mapped[str] = mapped_column(Text, nullable=False, default="")
away_team_short_ru: Mapped[str] = mapped_column(String(64), nullable=False, default="")
away_team_short_en: Mapped[str] = mapped_column(String(64), nullable=False, default="")
home_team_city_ru: Mapped[str] = mapped_column(Text, nullable=False, default="")
home_team_city_en: Mapped[str] = mapped_column(Text, nullable=False, default="")
away_team_city_ru: Mapped[str] = mapped_column(Text, nullable=False, default="")
away_team_city_en: Mapped[str] = mapped_column(Text, nullable=False, default="")
home_score: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
away_score: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
score_raw: Mapped[str] = mapped_column(String(64), nullable=False, default="")
overtime_type: Mapped[str] = mapped_column(String(32), nullable=False, default="")
period_1_score: Mapped[str] = mapped_column(String(32), nullable=False, default="")
period_2_score: Mapped[str] = mapped_column(String(32), nullable=False, default="")
period_3_score: Mapped[str] = mapped_column(String(32), nullable=False, default="")
period_4_score: Mapped[str] = mapped_column(String(32), nullable=False, default="")
series_score: Mapped[str] = mapped_column(String(32), nullable=False, default="")
attendance: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
temporary_value: Mapped[str] = mapped_column(Text, nullable=False, default="")
period_scores: Mapped[str] = mapped_column(Text, nullable=False, default="")
details_source_endpoint: Mapped[str] = mapped_column(
Text, nullable=False, default=""
)
details_raw_payload_ru: Mapped[str] = mapped_column(
Text, nullable=False, default=""
)
details_raw_payload_en: Mapped[str] = mapped_column(
Text, nullable=False, default=""
)
details_synced_at: Mapped[Optional[datetime]] = mapped_column(
DateTime, nullable=True
)
shots_resource_available: Mapped[bool] = mapped_column(
Boolean, nullable=False, default=False
)
shot_attempts_home: Mapped[int] = mapped_column(
Integer, nullable=False, default=0
)
shot_attempts_away: Mapped[int] = mapped_column(
Integer, nullable=False, default=0
)
shots_source_endpoint: Mapped[str] = mapped_column(
Text, nullable=False, default=""
)
shots_raw_payload: Mapped[str] = mapped_column(
Text, nullable=False, default=""
)
shots_synced_at: Mapped[Optional[datetime]] = mapped_column(
DateTime, nullable=True
)
source_endpoint: Mapped[str] = mapped_column(Text, nullable=False, default="")
raw_payload: Mapped[str] = mapped_column(Text, nullable=False, default="")
synced_at: Mapped[datetime] = mapped_column(
DateTime, nullable=False, default=datetime.utcnow
)
__table_args__ = (
Index("ix_hockey_games_date", "game_date"),
Index("ix_hockey_games_tournament", "tournament_external_id"),
Index("ix_hockey_games_tournament_date", "tournament_external_id", "game_date"),
Index("ix_hockey_games_status", "status"),
)
class GameTeamStatistic(Base):
"""Normalized team statistics for a game total and each period."""
__tablename__ = "hockey_game_team_statistics"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
game_external_id: Mapped[str] = mapped_column(String(96), ForeignKey("hockey_games.external_id", ondelete="CASCADE"), nullable=False)
tournament_external_id: Mapped[str] = mapped_column(String(64), nullable=False)
team_external_id: Mapped[str] = mapped_column(String(96), nullable=False, default="")
side: Mapped[str] = mapped_column(String(16), nullable=False)
segment_key: Mapped[str] = mapped_column(String(32), nullable=False, default="total")
period_number: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
goals: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
shots: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
shot_attempts: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
faceoffs: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
faceoffs_won: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
faceoffs_won_pct: Mapped[float] = mapped_column(Float, nullable=False, default=0.0)
hits: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
blocked_shots: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
takeaways: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
giveaways: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
interceptions: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
penalty_minutes: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
penalties: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
time_on_attack: Mapped[str] = mapped_column(String(64), nullable=False, default="")
even_strength_time: Mapped[str] = mapped_column(String(64), nullable=False, default="")
power_play_time: Mapped[str] = mapped_column(String(64), nullable=False, default="")
short_handed_time: Mapped[str] = mapped_column(String(64), nullable=False, default="")
empty_net_time: Mapped[str] = mapped_column(String(64), nullable=False, default="")
raw_payload: Mapped[str] = mapped_column(Text, nullable=False, default="")
synced_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=datetime.utcnow)
__table_args__ = (
UniqueConstraint("game_external_id", "side", "segment_key", name="uq_hockey_game_team_stat_segment"),
Index("ix_hockey_game_team_stats_game", "game_external_id"),
Index("ix_hockey_game_team_stats_tournament", "tournament_external_id"),
)
class GameRoster(Base):
__tablename__ = "hockey_game_rosters"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
game_external_id: Mapped[str] = mapped_column(
String(64), ForeignKey("hockey_games.external_id"), nullable=False
)
team_external_id: Mapped[str] = mapped_column(String(64), nullable=False)
player_external_id: Mapped[str] = mapped_column(String(64), nullable=False)
side: Mapped[str] = mapped_column(String(16), nullable=False)
number: Mapped[str] = mapped_column(String(16), nullable=False, default="")
role: Mapped[str] = mapped_column(String(64), nullable=False, default="")
position_ru: Mapped[str] = mapped_column(String(64), nullable=False, default="")
position_en: Mapped[str] = mapped_column(String(64), nullable=False, default="")
line_number: Mapped[str] = mapped_column(String(16), nullable=False, default="")
captain_role: Mapped[str] = mapped_column(String(32), nullable=False, default="")
lineup_status: Mapped[str] = mapped_column(String(64), nullable=False, default="")
raw_payload: Mapped[str] = mapped_column(Text, nullable=False, default="")
synced_at: Mapped[datetime] = mapped_column(
DateTime, nullable=False, default=datetime.utcnow
)
__table_args__ = (
UniqueConstraint(
"game_external_id",
"team_external_id",
"player_external_id",
name="uq_hockey_roster_game_team_player",
),
)
class Referee(Base):
__tablename__ = "hockey_referees"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
external_id: Mapped[str] = mapped_column(String(64), nullable=False, unique=True)
first_name_ru: Mapped[str] = mapped_column(String(255), nullable=False, default="")
first_name_en: Mapped[str] = mapped_column(String(255), nullable=False, default="")
last_name_ru: Mapped[str] = mapped_column(String(255), nullable=False, default="")
last_name_en: Mapped[str] = mapped_column(String(255), nullable=False, default="")
full_name_ru: Mapped[str] = mapped_column(Text, nullable=False, default="")
full_name_en: Mapped[str] = mapped_column(Text, nullable=False, default="")
position_code: Mapped[str] = mapped_column(String(16), nullable=False, default="")
number: Mapped[str] = mapped_column(String(16), nullable=False, default="")
active: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
birth_date: Mapped[Optional[date]] = mapped_column(Date, nullable=True)
country_ru: Mapped[str] = mapped_column(String(255), nullable=False, default="")
country_en: Mapped[str] = mapped_column(String(255), nullable=False, default="")
country_code: Mapped[str] = mapped_column(String(8), nullable=False, default="")
town_ru: Mapped[str] = mapped_column(String(255), nullable=False, default="")
town_en: Mapped[str] = mapped_column(String(255), nullable=False, default="")
raw_payload: Mapped[str] = mapped_column(Text, nullable=False, default="")
synced_at: Mapped[datetime] = mapped_column(
DateTime, nullable=False, default=datetime.utcnow
)
class RefereeTournament(Base):
__tablename__ = "hockey_referee_tournaments"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
referee_id: Mapped[int] = mapped_column(
Integer, ForeignKey("hockey_referees.id", ondelete="CASCADE"), nullable=False
)
tournament_external_id: Mapped[str] = mapped_column(String(64), nullable=False)
source: Mapped[str] = mapped_column(String(32), nullable=False, default="stat2tv")
active: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
synced_at: Mapped[datetime] = mapped_column(
DateTime, nullable=False, default=datetime.utcnow
)
__table_args__ = (
UniqueConstraint(
"referee_id", "tournament_external_id",
name="uq_hockey_referee_tournament",
),
Index("ix_hockey_referee_tournaments_tournament", "tournament_external_id"),
)
class GameOfficial(Base):
__tablename__ = "hockey_game_officials"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
game_external_id: Mapped[str] = mapped_column(
String(64), ForeignKey("hockey_games.external_id"), nullable=False
)
referee_external_id: Mapped[str] = mapped_column(String(64), nullable=False)
role: Mapped[str] = mapped_column(String(32), nullable=False)
slot: Mapped[int] = mapped_column(Integer, nullable=False, default=1)
number: Mapped[str] = mapped_column(String(16), nullable=False, default="")
raw_payload: Mapped[str] = mapped_column(Text, nullable=False, default="")
synced_at: Mapped[datetime] = mapped_column(
DateTime, nullable=False, default=datetime.utcnow
)
__table_args__ = (
UniqueConstraint(
"game_external_id", "role", "slot",
name="uq_hockey_game_official_role_slot",
),
Index("ix_hockey_game_officials_game", "game_external_id"),
)
class GameEvent(Base):
__tablename__ = "hockey_game_events"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
external_id: Mapped[str] = mapped_column(String(96), nullable=False)
game_external_id: Mapped[str] = mapped_column(
String(64), ForeignKey("hockey_games.external_id"), nullable=False
)
event_type: Mapped[str] = mapped_column(String(64), nullable=False)
period: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
event_time: Mapped[str] = mapped_column(String(32), nullable=False, default="")
team_external_id: Mapped[str] = mapped_column(String(64), nullable=False, default="")
player_external_id: Mapped[str] = mapped_column(String(64), nullable=False, default="")
title_ru: Mapped[str] = mapped_column(Text, nullable=False, default="")
title_en: Mapped[str] = mapped_column(Text, nullable=False, default="")
raw_payload: Mapped[str] = mapped_column(Text, nullable=False, default="")
synced_at: Mapped[datetime] = mapped_column(
DateTime, nullable=False, default=datetime.utcnow
)
__table_args__ = (
UniqueConstraint(
"game_external_id", "external_id", name="uq_hockey_event_game_external"
),
Index("ix_hockey_events_game_period", "game_external_id", "period"),
)
class GameControlState(Base):
"""Operator-controlled state that is not supplied by Stat2TV.
This table stores the currently selected period and the configuration of
the manual shootout panel. There is exactly one row per game.
"""
__tablename__ = "hockey_game_control_states"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
game_external_id: Mapped[str] = mapped_column(
String(64), ForeignKey("hockey_games.external_id", ondelete="CASCADE"),
nullable=False, unique=True,
)
current_period: Mapped[str] = mapped_column(
String(16), nullable=False, default="1"
)
shootout_started: Mapped[bool] = mapped_column(
Boolean, nullable=False, default=False
)
shootout_initial_attempts: Mapped[int] = mapped_column(
Integer, nullable=False, default=3
)
shootout_extra_rounds: Mapped[int] = mapped_column(
Integer, nullable=False, default=0
)
# Operator timers are stored as one JSON document per game. This keeps the
# main clock and all active penalty clocks together and makes future timer
# fields additive without another database migration.
timer_state_json: Mapped[str] = mapped_column(
Text, nullable=False, default=""
)
timer_state_updated_at: Mapped[Optional[datetime]] = mapped_column(
DateTime, nullable=True
)
# Match-scoped operator flags used by runtime controls and shortcut
# conditions (delayed penalty, empty net, prematch toggles, etc.).
# Stored separately from timer_state_json so timer saves cannot wipe them.
runtime_flags_json: Mapped[str] = mapped_column(
Text, nullable=False, default="{}"
)
# Match-scoped string values used by parameterised operator controls.
# Example: panel.stat_period.value = "2". Kept separate from boolean flags.
runtime_values_json: Mapped[str] = mapped_column(
Text, nullable=False, default="{}"
)
updated_by: Mapped[str] = mapped_column(String(128), nullable=False, default="")
created_at: Mapped[datetime] = mapped_column(
DateTime, nullable=False, default=datetime.utcnow
)
updated_at: Mapped[datetime] = mapped_column(
DateTime, nullable=False, default=datetime.utcnow
)
__table_args__ = (
Index("ix_hockey_game_control_game", "game_external_id"),
)
class ShootoutAttempt(Base):
"""One manually registered shootout attempt."""
__tablename__ = "hockey_shootout_attempts"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
game_external_id: Mapped[str] = mapped_column(
String(64), ForeignKey("hockey_games.external_id", ondelete="CASCADE"),
nullable=False,
)
sequence_number: Mapped[int] = mapped_column(Integer, nullable=False, default=1)
team_attempt_number: Mapped[int] = mapped_column(Integer, nullable=False, default=1)
series_number: Mapped[int] = mapped_column(Integer, nullable=False, default=1)
side: Mapped[str] = mapped_column(String(16), nullable=False)
team_external_id: Mapped[str] = mapped_column(String(64), nullable=False, default="")
player_external_id: Mapped[str] = mapped_column(String(64), nullable=False, default="")
player_number: Mapped[str] = mapped_column(String(16), nullable=False, default="")
player_name: Mapped[str] = mapped_column(Text, nullable=False, default="")
scored: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
created_by: Mapped[str] = mapped_column(String(128), nullable=False, default="")
created_at: Mapped[datetime] = mapped_column(
DateTime, nullable=False, default=datetime.utcnow
)
__table_args__ = (
UniqueConstraint(
"game_external_id", "sequence_number",
name="uq_hockey_shootout_game_sequence",
),
Index("ix_hockey_shootout_game_side", "game_external_id", "side"),
)
class OperatorSession(Base):
__tablename__ = "hockey_operator_sessions"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
session_token: Mapped[str] = mapped_column(String(128), nullable=False, unique=True)
wfl_user_id: Mapped[str] = mapped_column(String(128), nullable=False)
login_snapshot: Mapped[str] = mapped_column(String(100), nullable=False, default="")
display_name_snapshot: Mapped[str] = mapped_column(
String(200), nullable=False, default=""
)
tournament_external_id: Mapped[str] = mapped_column(String(64), nullable=False)
game_external_id: Mapped[str] = mapped_column(String(64), nullable=False, default="")
display_language: Mapped[str] = mapped_column(String(8), nullable=False, default="ru")
vmix_language: Mapped[str] = mapped_column(String(16), nullable=False, default="ru")
vmix_device_uuid: Mapped[str] = mapped_column(String(128), nullable=False, default="")
status: Mapped[str] = mapped_column(String(32), nullable=False, default="active")
created_at: Mapped[datetime] = mapped_column(
DateTime, nullable=False, default=datetime.utcnow
)
last_activity_at: Mapped[datetime] = mapped_column(
DateTime, nullable=False, default=datetime.utcnow
)
closed_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True)
__table_args__ = (
Index("ix_hockey_sessions_user_status", "wfl_user_id", "status"),
Index("ix_hockey_sessions_game_status", "game_external_id", "status"),
)
class VmixDevice(Base):
__tablename__ = "hockey_vmix_devices"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
device_uuid: Mapped[str] = mapped_column(String(128), nullable=False, unique=True)
device_secret_hash: Mapped[str] = mapped_column(String(64), nullable=False)
name: Mapped[str] = mapped_column(String(200), nullable=False, default="")
hostname: Mapped[str] = mapped_column(String(200), nullable=False, default="")
agent_version: Mapped[str] = mapped_column(String(64), nullable=False, default="")
wfl_user_id: Mapped[Optional[str]] = mapped_column(String(128), nullable=True)
login_snapshot: Mapped[str] = mapped_column(String(100), nullable=False, default="")
is_active_for_account: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
paired_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True)
first_seen_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=datetime.utcnow)
last_seen_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=datetime.utcnow)
last_ip: Mapped[str] = mapped_column(String(128), nullable=False, default="")
vmix_connected: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
vmix_version: Mapped[str] = mapped_column(String(64), nullable=False, default="")
vmix_url: Mapped[str] = mapped_column(String(500), nullable=False, default="")
current_match_external_id: Mapped[str] = mapped_column(String(64), nullable=False, default="")
current_assignment_key: Mapped[str] = mapped_column(String(128), nullable=False, default="")
last_error: Mapped[str] = mapped_column(Text, nullable=False, default="")
project_fingerprint: Mapped[str] = mapped_column(String(64), nullable=False, default="")
project_inventory_json: Mapped[str] = mapped_column(Text, nullable=False, default="{}")
project_input_count: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
project_field_count: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
project_scanned_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True)
__table_args__ = (
Index("ix_hockey_vmix_devices_user", "wfl_user_id"),
Index("ix_hockey_vmix_devices_last_seen", "last_seen_at"),
)
class VmixAssignment(Base):
__tablename__ = "hockey_vmix_assignments"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
assignment_key: Mapped[str] = mapped_column(String(128), nullable=False, unique=True)
device_id: Mapped[int] = mapped_column(
ForeignKey("hockey_vmix_devices.id", ondelete="CASCADE"), nullable=False
)
wfl_user_id: Mapped[str] = mapped_column(String(128), nullable=False)
operator_session_token: Mapped[str] = mapped_column(String(128), nullable=False, default="")
tournament_external_id: Mapped[str] = mapped_column(String(64), nullable=False, default="")
game_external_id: Mapped[str] = mapped_column(String(64), nullable=False)
active: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=datetime.utcnow)
last_activity_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=datetime.utcnow)
ended_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True)
__table_args__ = (
Index("ix_hockey_vmix_assignments_device_active", "device_id", "active"),
Index("ix_hockey_vmix_assignments_user_game", "wfl_user_id", "game_external_id"),
)
class VmixMappingProfile(Base):
__tablename__ = "hockey_vmix_mapping_profiles"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
name: Mapped[str] = mapped_column(String(200), nullable=False, unique=True)
description: Mapped[str] = mapped_column(Text, nullable=False, default="")
project_fingerprint: Mapped[str] = mapped_column(String(64), nullable=False, default="", index=True)
inventory_json: Mapped[str] = mapped_column(Text, nullable=False, default="{}")
version: Mapped[int] = mapped_column(Integer, nullable=False, default=1)
active: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
created_by: Mapped[str] = mapped_column(String(128), nullable=False, default="")
updated_by: Mapped[str] = mapped_column(String(128), nullable=False, default="")
created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=datetime.utcnow)
updated_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=datetime.utcnow)
__table_args__ = (
Index("ix_hockey_vmix_mapping_profiles_fingerprint_active", "project_fingerprint", "active"),
)
class VmixMappingField(Base):
__tablename__ = "hockey_vmix_mapping_fields"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
profile_id: Mapped[int] = mapped_column(
ForeignKey("hockey_vmix_mapping_profiles.id", ondelete="CASCADE"), nullable=False
)
graphic: Mapped[str] = mapped_column(String(100), nullable=False, default="")
data_key: Mapped[str] = mapped_column(String(200), nullable=False)
vmix_input_key: Mapped[str] = mapped_column(String(128), nullable=False, default="")
vmix_input_number: Mapped[str] = mapped_column(String(32), nullable=False, default="")
vmix_input_title: Mapped[str] = mapped_column(String(300), nullable=False, default="")
vmix_field: Mapped[str] = mapped_column(String(300), nullable=False)
field_type: Mapped[str] = mapped_column(String(32), nullable=False, default="text")
# Optional conditional action for the target (text colour / deterministic visibility).
# JSON keeps the rule engine extensible without creating a migration for every new operator.
rule_json: Mapped[str] = mapped_column(Text, nullable=False, default="{}")
enabled: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
sort_order: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
__table_args__ = (
Index("ix_hockey_vmix_mapping_fields_profile", "profile_id", "sort_order"),
UniqueConstraint("profile_id", "graphic", "data_key", "vmix_input_key", "vmix_field", name="uq_hockey_vmix_mapping_target"),
)
class MappingContextVariable(Base):
__tablename__ = "hockey_mapping_context_variables"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
key: Mapped[str] = mapped_column(String(128), nullable=False, unique=True)
label: Mapped[str] = mapped_column(String(200), nullable=False)
category: Mapped[str] = mapped_column(String(100), nullable=False, default="Контекст")
description: Mapped[str] = mapped_column(Text, nullable=False, default="")
value_type: Mapped[str] = mapped_column(String(32), nullable=False, default="text")
entity_type: Mapped[str] = mapped_column(String(32), nullable=False, default="")
scope: Mapped[str] = mapped_column(String(32), nullable=False, default="match")
source_type: Mapped[str] = mapped_column(String(32), nullable=False, default="manual")
default_value: Mapped[str] = mapped_column(Text, nullable=False, default="")
is_system: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
enabled: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
sort_order: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
created_by: Mapped[str] = mapped_column(String(128), nullable=False, default="")
updated_by: Mapped[str] = mapped_column(String(128), nullable=False, default="")
created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=datetime.utcnow)
updated_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=datetime.utcnow)
__table_args__ = (
Index("ix_hockey_mapping_context_variable_group", "category", "sort_order"),
)
class MappingContextValue(Base):
__tablename__ = "hockey_mapping_context_values"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
variable_id: Mapped[int] = mapped_column(
ForeignKey("hockey_mapping_context_variables.id", ondelete="CASCADE"), nullable=False
)
scope_key: Mapped[str] = mapped_column(String(500), nullable=False)
wfl_user_id: Mapped[str] = mapped_column(String(128), nullable=False, default="")
operator_session_token: Mapped[str] = mapped_column(String(128), nullable=False, default="")
game_external_id: Mapped[str] = mapped_column(String(96), nullable=False, default="")
value_text: Mapped[str] = mapped_column(Text, nullable=False, default="")
updated_by: Mapped[str] = mapped_column(String(128), nullable=False, default="")
updated_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=datetime.utcnow)
__table_args__ = (
UniqueConstraint("variable_id", "scope_key", name="uq_hockey_mapping_context_value_scope"),
Index("ix_hockey_mapping_context_values_user_game", "wfl_user_id", "game_external_id"),
)
class MappingSqlDataSource(Base):
__tablename__ = "hockey_mapping_sql_sources"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
code: Mapped[str] = mapped_column(String(100), nullable=False, unique=True)
name: Mapped[str] = mapped_column(String(200), nullable=False)
category: Mapped[str] = mapped_column(String(100), nullable=False, default="Данные")
description: Mapped[str] = mapped_column(Text, nullable=False, default="")
sql_text: Mapped[str] = mapped_column(Text, nullable=False)
field_metadata_json: Mapped[str] = mapped_column(Text, nullable=False, default="{}")
enabled: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
auto_refresh_enabled: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
refresh_interval_ms: Mapped[int] = mapped_column(Integer, nullable=False, default=1000)
sort_order: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
created_by: Mapped[str] = mapped_column(String(128), nullable=False, default="")
updated_by: Mapped[str] = mapped_column(String(128), nullable=False, default="")
created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=datetime.utcnow)
updated_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=datetime.utcnow)
__table_args__ = (
Index("ix_hockey_mapping_sql_sources_enabled", "enabled", "sort_order"),
)
class HockeyWebSession(Base):
__tablename__ = "hockey_web_sessions"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
token_hash: Mapped[str] = mapped_column(String(64), nullable=False, unique=True)
wfl_user_id: Mapped[str] = mapped_column(String(128), nullable=False)
login_snapshot: Mapped[str] = mapped_column(String(100), nullable=False, default="")
role_snapshot: Mapped[str] = mapped_column(String(32), nullable=False, default="operator")
created_at: Mapped[datetime] = mapped_column(
DateTime, nullable=False, default=datetime.utcnow
)
last_activity_at: Mapped[datetime] = mapped_column(
DateTime, nullable=False, default=datetime.utcnow
)
expires_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
revoked_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True)
__table_args__ = (
Index("ix_hockey_web_sessions_user", "wfl_user_id"),
Index("ix_hockey_web_sessions_expires", "expires_at"),
)
class UserPreference(Base):
__tablename__ = "hockey_user_preferences"
wfl_user_id: Mapped[str] = mapped_column("user_id", String(128), primary_key=True)
display_language: Mapped[str] = mapped_column(String(8), nullable=False, default="ru")
vmix_language: Mapped[str] = mapped_column(String(16), nullable=False, default="ru")
# Per-account runtime preference. Store the negative form so additive schema
# repair safely gives existing accounts FALSE (= events enabled).
triggers_disabled: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
selected_tournament_external_id: Mapped[str] = mapped_column(
String(64), nullable=False, default=""
)
updated_at: Mapped[datetime] = mapped_column(
DateTime, nullable=False, default=datetime.utcnow
)
class SyncRun(Base):
__tablename__ = "hockey_sync_runs"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
endpoint: Mapped[str] = mapped_column(Text, nullable=False)
status: Mapped[str] = mapped_column(String(32), nullable=False)
auth_mode: Mapped[str] = mapped_column(String(16), nullable=False, default="")
status_code: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
item_count: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
source: Mapped[str] = mapped_column(String(32), nullable=False, default="")
error: Mapped[str] = mapped_column(Text, nullable=False, default="")
snapshot_path: Mapped[str] = mapped_column(Text, nullable=False, default="")
started_at: Mapped[datetime] = mapped_column(
DateTime, nullable=False, default=datetime.utcnow
)
finished_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True)
class EndpointDiscovery(Base):
__tablename__ = "hockey_endpoint_discoveries"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
resource_type: Mapped[str] = mapped_column(String(32), nullable=False)
tournament_external_id: Mapped[str] = mapped_column(String(64), nullable=False, default="")
endpoint_template: Mapped[str] = mapped_column(Text, nullable=False, default="")
resolved_endpoint: Mapped[str] = mapped_column(Text, nullable=False, default="")
status: Mapped[str] = mapped_column(String(32), nullable=False)
status_code: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
root_tag: Mapped[str] = mapped_column(String(128), nullable=False, default="")
item_tag: Mapped[str] = mapped_column(String(128), nullable=False, default="")
item_count: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
diagnostics_json: Mapped[str] = mapped_column(Text, nullable=False, default="{}")
snapshot_path: Mapped[str] = mapped_column(Text, nullable=False, default="")
error: Mapped[str] = mapped_column(Text, nullable=False, default="")
created_at: Mapped[datetime] = mapped_column(
DateTime, nullable=False, default=datetime.utcnow
)
__table_args__ = (
Index(
"ix_hockey_endpoint_discovery_resource_tournament",
"resource_type",
"tournament_external_id",
),
)