🏓 Initial ServeSync demo build
- FastAPI backend with PostgreSQL + Redis - 4 core features: Registration, Courts, Matchmaking, Tournament - Double elimination tournament with bracket visualization - ELO-based matchmaking (Stage 1 Open, Stage 2 Skill-Based) - Real-time WebSocket updates - TV Screen display for courts - Vue 3 + Tailwind CSS frontend - Seed data: 12 players, 4 courts, active matches, tournament in progress - Docker compose stack with Nginx reverse proxy
This commit is contained in:
82
backend/app/api/tournaments.py
Normal file
82
backend/app/api/tournaments.py
Normal file
@@ -0,0 +1,82 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from sqlalchemy.orm import Session
|
||||
from app.database import get_db
|
||||
from app.models.tournament import Tournament, TournamentEntry, TournamentMatch, TournamentStatus
|
||||
from app.services import tournament as tournament_service
|
||||
from pydantic import BaseModel
|
||||
from typing import Optional, List
|
||||
|
||||
router = APIRouter(prefix="/tournaments", tags=["tournaments"])
|
||||
|
||||
|
||||
class CreateTournamentRequest(BaseModel):
|
||||
name: str
|
||||
max_participants: int = 8
|
||||
|
||||
|
||||
class RegisterPlayerRequest(BaseModel):
|
||||
player_id: int
|
||||
|
||||
|
||||
class MatchScoreRequest(BaseModel):
|
||||
team1_score: int
|
||||
team2_score: int
|
||||
|
||||
|
||||
@router.get("/")
|
||||
def get_tournaments(db: Session = Depends(get_db)):
|
||||
tournaments = db.query(Tournament).order_by(Tournament.created_at.desc()).all()
|
||||
return [
|
||||
{
|
||||
"id": t.id,
|
||||
"name": t.name,
|
||||
"status": t.status.value,
|
||||
"max_participants": t.max_participants,
|
||||
"current_participants": len(t.entries),
|
||||
"started_at": t.started_at.isoformat() if t.started_at else None,
|
||||
}
|
||||
for t in tournaments
|
||||
]
|
||||
|
||||
|
||||
@router.get("/{tournament_id}")
|
||||
def get_tournament(tournament_id: int, db: Session = Depends(get_db)):
|
||||
return tournament_service.get_bracket_data(db, tournament_id)
|
||||
|
||||
|
||||
@router.post("/")
|
||||
def create_tournament(req: CreateTournamentRequest, db: Session = Depends(get_db)):
|
||||
t = tournament_service.create_tournament(db, req.name, req.max_participants)
|
||||
return {"id": t.id, "name": t.name, "status": t.status.value}
|
||||
|
||||
|
||||
@router.post("/{tournament_id}/register")
|
||||
def register_player(tournament_id: int, req: RegisterPlayerRequest, db: Session = Depends(get_db)):
|
||||
entry = tournament_service.register_player(db, tournament_id, req.player_id)
|
||||
if not entry:
|
||||
raise HTTPException(status_code=400, detail="Cannot register player (tournament full or already registered)")
|
||||
return {"message": "Registered successfully", "player_id": req.player_id}
|
||||
|
||||
|
||||
@router.post("/{tournament_id}/start")
|
||||
def start_tournament(tournament_id: int, db: Session = Depends(get_db)):
|
||||
t = tournament_service.start_tournament(db, tournament_id)
|
||||
if not t:
|
||||
raise HTTPException(status_code=400, detail="Cannot start tournament (need at least 4 players)")
|
||||
return tournament_service.get_bracket_data(db, tournament_id)
|
||||
|
||||
|
||||
@router.post("/{tournament_id}/matches/{match_id}/score")
|
||||
def complete_tournament_match(
|
||||
tournament_id: int, match_id: int, req: MatchScoreRequest, db: Session = Depends(get_db)
|
||||
):
|
||||
tm = tournament_service.complete_tournament_match(db, match_id, req.team1_score, req.team2_score)
|
||||
if not tm:
|
||||
raise HTTPException(status_code=400, detail="Cannot complete match")
|
||||
return tournament_service.get_bracket_data(db, tournament_id)
|
||||
|
||||
|
||||
@router.get("/{tournament_id}/leaderboard")
|
||||
def get_leaderboard(tournament_id: int, db: Session = Depends(get_db)):
|
||||
data = tournament_service.get_bracket_data(db, tournament_id)
|
||||
return data.get("leaderboard", [])
|
||||
Reference in New Issue
Block a user