164 lines
5.6 KiB
Python
164 lines
5.6 KiB
Python
"""Screen Display API - for TV/display views"""
|
|
from fastapi import APIRouter, Depends
|
|
from sqlalchemy.orm import Session
|
|
from sqlalchemy import desc
|
|
from app.database import get_db
|
|
from app.models.court import Court
|
|
from app.models.match import Match, MatchPlayer, MatchStatus
|
|
from app.models.player import Player
|
|
from app.models.tournament import Tournament, TournamentStatus
|
|
from app.services.tournament import get_bracket_data
|
|
from datetime import datetime, timezone
|
|
|
|
router = APIRouter(prefix="/screen", tags=["screen"])
|
|
|
|
|
|
@router.get("/court/{court_id}")
|
|
def get_court_display(court_id: int, db: Session = Depends(get_db)):
|
|
"""Full court display data for TV screen (court_id = court_number)"""
|
|
# Try by court_number first (friendlier URLs), then by id
|
|
court = db.query(Court).filter(Court.court_number == court_id).first()
|
|
if not court:
|
|
court = db.query(Court).filter(Court.id == court_id).first()
|
|
if not court:
|
|
return {"error": "Court not found"}
|
|
|
|
# Current active match
|
|
active_match = db.query(Match).filter(
|
|
Match.court_id == court_id,
|
|
Match.status == MatchStatus.IN_PROGRESS
|
|
).first()
|
|
|
|
match_data = None
|
|
if active_match:
|
|
team1_players = []
|
|
team2_players = []
|
|
for mp in active_match.match_players:
|
|
p = db.query(Player).filter(Player.id == mp.player_id).first()
|
|
if p:
|
|
pd = {
|
|
"id": p.id,
|
|
"name": p.name,
|
|
"elo": round(p.elo_rating, 1),
|
|
"avatar_color": p.avatar_color,
|
|
"membership_tier": p.membership_tier.value,
|
|
}
|
|
if mp.team == 1:
|
|
team1_players.append(pd)
|
|
else:
|
|
team2_players.append(pd)
|
|
|
|
elapsed = None
|
|
if active_match.started_at:
|
|
now = datetime.now(timezone.utc)
|
|
started = active_match.started_at
|
|
if started.tzinfo is None:
|
|
started = started.replace(tzinfo=timezone.utc)
|
|
elapsed = int((now - started).total_seconds())
|
|
|
|
match_data = {
|
|
"id": active_match.id,
|
|
"stage": active_match.stage.value,
|
|
"match_type": active_match.match_type.value,
|
|
"team1": team1_players,
|
|
"team2": team2_players,
|
|
"team1_score": active_match.team1_score,
|
|
"team2_score": active_match.team2_score,
|
|
"elapsed_seconds": elapsed,
|
|
}
|
|
|
|
# Recent completed matches on this court
|
|
recent_matches = db.query(Match).filter(
|
|
Match.court_id == court_id,
|
|
Match.status == MatchStatus.COMPLETED
|
|
).order_by(desc(Match.ended_at)).limit(3).all()
|
|
|
|
recent = []
|
|
for m in recent_matches:
|
|
team1 = [db.query(Player).filter(Player.id == mp.player_id).first().name
|
|
for mp in m.match_players if mp.team == 1]
|
|
team2 = [db.query(Player).filter(Player.id == mp.player_id).first().name
|
|
for mp in m.match_players if mp.team == 2]
|
|
recent.append({
|
|
"team1": team1,
|
|
"team2": team2,
|
|
"team1_score": m.team1_score,
|
|
"team2_score": m.team2_score,
|
|
"winner": "team1" if m.team1_score > m.team2_score else "team2",
|
|
})
|
|
|
|
return {
|
|
"court": {
|
|
"id": court.id,
|
|
"name": court.name,
|
|
"court_number": court.court_number,
|
|
"surface_type": court.surface_type,
|
|
},
|
|
"active_match": match_data,
|
|
"recent_matches": recent,
|
|
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
}
|
|
|
|
|
|
@router.get("/overview")
|
|
def get_overview(db: Session = Depends(get_db)):
|
|
"""All courts overview for main display"""
|
|
courts = db.query(Court).filter(Court.is_active == True).order_by(Court.court_number).all()
|
|
result = []
|
|
for court in courts:
|
|
active_match = db.query(Match).filter(
|
|
Match.court_id == court.id,
|
|
Match.status == MatchStatus.IN_PROGRESS
|
|
).first()
|
|
|
|
court_data = {
|
|
"id": court.id,
|
|
"name": court.name,
|
|
"court_number": court.court_number,
|
|
"status": "occupied" if active_match else "available",
|
|
}
|
|
|
|
if active_match:
|
|
team1 = [mp.player.name for mp in active_match.match_players if mp.team == 1]
|
|
team2 = [mp.player.name for mp in active_match.match_players if mp.team == 2]
|
|
court_data["match"] = {
|
|
"team1_names": team1,
|
|
"team2_names": team2,
|
|
"team1_score": active_match.team1_score,
|
|
"team2_score": active_match.team2_score,
|
|
"stage": active_match.stage.value,
|
|
}
|
|
|
|
result.append(court_data)
|
|
|
|
# Active tournament
|
|
tournament_data = None
|
|
active_tournament = db.query(Tournament).filter(
|
|
Tournament.status == TournamentStatus.IN_PROGRESS
|
|
).first()
|
|
if active_tournament:
|
|
tournament_data = get_bracket_data(db, active_tournament.id)
|
|
|
|
# Leaderboard top 5
|
|
top_players = db.query(Player).filter(Player.is_active == True).order_by(
|
|
Player.elo_rating.desc()
|
|
).limit(5).all()
|
|
|
|
return {
|
|
"courts": result,
|
|
"tournament": tournament_data,
|
|
"leaderboard": [
|
|
{
|
|
"rank": i + 1,
|
|
"name": p.name,
|
|
"elo": round(p.elo_rating, 1),
|
|
"tier": p.membership_tier.value,
|
|
"wins": p.wins,
|
|
"losses": p.losses,
|
|
"avatar_color": p.avatar_color,
|
|
}
|
|
for i, p in enumerate(top_players)
|
|
],
|
|
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
}
|