ctfy.sdk.admin_resources.competition_applications

client.admin.competition_applications — people asking to be let into a private competition, and the organisers' answer (plan L2).

 1"""``client.admin.competition_applications`` — people asking to be let
 2into a private competition, and the organisers' answer (plan L2)."""
 3
 4from __future__ import annotations
 5
 6from typing import Any
 7
 8from ctfy.core.enums import ReviewStatus
 9from ctfy.sdk._helpers import PagedList, _extract_items, _raise_for_status
10from ctfy.sdk.base import BaseHttpClient
11from ctfy.server.models import CompetitionApplicationRow
12
13
14class AdminCompetitionApplicationsResource:
15    """Read one competition's applications and rule on them. For its
16    organisers — whoever may invite (global admins and the event's full
17    organisers); a roster reviewer may not, since an approval *is* an
18    invitation."""
19
20    def __init__(self, http: BaseHttpClient) -> None:
21        self._http = http
22
23    def list(
24        self,
25        competition_id: str,
26        *,
27        status: ReviewStatus | None = None,
28        offset: int = 0,
29        limit: int = 50,
30    ) -> PagedList[CompetitionApplicationRow]:
31        """The queue, oldest first; ``status`` narrows the page and its
32        ``total`` alike. ``invited`` on each row is whether the person
33        holds an invitation *now* — distinct from ``status == "approved"``
34        once an invitation is revoked."""
35        params: dict[str, Any] = {"offset": offset, "limit": limit}
36        if status is not None:
37            params["status"] = status
38        resp = self._http.request(
39            "GET", f"/admin/competitions/{competition_id}/applications", params=params
40        )
41        _raise_for_status(resp)
42        return _extract_items(resp.json(), CompetitionApplicationRow)
43
44    def export_csv(self, competition_id: str) -> bytes:
45        """Every application as CSV (UTF-8 with BOM, #2306): the queue's own
46        columns -- organisation, purpose, contact address among them. The
47        read is recorded as a personal-data export."""
48        resp = self._http.request("GET", f"/admin/competitions/{competition_id}/application-export")
49        _raise_for_status(resp)
50        return resp.content
51
52    def approve(self, competition_id: str, user_id: str) -> CompetitionApplicationRow:
53        """Let them in: writes their invitation and sends the invitation
54        mail. Once — a second verdict is ``application_already_decided``."""
55        return self._review(competition_id, user_id, {"status": "approved"})
56
57    def reject(self, competition_id: str, user_id: str, note: str) -> CompetitionApplicationRow:
58        """Turn them down, saying why. Final for the application — the
59        person cannot send it again, and you can still invite them — so
60        ``note`` is required (``application_review_note_required``)."""
61        return self._review(competition_id, user_id, {"status": "rejected", "note": note})
62
63    def _review(
64        self, competition_id: str, user_id: str, body: dict[str, str]
65    ) -> CompetitionApplicationRow:
66        resp = self._http.request(
67            "POST",
68            f"/admin/competitions/{competition_id}/applications/{user_id}/review",
69            json=body,
70        )
71        _raise_for_status(resp)
72        return CompetitionApplicationRow.model_validate(resp.json())
class AdminCompetitionApplicationsResource:
15class AdminCompetitionApplicationsResource:
16    """Read one competition's applications and rule on them. For its
17    organisers — whoever may invite (global admins and the event's full
18    organisers); a roster reviewer may not, since an approval *is* an
19    invitation."""
20
21    def __init__(self, http: BaseHttpClient) -> None:
22        self._http = http
23
24    def list(
25        self,
26        competition_id: str,
27        *,
28        status: ReviewStatus | None = None,
29        offset: int = 0,
30        limit: int = 50,
31    ) -> PagedList[CompetitionApplicationRow]:
32        """The queue, oldest first; ``status`` narrows the page and its
33        ``total`` alike. ``invited`` on each row is whether the person
34        holds an invitation *now* — distinct from ``status == "approved"``
35        once an invitation is revoked."""
36        params: dict[str, Any] = {"offset": offset, "limit": limit}
37        if status is not None:
38            params["status"] = status
39        resp = self._http.request(
40            "GET", f"/admin/competitions/{competition_id}/applications", params=params
41        )
42        _raise_for_status(resp)
43        return _extract_items(resp.json(), CompetitionApplicationRow)
44
45    def export_csv(self, competition_id: str) -> bytes:
46        """Every application as CSV (UTF-8 with BOM, #2306): the queue's own
47        columns -- organisation, purpose, contact address among them. The
48        read is recorded as a personal-data export."""
49        resp = self._http.request("GET", f"/admin/competitions/{competition_id}/application-export")
50        _raise_for_status(resp)
51        return resp.content
52
53    def approve(self, competition_id: str, user_id: str) -> CompetitionApplicationRow:
54        """Let them in: writes their invitation and sends the invitation
55        mail. Once — a second verdict is ``application_already_decided``."""
56        return self._review(competition_id, user_id, {"status": "approved"})
57
58    def reject(self, competition_id: str, user_id: str, note: str) -> CompetitionApplicationRow:
59        """Turn them down, saying why. Final for the application — the
60        person cannot send it again, and you can still invite them — so
61        ``note`` is required (``application_review_note_required``)."""
62        return self._review(competition_id, user_id, {"status": "rejected", "note": note})
63
64    def _review(
65        self, competition_id: str, user_id: str, body: dict[str, str]
66    ) -> CompetitionApplicationRow:
67        resp = self._http.request(
68            "POST",
69            f"/admin/competitions/{competition_id}/applications/{user_id}/review",
70            json=body,
71        )
72        _raise_for_status(resp)
73        return CompetitionApplicationRow.model_validate(resp.json())

Read one competition's applications and rule on them. For its organisers — whoever may invite (global admins and the event's full organisers); a roster reviewer may not, since an approval is an invitation.

AdminCompetitionApplicationsResource(http: ctfy.sdk.base.BaseHttpClient)
21    def __init__(self, http: BaseHttpClient) -> None:
22        self._http = http
def list( self, competition_id: str, *, status: Optional[Literal['pending', 'approved', 'rejected']] = None, offset: int = 0, limit: int = 50) -> ctfy.sdk._helpers.PagedList[ctfy.server.models.CompetitionApplicationRow]:
24    def list(
25        self,
26        competition_id: str,
27        *,
28        status: ReviewStatus | None = None,
29        offset: int = 0,
30        limit: int = 50,
31    ) -> PagedList[CompetitionApplicationRow]:
32        """The queue, oldest first; ``status`` narrows the page and its
33        ``total`` alike. ``invited`` on each row is whether the person
34        holds an invitation *now* — distinct from ``status == "approved"``
35        once an invitation is revoked."""
36        params: dict[str, Any] = {"offset": offset, "limit": limit}
37        if status is not None:
38            params["status"] = status
39        resp = self._http.request(
40            "GET", f"/admin/competitions/{competition_id}/applications", params=params
41        )
42        _raise_for_status(resp)
43        return _extract_items(resp.json(), CompetitionApplicationRow)

The queue, oldest first; status narrows the page and its total alike. invited on each row is whether the person holds an invitation now — distinct from status == "approved" once an invitation is revoked.

def export_csv(self, competition_id: str) -> bytes:
45    def export_csv(self, competition_id: str) -> bytes:
46        """Every application as CSV (UTF-8 with BOM, #2306): the queue's own
47        columns -- organisation, purpose, contact address among them. The
48        read is recorded as a personal-data export."""
49        resp = self._http.request("GET", f"/admin/competitions/{competition_id}/application-export")
50        _raise_for_status(resp)
51        return resp.content

Every application as CSV (UTF-8 with BOM, #2306): the queue's own columns -- organisation, purpose, contact address among them. The read is recorded as a personal-data export.

def approve( self, competition_id: str, user_id: str) -> ctfy.server.models.CompetitionApplicationRow:
53    def approve(self, competition_id: str, user_id: str) -> CompetitionApplicationRow:
54        """Let them in: writes their invitation and sends the invitation
55        mail. Once — a second verdict is ``application_already_decided``."""
56        return self._review(competition_id, user_id, {"status": "approved"})

Let them in: writes their invitation and sends the invitation mail. Once — a second verdict is application_already_decided.

def reject( self, competition_id: str, user_id: str, note: str) -> ctfy.server.models.CompetitionApplicationRow:
58    def reject(self, competition_id: str, user_id: str, note: str) -> CompetitionApplicationRow:
59        """Turn them down, saying why. Final for the application — the
60        person cannot send it again, and you can still invite them — so
61        ``note`` is required (``application_review_note_required``)."""
62        return self._review(competition_id, user_id, {"status": "rejected", "note": note})

Turn them down, saying why. Final for the application — the person cannot send it again, and you can still invite them — so note is required (application_review_note_required).