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).
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.