ctfy.sdk.admin_resources.competition_topics

client.admin.competition_topics — the topics an organiser schedules for a competition, each released by the clock at its release_at (#1965).

 1"""``client.admin.competition_topics`` — the topics an organiser schedules
 2for a competition, each released by the clock at its ``release_at``
 3(#1965)."""
 4
 5from __future__ import annotations
 6
 7from datetime import datetime
 8from typing import Any
 9
10from ctfy.core.localized import LocalizedText
11from ctfy.sdk._helpers import _raise_for_status
12from ctfy.sdk.base import BaseHttpClient
13from ctfy.server.models import CompetitionTopicInfo, CompetitionTopicsResponse
14
15
16class AdminCompetitionTopicsResource:
17    """Schedule, list, edit and delete one competition's topics. For its
18    organisers (global admins and the event's own competition admins)."""
19
20    def __init__(self, http: BaseHttpClient) -> None:
21        self._http = http
22
23    def list(self, competition_id: str) -> CompetitionTopicsResponse:
24        """Every topic, released or not, in release order; ``released``
25        says which have reached their instant."""
26        resp = self._http.request("GET", f"/admin/competitions/{competition_id}/topics")
27        _raise_for_status(resp)
28        return CompetitionTopicsResponse.model_validate(resp.json())
29
30    def create(
31        self,
32        competition_id: str,
33        *,
34        title: str | LocalizedText,
35        body: str | LocalizedText = "",
36        release_at: datetime | None = None,
37    ) -> CompetitionTopicInfo:
38        """Schedule one topic. ``title`` / ``body`` are ``{locale: text}``
39        (a bare string is read as ``en``); ``body`` is markdown.
40        ``release_at`` ``None`` takes the start of the next authoring window
41        no other topic is released at — or ``422 topic_release_at_required``
42        when there is none."""
43        payload: dict[str, Any] = {"title": title, "body": body}
44        if release_at is not None:
45            payload["release_at"] = release_at.isoformat()
46        resp = self._http.request(
47            "POST", f"/admin/competitions/{competition_id}/topics", json=payload
48        )
49        _raise_for_status(resp)
50        return CompetitionTopicInfo.model_validate(resp.json())
51
52    def update(
53        self,
54        competition_id: str,
55        topic_id: str,
56        *,
57        title: str | LocalizedText | None = None,
58        body: str | LocalizedText | None = None,
59        release_at: datetime | None = None,
60    ) -> CompetitionTopicInfo:
61        """Edit one topic. Partial: only the arguments you pass are sent."""
62        payload: dict[str, Any] = {}
63        if title is not None:
64            payload["title"] = title
65        if body is not None:
66            payload["body"] = body
67        if release_at is not None:
68            payload["release_at"] = release_at.isoformat()
69        resp = self._http.request(
70            "PATCH", f"/admin/competitions/{competition_id}/topics/{topic_id}", json=payload
71        )
72        _raise_for_status(resp)
73        return CompetitionTopicInfo.model_validate(resp.json())
74
75    def delete(self, competition_id: str, topic_id: str) -> CompetitionTopicsResponse:
76        """Remove one topic; returns the list as it now stands."""
77        resp = self._http.request(
78            "DELETE", f"/admin/competitions/{competition_id}/topics/{topic_id}"
79        )
80        _raise_for_status(resp)
81        return CompetitionTopicsResponse.model_validate(resp.json())
class AdminCompetitionTopicsResource:
17class AdminCompetitionTopicsResource:
18    """Schedule, list, edit and delete one competition's topics. For its
19    organisers (global admins and the event's own competition admins)."""
20
21    def __init__(self, http: BaseHttpClient) -> None:
22        self._http = http
23
24    def list(self, competition_id: str) -> CompetitionTopicsResponse:
25        """Every topic, released or not, in release order; ``released``
26        says which have reached their instant."""
27        resp = self._http.request("GET", f"/admin/competitions/{competition_id}/topics")
28        _raise_for_status(resp)
29        return CompetitionTopicsResponse.model_validate(resp.json())
30
31    def create(
32        self,
33        competition_id: str,
34        *,
35        title: str | LocalizedText,
36        body: str | LocalizedText = "",
37        release_at: datetime | None = None,
38    ) -> CompetitionTopicInfo:
39        """Schedule one topic. ``title`` / ``body`` are ``{locale: text}``
40        (a bare string is read as ``en``); ``body`` is markdown.
41        ``release_at`` ``None`` takes the start of the next authoring window
42        no other topic is released at — or ``422 topic_release_at_required``
43        when there is none."""
44        payload: dict[str, Any] = {"title": title, "body": body}
45        if release_at is not None:
46            payload["release_at"] = release_at.isoformat()
47        resp = self._http.request(
48            "POST", f"/admin/competitions/{competition_id}/topics", json=payload
49        )
50        _raise_for_status(resp)
51        return CompetitionTopicInfo.model_validate(resp.json())
52
53    def update(
54        self,
55        competition_id: str,
56        topic_id: str,
57        *,
58        title: str | LocalizedText | None = None,
59        body: str | LocalizedText | None = None,
60        release_at: datetime | None = None,
61    ) -> CompetitionTopicInfo:
62        """Edit one topic. Partial: only the arguments you pass are sent."""
63        payload: dict[str, Any] = {}
64        if title is not None:
65            payload["title"] = title
66        if body is not None:
67            payload["body"] = body
68        if release_at is not None:
69            payload["release_at"] = release_at.isoformat()
70        resp = self._http.request(
71            "PATCH", f"/admin/competitions/{competition_id}/topics/{topic_id}", json=payload
72        )
73        _raise_for_status(resp)
74        return CompetitionTopicInfo.model_validate(resp.json())
75
76    def delete(self, competition_id: str, topic_id: str) -> CompetitionTopicsResponse:
77        """Remove one topic; returns the list as it now stands."""
78        resp = self._http.request(
79            "DELETE", f"/admin/competitions/{competition_id}/topics/{topic_id}"
80        )
81        _raise_for_status(resp)
82        return CompetitionTopicsResponse.model_validate(resp.json())

Schedule, list, edit and delete one competition's topics. For its organisers (global admins and the event's own competition admins).

AdminCompetitionTopicsResource(http: ctfy.sdk.base.BaseHttpClient)
21    def __init__(self, http: BaseHttpClient) -> None:
22        self._http = http
def list( self, competition_id: str) -> ctfy.server.models.CompetitionTopicsResponse:
24    def list(self, competition_id: str) -> CompetitionTopicsResponse:
25        """Every topic, released or not, in release order; ``released``
26        says which have reached their instant."""
27        resp = self._http.request("GET", f"/admin/competitions/{competition_id}/topics")
28        _raise_for_status(resp)
29        return CompetitionTopicsResponse.model_validate(resp.json())

Every topic, released or not, in release order; released says which have reached their instant.

def create( self, competition_id: str, *, title: str | dict[str, str], body: str | dict[str, str] = '', release_at: datetime.datetime | None = None) -> ctfy.server.models.CompetitionTopicInfo:
31    def create(
32        self,
33        competition_id: str,
34        *,
35        title: str | LocalizedText,
36        body: str | LocalizedText = "",
37        release_at: datetime | None = None,
38    ) -> CompetitionTopicInfo:
39        """Schedule one topic. ``title`` / ``body`` are ``{locale: text}``
40        (a bare string is read as ``en``); ``body`` is markdown.
41        ``release_at`` ``None`` takes the start of the next authoring window
42        no other topic is released at — or ``422 topic_release_at_required``
43        when there is none."""
44        payload: dict[str, Any] = {"title": title, "body": body}
45        if release_at is not None:
46            payload["release_at"] = release_at.isoformat()
47        resp = self._http.request(
48            "POST", f"/admin/competitions/{competition_id}/topics", json=payload
49        )
50        _raise_for_status(resp)
51        return CompetitionTopicInfo.model_validate(resp.json())

Schedule one topic. title / body are {locale: text} (a bare string is read as en); body is markdown. release_at None takes the start of the next authoring window no other topic is released at — or 422 topic_release_at_required when there is none.

def update( self, competition_id: str, topic_id: str, *, title: str | dict[str, str] | None = None, body: str | dict[str, str] | None = None, release_at: datetime.datetime | None = None) -> ctfy.server.models.CompetitionTopicInfo:
53    def update(
54        self,
55        competition_id: str,
56        topic_id: str,
57        *,
58        title: str | LocalizedText | None = None,
59        body: str | LocalizedText | None = None,
60        release_at: datetime | None = None,
61    ) -> CompetitionTopicInfo:
62        """Edit one topic. Partial: only the arguments you pass are sent."""
63        payload: dict[str, Any] = {}
64        if title is not None:
65            payload["title"] = title
66        if body is not None:
67            payload["body"] = body
68        if release_at is not None:
69            payload["release_at"] = release_at.isoformat()
70        resp = self._http.request(
71            "PATCH", f"/admin/competitions/{competition_id}/topics/{topic_id}", json=payload
72        )
73        _raise_for_status(resp)
74        return CompetitionTopicInfo.model_validate(resp.json())

Edit one topic. Partial: only the arguments you pass are sent.

def delete( self, competition_id: str, topic_id: str) -> ctfy.server.models.CompetitionTopicsResponse:
76    def delete(self, competition_id: str, topic_id: str) -> CompetitionTopicsResponse:
77        """Remove one topic; returns the list as it now stands."""
78        resp = self._http.request(
79            "DELETE", f"/admin/competitions/{competition_id}/topics/{topic_id}"
80        )
81        _raise_for_status(resp)
82        return CompetitionTopicsResponse.model_validate(resp.json())

Remove one topic; returns the list as it now stands.