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="") color_hex: Mapped[str] = mapped_column(String(9), 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", ), )