# 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 ( TournamentTypes, MatchSourceType, MatchStatus, WinnerSide, BracketType, ) 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" ) # --- ADD THESE PROPERTIES --- @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"), primary_key=True ) # --- Structural Info --- bracket: Mapped[BracketType] = mapped_column( SqlEnum(BracketType, native_enum=False) ) round: Mapped[int] = mapped_column(Integer) number: Mapped[Optional[int]] = mapped_column(Integer, nullable=True) # --- Scheduling --- timestamp: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True) court_name: Mapped[Optional[str]] = mapped_column(String, nullable=True) # --- Player Info --- p1_name: Mapped[Optional[str]] = mapped_column(String, nullable=True) p2_name: Mapped[Optional[str]] = mapped_column(String, nullable=True) winner: Mapped[Optional[str]] = mapped_column(String, nullable=True) status: Mapped[MatchStatus] = mapped_column( SqlEnum(MatchStatus, native_enum=False), default=MatchStatus.PENDING ) sets: Mapped[list[dict]] = mapped_column(JSON, default=list) previous_match_p1_id: Mapped[Optional[str]] = mapped_column(String, nullable=True) previous_match_p2_id: Mapped[Optional[str]] = mapped_column(String, nullable=True) source_p1_type: Mapped[Optional[MatchSourceType]] = mapped_column( SqlEnum(MatchSourceType, native_enum=False), nullable=True ) source_p2_type: Mapped[Optional[MatchSourceType]] = mapped_column( SqlEnum(MatchSourceType, native_enum=False), nullable=True ) winner_next_match_id: Mapped[Optional[str]] = mapped_column(String, nullable=True) loser_next_match_id: Mapped[Optional[str]] = mapped_column(String, nullable=True) tournament: Mapped["Tournament"] = relationship(back_populates="matches") @property def winner_side(self) -> WinnerSide: if not self.winner: return WinnerSide.NONE if self.winner == self.p1_name: return WinnerSide.P1 if self.winner == self.p2_name: return WinnerSide.P2 return WinnerSide.NONE