feat(submissions): auto-replace player in scopes, cancel sync + info about the sync

This commit is contained in:
2026-10-16 01:49:53 +02:00
parent de5ee8ae7a
commit 13c09adff3
18 changed files with 1051 additions and 137 deletions
@@ -12,6 +12,7 @@ from accounts.models import CustomUser
from games.models import EventGame
from ..models import Scope
from ..signals import broadcast_scope_updates
from .aandt_client import AuthenticatedClient
from .aandt_client.api.default import (
aandt_api_infos,
@@ -120,6 +121,9 @@ def sync_event_game_scopes(event_game: EventGame, /) -> ScopeSyncResult:
Scopes whose match is no longer returned by the API (deleted, or from a
previous AAndT run reusing the same token) are deactivated.
Users of scopes that opened/closed or got their players changed are
notified over the submissions websocket (see `broadcast_scope_updates`).
Args:
event_game: The EventGame to sync; must have `apibot_token` set.
@@ -173,10 +177,11 @@ def sync_event_game_scopes(event_game: EventGame, /) -> ScopeSyncResult:
result = ScopeSyncResult(status=status.value if status is not None else None)
now = timezone.now()
synced_match_ids: set[int] = set()
changes: dict[Scope, set[int]] = {}
for match in matches:
match_id = _unwrap(match.id)
created, warnings = _sync_match(
created, warnings, change = _sync_match(
event_game, client, match, userids_by_member_id, now
)
if created is None:
@@ -185,14 +190,22 @@ def sync_event_game_scopes(event_game: EventGame, /) -> ScopeSyncResult:
result.created += created
result.updated += not created
result.warnings.extend(warnings)
if change is not None:
changes[change[0]] = change[1]
(
gone = list(
Scope.objects.filter(
event_game=event_game, is_active=True, external_id__isnull=False
)
.exclude(external_id__in=synced_match_ids)
.update(is_active=False)
.prefetch_related("assigned_users")
)
Scope.objects.filter(id__in=[scope.id for scope in gone]).update(is_active=False)
for scope in gone:
scope.is_active = False
changes[scope] = {user.id for user in scope.assigned_users.all()}
broadcast_scope_updates(changes)
return result
@@ -203,10 +216,17 @@ def _sync_match(
match: Match,
userids_by_member_id: dict[int, set[int]],
now: datetime,
) -> tuple[bool | None, list[str]]:
) -> tuple[bool | None, list[str], tuple[Scope, set[int]] | None]:
"""Upsert the Scope of one match.
Returns:
Whether the Scope was created (None when the match has no id), the
warnings, and when it opened/closed or its players changed, the Scope
with every user id that was or now is assigned to it.
"""
match_id = _unwrap(match.id)
if match_id is None:
return None, []
return None, [], None
warnings: list[str] = []
@@ -218,6 +238,12 @@ def _sync_match(
users = list(CustomUser.objects.filter(cas_user_id__in=userids))
previous = Scope.objects.filter(event_game=event_game, external_id=match_id).first()
was_active = previous.is_active if previous else False
previous_user_ids = (
set(previous.assigned_users.values_list("id", flat=True)) if previous else set()
)
scope, created = Scope.objects.update_or_create(
event_game=event_game,
external_id=match_id,
@@ -241,7 +267,12 @@ def _sync_match(
if scope.is_active:
warnings.extend(_push_submission_link(client, match_id, scope))
return created, warnings
user_ids = {user.id for user in users}
change = None
if scope.is_active != was_active or user_ids != previous_user_ids:
change = (scope, user_ids | previous_user_ids)
return created, warnings, change
def _push_submission_link(