# 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, Boolean 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) # --- NEW: Dynamically check if the tournament has started --- @property def has_started(self) -> bool: return any(m.status == MatchStatus.FINISHED for m in self.matches) class Team(Base): __tablename__ = "teams" id: Mapped[int] = mapped_column(Integer, primary_key=True) name: Mapped[str] = mapped_column(String, nullable=False) dropped_out: Mapped[bool] = mapped_column(Boolean, default=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] )