Better bracket logic

This commit is contained in:
2026-02-14 01:04:54 +01:00 Verified
parent 87d3ea5ef0
commit 7b878b3abe
32 changed files with 1326 additions and 1348 deletions
+25 -67
View File
@@ -7,7 +7,7 @@ from sqlalchemy import Enum as SqlEnum
from sqlalchemy import ForeignKey, Integer, String
from sqlalchemy.orm import Mapped, mapped_column, relationship
from .constants import BracketType, MatchSourceType, MatchStatus, TournamentTypes
from .constants import MatchStatus, TournamentTypes, BracketTypes
from .database import Base
@@ -28,12 +28,6 @@ class Tournament(Base):
"Court", back_populates="tournament", cascade="all, delete-orphan"
)
# The Structure (Skeleton)
nodes: Mapped[list["BracketNode"]] = relationship(
"BracketNode", back_populates="tournament", cascade="all, delete-orphan"
)
# The Events (Real Games)
matches: Mapped[list["Match"]] = relationship(
"Match", back_populates="tournament", cascade="all, delete-orphan"
)
@@ -65,86 +59,50 @@ class Court(Base):
tournament: Mapped["Tournament"] = relationship(back_populates="courts")
class BracketNode(Base):
__tablename__ = "bracket_nodes"
id: Mapped[str] = mapped_column(String, primary_key=True)
tournament_id: Mapped[str] = mapped_column(ForeignKey("tournaments.id"))
bracket_type: Mapped[BracketType] = mapped_column(SqlEnum(BracketType))
round_number: Mapped[int] = mapped_column(Integer)
display_number: Mapped[int] = mapped_column(Integer)
# --- Planning / Scheduling ---
planned_court_id: Mapped[Optional[int]] = mapped_column(
ForeignKey("courts.id"), nullable=True
)
planned_start_time: Mapped[Optional[datetime]] = mapped_column(
DateTime, nullable=True
)
# --- Flow Logic ---
source_p1_node_id: Mapped[Optional[str]] = mapped_column(String, nullable=True)
source_p2_node_id: Mapped[Optional[str]] = mapped_column(String, nullable=True)
source_p1_type: Mapped[Optional[MatchSourceType]] = mapped_column(
SqlEnum(MatchSourceType), nullable=True
)
source_p2_type: Mapped[Optional[MatchSourceType]] = mapped_column(
SqlEnum(MatchSourceType), nullable=True
)
# --- Next Step ---
winner_next_node_id: Mapped[Optional[str]] = mapped_column(String, nullable=True)
loser_next_node_id: Mapped[Optional[str]] = mapped_column(String, nullable=True)
# --- Current State ---
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
)
# Link to actual match (only exists if active)
match: Mapped[Optional["Match"]] = relationship(
"Match", back_populates="node", uselist=False
)
tournament: Mapped["Tournament"] = relationship(back_populates="nodes")
court: Mapped[Optional["Court"]] = relationship()
# Helper to get team names quickly
p1_team: Mapped["Team"] = relationship("Team", foreign_keys=[p1_team_id])
p2_team: Mapped["Team"] = relationship("Team", foreign_keys=[p2_team_id])
class Match(Base):
__tablename__ = "matches"
id: Mapped[str] = mapped_column(String, primary_key=True)
tournament_id: Mapped[str] = mapped_column(ForeignKey("tournaments.id"))
node_id: Mapped[str] = mapped_column(ForeignKey("bracket_nodes.id"), unique=True)
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
)
timestamp: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True)
start_time: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True)
status: Mapped[MatchStatus] = mapped_column(
SqlEnum(MatchStatus), default=MatchStatus.PENDING
SqlEnum(MatchStatus), default=MatchStatus.SCHEDULED
)
p1_team_id: Mapped[int] = mapped_column(ForeignKey("teams.id"))
p2_team_id: Mapped[int] = mapped_column(ForeignKey("teams.id"))
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
)
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
)
loser_next_match_id: Mapped[Optional[str]] = mapped_column(
ForeignKey("matches.id"), nullable=True
)
node: Mapped["BracketNode"] = relationship(back_populates="match")
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])
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]
)