fix: polylan sync
This commit is contained in:
@@ -14,11 +14,13 @@ from games.models import EventGame
|
||||
from ..models import Scope
|
||||
from .aandt_client import AuthenticatedClient
|
||||
from .aandt_client.api.default import (
|
||||
aandt_api_infos,
|
||||
aandt_api_match_messages_set,
|
||||
aandt_api_matches,
|
||||
aandt_api_members,
|
||||
)
|
||||
from .aandt_client.models import (
|
||||
AandtApiInfosResponse200,
|
||||
AandtApiMatchesResponse200,
|
||||
AandtApiMatchMessagesSetBody,
|
||||
AandtApiMembersResponse200,
|
||||
@@ -33,6 +35,11 @@ from .aandt_client.types import Unset
|
||||
# uvx openapi-python-client generate --path aandt_openapi.yaml --meta none \
|
||||
# --output-path aandt_client --overwrite
|
||||
|
||||
# AAndTInfosStatus value for "matches in progress" (see the / endpoint). Compared
|
||||
# as a plain string rather than the generated enum, whose member names
|
||||
# (VALUE_0, VALUE_1, ...) aren't stable across regenerations.
|
||||
AANDT_STATUS_IN_PROGRESS = "7inprog"
|
||||
|
||||
|
||||
class ScopeSyncError(Exception):
|
||||
"""Raised when an EventGame cannot be synced against the AAndT API."""
|
||||
@@ -43,6 +50,7 @@ class ScopeSyncResult:
|
||||
created: int = 0
|
||||
updated: int = 0
|
||||
warnings: list[str] = field(default_factory=list)
|
||||
status: str | None = None
|
||||
|
||||
|
||||
_T = TypeVar("_T")
|
||||
@@ -109,11 +117,16 @@ def sync_event_game_scopes(event_game: EventGame, /) -> ScopeSyncResult:
|
||||
submission page's URL pushed back as the match's player-facing message, so
|
||||
participants browsing the legacy site get a direct link here.
|
||||
|
||||
Scopes whose match is no longer returned by the API (deleted, or from a
|
||||
previous AAndT run reusing the same token) are deactivated.
|
||||
|
||||
Args:
|
||||
event_game: The EventGame to sync; must have `apibot_token` set.
|
||||
|
||||
Returns:
|
||||
A summary of created/updated scopes and any unresolved participants.
|
||||
A summary of created/updated scopes and any unresolved participants,
|
||||
plus the AAndT's current status (see `AANDT_STATUS_IN_PROGRESS`), so
|
||||
callers can decide whether to keep polling.
|
||||
|
||||
Raises:
|
||||
ScopeSyncError: When the EventGame has no token, or the API call fails.
|
||||
@@ -124,6 +137,7 @@ def sync_event_game_scopes(event_game: EventGame, /) -> ScopeSyncResult:
|
||||
client = _client(event_game)
|
||||
|
||||
try:
|
||||
infos_response = aandt_api_infos.sync_detailed(client=client)
|
||||
members_response = aandt_api_members.sync_detailed(client=client)
|
||||
matches_response = aandt_api_matches.sync_detailed(client=client)
|
||||
except (httpx.HTTPError, ValueError) as exc:
|
||||
@@ -134,32 +148,52 @@ def sync_event_game_scopes(event_game: EventGame, /) -> ScopeSyncResult:
|
||||
# resync this copy and regenerate.
|
||||
raise ScopeSyncError(f"Failed to reach AAndT API: {exc}") from exc
|
||||
|
||||
infos_parsed = infos_response.parsed
|
||||
members_parsed = members_response.parsed
|
||||
matches_parsed = matches_response.parsed
|
||||
|
||||
if not isinstance(members_parsed, AandtApiMembersResponse200) or not isinstance(
|
||||
matches_parsed, AandtApiMatchesResponse200
|
||||
if (
|
||||
not isinstance(infos_parsed, AandtApiInfosResponse200)
|
||||
or not isinstance(members_parsed, AandtApiMembersResponse200)
|
||||
or not isinstance(matches_parsed, AandtApiMatchesResponse200)
|
||||
):
|
||||
raise ScopeSyncError(
|
||||
"AAndT API returned "
|
||||
f"{members_response.status_code.value}/{matches_response.status_code.value}"
|
||||
f"{infos_response.status_code.value}/{members_response.status_code.value}"
|
||||
f"/{matches_response.status_code.value}"
|
||||
)
|
||||
|
||||
aandt_infos = _unwrap(infos_parsed.aandt)
|
||||
status = _unwrap(aandt_infos.status) if aandt_infos is not None else None
|
||||
|
||||
members = _unwrap(members_parsed.members, [])
|
||||
matches = _unwrap(matches_parsed.matches, [])
|
||||
|
||||
userids_by_member_id = _userids_by_member_id(members)
|
||||
result = ScopeSyncResult()
|
||||
result = ScopeSyncResult(status=status.value if status is not None else None)
|
||||
now = timezone.now()
|
||||
synced_match_ids: set[int] = set()
|
||||
|
||||
for match in matches:
|
||||
created, warnings = _sync_match(event_game, client, match, userids_by_member_id, now)
|
||||
match_id = _unwrap(match.id)
|
||||
created, warnings = _sync_match(
|
||||
event_game, client, match, userids_by_member_id, now
|
||||
)
|
||||
if created is None:
|
||||
continue
|
||||
synced_match_ids.add(match_id)
|
||||
result.created += created
|
||||
result.updated += not created
|
||||
result.warnings.extend(warnings)
|
||||
|
||||
(
|
||||
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)
|
||||
)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
@@ -210,11 +244,13 @@ def _sync_match(
|
||||
return created, warnings
|
||||
|
||||
|
||||
def _push_submission_link(client: AuthenticatedClient, match_id: int, scope: Scope) -> list[str]:
|
||||
def _push_submission_link(
|
||||
client: AuthenticatedClient, match_id: int, scope: Scope
|
||||
) -> list[str]:
|
||||
body = AandtApiMatchMessagesSetBody(
|
||||
players=(
|
||||
"<h3>Submit your result</h3>"
|
||||
f'<a href="{_submission_url(scope)}" target="_blank" rel="noopener">'
|
||||
"<h2>Submit your result</h2>"
|
||||
f'<a href="{_submission_url(scope)}" target="_blank" rel="noopener" class="btn btn-info">'
|
||||
"Submit your proof here</a>"
|
||||
),
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user