Complete main functionallity of backend and frontend.
This commit is contained in:
@@ -1,12 +1,11 @@
|
||||
# backend/app/core/config.py
|
||||
import os
|
||||
import secrets
|
||||
from pwdlib import PasswordHash
|
||||
|
||||
DB_PATH = os.getenv("DB_PATH", "./tournaments.db")
|
||||
|
||||
# Security Config
|
||||
SECRET_KEY = os.getenv("SECRET_KEY", secrets.token_hex(32))
|
||||
SECRET_KEY = os.getenv("SECRET_KEY", "SUPER-SECRET-TOKEN")
|
||||
ALGORITHM = "HS256"
|
||||
ACCESS_TOKEN_EXPIRE_MINUTES = 60 * 24 # 24 hours
|
||||
|
||||
|
||||
+34
-22
@@ -8,7 +8,6 @@ from sqlalchemy.orm import Session
|
||||
from . import models
|
||||
from .constants import MatchStatus, TournamentTypes
|
||||
from .core.brackets import BracketGenerator
|
||||
from .core import structures
|
||||
|
||||
|
||||
def generate_bracket(db: Session, t: models.Tournament):
|
||||
@@ -21,18 +20,26 @@ def generate_bracket(db: Session, t: models.Tournament):
|
||||
abstract_matches = gen.generate(len(teams), double_elimination=is_double)
|
||||
id_map = {m.id: str(uuid4()) for m in abstract_matches}
|
||||
|
||||
def resolve_target(match_node: structures.Match | None):
|
||||
curr = match_node
|
||||
while curr and curr.is_bye:
|
||||
def resolve_target(match_node, is_winner_path):
|
||||
if is_winner_path:
|
||||
curr = match_node.next_win
|
||||
slot = getattr(match_node, "next_win_slot", None)
|
||||
else:
|
||||
curr = match_node.next_loss
|
||||
slot = getattr(match_node, "next_loss_slot", None)
|
||||
|
||||
while curr and getattr(curr, "is_bye", False):
|
||||
slot = getattr(curr, "next_win_slot", None)
|
||||
curr = curr.next_win
|
||||
return curr
|
||||
|
||||
return curr, slot
|
||||
|
||||
db_matches = []
|
||||
friendly_counter = 1
|
||||
|
||||
for m in abstract_matches:
|
||||
real_win = resolve_target(m.next_win)
|
||||
real_loss = resolve_target(m.next_loss)
|
||||
real_win, win_slot = resolve_target(m, True)
|
||||
real_loss, loss_slot = resolve_target(m, False)
|
||||
|
||||
initial_status = MatchStatus.SCHEDULED
|
||||
p1_id = None
|
||||
@@ -59,7 +66,9 @@ def generate_bracket(db: Session, t: models.Tournament):
|
||||
p1_team_id=p1_id,
|
||||
p2_team_id=p2_id,
|
||||
winner_next_match_id=id_map[real_win.id] if real_win else None,
|
||||
winner_next_match_slot=win_slot if real_win else None,
|
||||
loser_next_match_id=id_map[real_loss.id] if real_loss else None,
|
||||
loser_next_match_slot=loss_slot if real_loss else None,
|
||||
)
|
||||
db_matches.append(new_match)
|
||||
friendly_counter += 1
|
||||
@@ -172,12 +181,14 @@ def advance_winner(db: Session, match: models.Match, winner_id: int):
|
||||
|
||||
loser_id = match.p1_team_id if match.p1_team_id != winner_id else match.p2_team_id
|
||||
|
||||
def update_next_match(next_match: models.Match, team_id: int):
|
||||
if not next_match:
|
||||
def update_next_match(
|
||||
next_match: models.Match, team_id: int, target_slot: int | None
|
||||
):
|
||||
if not next_match or target_slot is None:
|
||||
return
|
||||
if not next_match.p1_team_id:
|
||||
if target_slot == 0:
|
||||
next_match.p1_team_id = team_id
|
||||
elif not next_match.p2_team_id:
|
||||
elif target_slot == 1:
|
||||
next_match.p2_team_id = team_id
|
||||
|
||||
if (
|
||||
@@ -189,9 +200,9 @@ def advance_winner(db: Session, match: models.Match, winner_id: int):
|
||||
|
||||
db.add(next_match)
|
||||
|
||||
update_next_match(match.winner_next_match, winner_id)
|
||||
update_next_match(match.winner_next_match, winner_id, match.winner_next_match_slot)
|
||||
if match.loser_next_match and loser_id:
|
||||
update_next_match(match.loser_next_match, loser_id)
|
||||
update_next_match(match.loser_next_match, loser_id, match.loser_next_match_slot)
|
||||
|
||||
db.commit()
|
||||
|
||||
@@ -203,8 +214,10 @@ def undo_advancement(db: Session, match: models.Match):
|
||||
winner_id = match.winner_team_id
|
||||
loser_id = match.p1_team_id if match.p1_team_id != winner_id else match.p2_team_id
|
||||
|
||||
def clear_from_next(next_match: models.Match, team_id: int):
|
||||
if not next_match:
|
||||
def clear_from_next(
|
||||
next_match: models.Match, team_id: int, target_slot: int | None
|
||||
):
|
||||
if not next_match or target_slot is None:
|
||||
return
|
||||
|
||||
if next_match.winner_team_id:
|
||||
@@ -212,17 +225,16 @@ def undo_advancement(db: Session, match: models.Match):
|
||||
next_match.winner_team_id = None
|
||||
next_match.sets = []
|
||||
|
||||
if next_match.p1_team_id == team_id:
|
||||
if target_slot == 0:
|
||||
next_match.p1_team_id = None
|
||||
elif next_match.p2_team_id == team_id:
|
||||
elif target_slot == 1:
|
||||
next_match.p2_team_id = None
|
||||
if next_match.p1_team_id and next_match.p2_team_id:
|
||||
next_match.status = MatchStatus.PENDING
|
||||
else:
|
||||
|
||||
if next_match.status == MatchStatus.PENDING:
|
||||
next_match.status = MatchStatus.SCHEDULED
|
||||
|
||||
db.add(next_match)
|
||||
|
||||
clear_from_next(match.winner_next_match, winner_id)
|
||||
clear_from_next(match.winner_next_match, winner_id, match.winner_next_match_slot)
|
||||
if match.loser_next_match and loser_id:
|
||||
clear_from_next(match.loser_next_match, loser_id)
|
||||
clear_from_next(match.loser_next_match, loser_id, match.loser_next_match_slot)
|
||||
|
||||
@@ -96,6 +96,18 @@ class Match(Base):
|
||||
ForeignKey("matches.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])
|
||||
|
||||
@@ -3,4 +3,4 @@ from fastapi import APIRouter
|
||||
|
||||
router = APIRouter(prefix="/tournaments", tags=["Tournaments"])
|
||||
|
||||
from . import tournaments, teams, courts, matches, report
|
||||
from . import tournaments, settings, teams, courts, matches, report
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
# backend/app/routes/tournaments/settings.py
|
||||
|
||||
|
||||
from fastapi import Depends, HTTPException
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from ... import crud, schemas
|
||||
from ...database import get_db
|
||||
from ...core.auth import get_current_user
|
||||
from . import router
|
||||
|
||||
|
||||
@router.get("/{id}/settings", response_model=schemas.TournamentSettingsResponse)
|
||||
def get_tournament(
|
||||
id: str, db: Session = Depends(get_db), user: str = Depends(get_current_user)
|
||||
):
|
||||
t = crud.get_tournament(db, id)
|
||||
if not t:
|
||||
raise HTTPException(404, "Tournament not found")
|
||||
return t
|
||||
@@ -10,7 +10,7 @@ from ...core.auth import get_current_user
|
||||
from . import router
|
||||
|
||||
|
||||
@router.post("", response_model=schemas.TournamentUpdateResponse)
|
||||
@router.post("", response_model=schemas.TournamentSettingsResponse)
|
||||
async def create_tournament(
|
||||
data: schemas.TournamentCreate,
|
||||
db: Session = Depends(get_db),
|
||||
@@ -34,7 +34,7 @@ def get_tournament(id: str, db: Session = Depends(get_db)):
|
||||
return t
|
||||
|
||||
|
||||
@router.patch("/{id}", response_model=schemas.TournamentUpdateResponse)
|
||||
@router.patch("/{id}", response_model=schemas.TournamentSettingsResponse)
|
||||
async def update_settings(
|
||||
id: str,
|
||||
data: schemas.TournamentUpdate,
|
||||
|
||||
@@ -51,7 +51,9 @@ class MatchOut(BaseModel):
|
||||
winner_team_id: int | None = None
|
||||
|
||||
winner_next_match_id: str | None = None
|
||||
winner_next_match_slot: int | None = None
|
||||
loser_next_match_id: str | None = None
|
||||
loser_next_match_slot: int | None = None
|
||||
|
||||
sets: list[SetScore] = []
|
||||
|
||||
@@ -80,6 +82,7 @@ class TournamentOut(BaseModel):
|
||||
id: str
|
||||
name: str
|
||||
timestamp: datetime
|
||||
duration: int
|
||||
type: TournamentTypes
|
||||
team_count: int
|
||||
court_count: int
|
||||
@@ -98,8 +101,10 @@ class TournamentDetail(BaseModel):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
|
||||
class TournamentUpdateResponse(TournamentDetail):
|
||||
class TournamentSettingsResponse(TournamentOut):
|
||||
code: str
|
||||
teams: list[TeamSchema]
|
||||
courts: list[CourtSchema]
|
||||
|
||||
|
||||
class Token(BaseModel):
|
||||
|
||||
Reference in New Issue
Block a user