""" Delte spørringer for blind draw-synlighet (ADR-013). Finkornet synlighet håndheves i APP-laget, ikke RLS — begge lag i en turnering ligger i samme organisasjon, så RLS (som filtrerer på organization_id) kan ikke skille dem fra hverandre. Disse to hjelperne brukes av både routers/tournaments.py (økt-listing: vise lås-status) og routers/matches.py (match-listing: filtrere deltakere). """ from asyncpg import Connection async def locked_team_ids(conn: Connection, session_id: str) -> set[str]: """Hvilke lag som har låst oppstillingen sin for denne økten.""" rows = await conn.fetch( "SELECT team_id::text AS team_id FROM lineup_lock WHERE session_id = $1", session_id, ) return {r["team_id"] for r in rows} async def own_team_ids(conn: Connection, organization_id: str, tournament_id: str, user_id: str) -> set[str]: """Hvilke lag brukeren FÅR SE deltakerne til, før begge lag har låst. To uavhengige veier inn, samme par som team_authz.user_may_act_for_team (2026-07-18 -- se den for full begrunnelse): rostret spiller (via player.user_id -> team_roster -> team) ELLER org-eier/admin, som da ser BEGGE lag. Uten org-admin-veien kunne en organisator LEGGE TIL deltakere (team_authz gir dem det) men aldri SE dem igjen før reveal -- en reell "skriv blindt" felle funnet under blind draw-skjermens integrering, ikke bare et tenkt tilfelle. """ rows = await conn.fetch( """ SELECT DISTINCT tr.team_id::text AS team_id FROM team_roster tr JOIN team t ON t.id = tr.team_id JOIN player p ON p.id = tr.player_id WHERE t.tournament_id = $1 AND p.user_id = $2 """, tournament_id, user_id, ) own = {r["team_id"] for r in rows} is_org_admin = await conn.fetchval( """ SELECT EXISTS ( SELECT 1 FROM organization_membership WHERE organization_id = $1 AND user_id = $2 AND role IN ('owner', 'admin') ) """, organization_id, user_id, ) if is_org_admin: all_teams = await conn.fetch("SELECT id::text FROM team WHERE tournament_id = $1", tournament_id) own |= {r["id"] for r in all_teams} return own