# backend/app/models.py from datetime import datetime from typing import Optional from sqlalchemy import JSON, DateTime from sqlalchemy import Enum as SqlEnum from sqlalchemy import ForeignKey, Integer, String from sqlalchemy.orm import Mapped, mapped_column, relationship from .constants import MatchStatus, TournamentTypes, BracketTypes from .database import Base class Tournament(Base): __tablename__ = "tournaments" id: Mapped[str] = mapped_column(String(8), primary_key=True, index=True) name: Mapped[str] = mapped_column(String, nullable=False) code: Mapped[str] = mapped_column(String, nullable=True) timestamp: Mapped[datetime] = mapped_column(DateTime, nullable=False) duration: Mapped[int] = mapped_column(Integer, default=30) type: Mapped[TournamentTypes] = mapped_column(SqlEnum(TournamentTypes)) teams: Mapped[list["Team"]] = relationship( "Team", back_populates="tournament", cascade="all, delete-orphan" ) courts: Mapped[list["Court"]] = relationship( "Court", back_populates="tournament", cascade="all, delete-orphan" ) matches: Mapped[list["Match"]] = relationship( "Match", back_populates="tournament", cascade="all, delete-orphan" ) @property def team_count(self) -> int: return len(self.teams) @property def court_count(self) -> int: return len(self.courts) class Team(Base): __tablename__ = "teams" id: Mapped[int] = mapped_column(Integer, primary_key=True) name: Mapped[str] = mapped_column(String, nullable=False) tournament_id: Mapped[str] = mapped_column(ForeignKey("tournaments.id")) tournament: Mapped["Tournament"] = relationship(back_populates="teams") class Court(Base): __tablename__ = "courts" id: Mapped[int] = mapped_column(Integer, primary_key=True) name: Mapped[str] = mapped_column(String, nullable=False) tournament_id: Mapped[str] = mapped_column(ForeignKey("tournaments.id")) tournament: Mapped["Tournament"] = relationship(back_populates="courts") class Match(Base): __tablename__ = "matches" id: Mapped[str] = mapped_column(String, primary_key=True) tournament_id: Mapped[str] = mapped_column(ForeignKey("tournaments.id")) match_number: Mapped[int] = mapped_column(Integer) round_number: Mapped[int] = mapped_column(Integer, default=1) bracket_type: Mapped[BracketTypes] = mapped_column(SqlEnum(BracketTypes)) court_id: Mapped[Optional[int]] = mapped_column(ForeignKey("courts.id"), nullable=True) start_time: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True) status: Mapped[MatchStatus] = mapped_column(SqlEnum(MatchStatus), default=MatchStatus.SCHEDULED) p1_team_id: Mapped[Optional[int]] = mapped_column(ForeignKey("teams.id"), nullable=True) p2_team_id: Mapped[Optional[int]] = mapped_column(ForeignKey("teams.id"), nullable=True) ref_team_id: Mapped[Optional[int]] = mapped_column(ForeignKey("teams.id"), nullable=True) ref_label: Mapped[Optional[str]] = mapped_column(String, nullable=True) sets: Mapped[list[dict]] = mapped_column(JSON, default=list) winner_team_id: Mapped[Optional[int]] = mapped_column(ForeignKey("teams.id"), nullable=True) winner_next_match_id: Mapped[Optional[str]] = mapped_column(ForeignKey("matches.id"), nullable=True) winner_next_match_slot: Mapped[Optional[int]] = mapped_column(Integer, nullable=True) loser_next_match_id: Mapped[Optional[str]] = mapped_column(ForeignKey("matches.id"), nullable=True) loser_next_match_slot: Mapped[Optional[int]] = mapped_column(Integer, nullable=True) tournament: Mapped["Tournament"] = relationship(back_populates="matches") court: Mapped[Optional["Court"]] = relationship() p1_team: Mapped["Team"] = relationship("Team", foreign_keys=[p1_team_id]) p2_team: Mapped["Team"] = relationship("Team", foreign_keys=[p2_team_id]) ref_team: Mapped[Optional["Team"]] = relationship("Team", foreign_keys=[ref_team_id]) winner_next_match: Mapped["Match"] = relationship("Match", remote_side=[id], foreign_keys=[winner_next_match_id]) loser_next_match: Mapped["Match"] = relationship("Match", remote_side=[id], foreign_keys=[loser_next_match_id])