Files
brackets/backend/app/routes/tournaments/report.py
T

151 lines
4.0 KiB
Python

# backend/app/routes/tournaments/report.py
from typing import Optional
from fastapi import Depends, HTTPException, Query
from sqlalchemy.orm import Session
from sqlalchemy.orm.attributes import flag_modified
from ... import crud, logic, models, schemas
from ...constants import SUCCESS, MatchStatus
from ...core.auth import get_optional_user
from ...core.websocket_manager import send_ws_update
from ...database import get_db
from . import router
def _check_auth(t: models.Tournament, user: Optional[str], code: Optional[str]):
is_admin = user is not None
code_matches = code is not None and str(code).strip() == str(t.code).strip()
if not is_admin and not code_matches:
raise HTTPException(403, "Invalid tournament code or admin privileges required")
@router.post("/{id}/matches/{match_id}/score")
async def report_score(
id: str,
match_id: str,
report: schemas.ScoreReport,
db: Session = Depends(get_db),
user: Optional[str] = Depends(get_optional_user),
):
t = crud.get_tournament(db, id, lock=True)
if not t:
raise HTTPException(404, "Tournament not found")
_check_auth(t, user, report.code)
match = crud.get_match(db, match_id)
if not match:
raise HTTPException(404, "Match not found")
if not report.sets:
raise HTTPException(400, "No sets submitted")
if match.winner_team_id:
logic.undo_advancement(db, match)
_apply_score(match, report.sets)
flag_modified(match, "sets")
if match.winner_team_id:
logic.advance_winner(db, match, match.winner_team_id)
db.commit()
await send_ws_update(id)
return SUCCESS
@router.patch("/{id}/matches/{match_id}/score")
async def edit_score(
id: str,
match_id: str,
report: schemas.ScoreReport,
db: Session = Depends(get_db),
user: Optional[str] = Depends(get_optional_user),
):
t = crud.get_tournament(db, id, lock=True)
if not t:
raise HTTPException(404, "Tournament not found")
_check_auth(t, user, report.code)
match = crud.get_match(db, match_id)
if not match:
raise HTTPException(404, "Match not found")
logic.undo_advancement(db, match)
if report.sets:
_apply_score(match, report.sets)
flag_modified(match, "sets")
if match.winner_team_id:
logic.advance_winner(db, match, match.winner_team_id)
db.commit()
await send_ws_update(id)
return SUCCESS
@router.delete("/{id}/matches/{match_id}/score")
async def clear_score(
id: str,
match_id: str,
code: Optional[str] = Query(None),
db: Session = Depends(get_db),
user: Optional[str] = Depends(get_optional_user),
):
t = crud.get_tournament(db, id, lock=True)
if not t:
raise HTTPException(404, "Tournament not found")
_check_auth(t, user, code)
match = crud.get_match(db, match_id)
if not match:
raise HTTPException(404, "Match not found")
logic.undo_advancement(db, match)
match.winner_team_id = None
if match.p1_team_id and match.p2_team_id:
match.status = MatchStatus.PENDING
else:
match.status = MatchStatus.SCHEDULED
match.sets = []
flag_modified(match, "sets")
db.commit()
await send_ws_update(id)
return SUCCESS
def _apply_score(match: models.Match, sets: list[schemas.SetScore]):
"""
Calculates winner based on sets and updates the match object.
Does NOT commit to DB.
"""
p1_wins = sum(1 for s in sets if s.p1 > s.p2)
p2_wins = sum(1 for s in sets if s.p2 > s.p1)
if p1_wins > p2_wins:
match.winner_team_id = match.p1_team_id
elif p2_wins > p1_wins:
match.winner_team_id = match.p2_team_id
else:
p1_points = sum(s.p1 for s in sets)
p2_points = sum(s.p2 for s in sets)
if p1_points == p2_points:
raise HTTPException(400, "Absolute tie: Sets and Points are equal.")
match.winner_team_id = (
match.p1_team_id if p1_points > p2_points else match.p2_team_id
)
match.status = MatchStatus.FINISHED
match.sets = [s.model_dump() for s in sets]