# backend/app/models.py from datetime import datetime from typing import Optional from sqlalchemy import JSON, DateTime, Table, Column 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 tournament_courts = Table( "tournament_courts", Base.metadata, Column( "tournament_id", String(8), ForeignKey("tournaments.id", ondelete="CASCADE"), primary_key=True, ), Column( "court_id", Integer, ForeignKey("courts.id", ondelete="CASCADE"), primary_key=True, ), ) class Court(Base): __tablename__ = "courts" id: Mapped[int] = mapped_column(Integer, primary_key=True) name: Mapped[str] = mapped_column(String, nullable=False, unique=True) tournaments: Mapped[list["Tournament"]] = relationship( "Tournament", secondary=tournament_courts, back_populates="courts" ) 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", secondary=tournament_courts, back_populates="tournaments" ) 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 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] )