47 lines
1.8 KiB
Python
47 lines
1.8 KiB
Python
"""
|
|
Delt in-memory WebSocket-kringkasting for sanntidsoppdateringer på den
|
|
offentlige "Følg live"-siden (ADR-027, oppfølging av ADR-025/026).
|
|
|
|
Egen, RUTEFRI modul -- importerer INGEN router. Grunnen: både
|
|
`scoring.py`/`tournaments.py` (som endrer matchstatus) og `messaging.py`
|
|
(som eier selve WS-ruten, `/ws/public/tournaments/{id}/live`) trenger denne,
|
|
og `registration.py` importerer allerede fra scoring.py/tournaments.py/
|
|
matches.py mens `messaging.py` importerer fra `registration.py` -- en
|
|
kringkastingsfunksjon plassert i en av routerne ville skapt en sirkulær
|
|
import. Denne modulen ligger BAK alle routere i importgrafen i stedet.
|
|
|
|
In-memory PER PROSESS -- samme kjente begrensning som meldinger sin egen
|
|
kringkasting (`app/routers/messaging.py`): trygt med dagens ene
|
|
`teecup_api`-container, deles IKKE på tvers av flere prosesser/containere
|
|
(se ARCHITECTURE_DECISIONS.md sine "Åpne spørsmål").
|
|
|
|
Bevisst "noe endret seg, hent på nytt"-signal, IKKE selve dataene --
|
|
i motsetning til meldinger (der selve meldingen kringkastes direkte),
|
|
ville det å bygge/sende hele leaderboard+matchliste+scorekort-formen her
|
|
duplisert betydelig beregningslogikk. Klienten reagerer ved å kalle de
|
|
samme REST-endepunktene på nytt, akkurat som ved førstegangslasting.
|
|
"""
|
|
|
|
from collections import defaultdict
|
|
|
|
from fastapi import WebSocket
|
|
|
|
_tournament_live_sockets: dict[str, set[WebSocket]] = defaultdict(set)
|
|
|
|
|
|
def live_sockets_for(tournament_id: str) -> set[WebSocket]:
|
|
return _tournament_live_sockets[tournament_id]
|
|
|
|
|
|
async def broadcast_live_update(tournament_id: str) -> None:
|
|
sockets = _tournament_live_sockets.get(tournament_id)
|
|
if not sockets:
|
|
return
|
|
dead = []
|
|
for ws in list(sockets):
|
|
try:
|
|
await ws.send_json({"type": "update"})
|
|
except Exception:
|
|
dead.append(ws)
|
|
for ws in dead:
|
|
sockets.discard(ws)
|