ctfy.sdk.resources.scoreboard
client.scoreboard — global standings and per-challenge stats.
1"""``client.scoreboard`` — global standings and per-challenge stats.""" 2 3from __future__ import annotations 4 5from typing import Any 6 7from ctfy.sdk._helpers import PagedList, _extract_items, _raise_for_status 8from ctfy.sdk.base import BaseHttpClient 9from ctfy.server.models import ChallengeStats, ScoreboardEntry, UserScoreboardEntry 10 11 12class ScoreboardResource: 13 """Global (admin/snapshot) scoreboard surface. Per-competition standings 14 live on :meth:`CompetitionsResource.scoreboard`.""" 15 16 def __init__(self, http: BaseHttpClient) -> None: 17 self._http = http 18 19 def get( 20 self, category: str = "", offset: int = 0, limit: int = 50, division: str = "" 21 ) -> PagedList[ScoreboardEntry]: 22 """The global standings. 23 24 ``division`` narrows to one side of a mixed board: ``"human"`` 25 or ``"agent"``. Empty — the default — is everybody, which is the 26 board this method has always returned. ⚠️ Split here rather than 27 by filtering the result: the rows are paginated server-side, so a 28 client-side filter yields whichever of one page matched, still 29 carrying their *merged* ranks. 30 """ 31 params: dict[str, Any] = {"offset": offset, "limit": limit} 32 if category: 33 params["category"] = category 34 if division: 35 params["division"] = division 36 resp = self._http.request("GET", "/scoreboard", params=params) 37 _raise_for_status(resp) 38 return _extract_items(resp.json(), ScoreboardEntry) 39 40 def challenge_stats(self, offset: int = 0, limit: int = 50) -> PagedList[ChallengeStats]: 41 resp = self._http.request( 42 "GET", "/scoreboard/challenges", params={"offset": offset, "limit": limit} 43 ) 44 _raise_for_status(resp) 45 return _extract_items(resp.json(), ChallengeStats) 46 47 def users(self, offset: int = 0, limit: int = 50) -> PagedList[UserScoreboardEntry]: 48 """Per-user (not per-team) global standings.""" 49 resp = self._http.request( 50 "GET", "/scoreboard/users", params={"offset": offset, "limit": limit} 51 ) 52 _raise_for_status(resp) 53 return _extract_items(resp.json(), UserScoreboardEntry)
class
ScoreboardResource:
13class ScoreboardResource: 14 """Global (admin/snapshot) scoreboard surface. Per-competition standings 15 live on :meth:`CompetitionsResource.scoreboard`.""" 16 17 def __init__(self, http: BaseHttpClient) -> None: 18 self._http = http 19 20 def get( 21 self, category: str = "", offset: int = 0, limit: int = 50, division: str = "" 22 ) -> PagedList[ScoreboardEntry]: 23 """The global standings. 24 25 ``division`` narrows to one side of a mixed board: ``"human"`` 26 or ``"agent"``. Empty — the default — is everybody, which is the 27 board this method has always returned. ⚠️ Split here rather than 28 by filtering the result: the rows are paginated server-side, so a 29 client-side filter yields whichever of one page matched, still 30 carrying their *merged* ranks. 31 """ 32 params: dict[str, Any] = {"offset": offset, "limit": limit} 33 if category: 34 params["category"] = category 35 if division: 36 params["division"] = division 37 resp = self._http.request("GET", "/scoreboard", params=params) 38 _raise_for_status(resp) 39 return _extract_items(resp.json(), ScoreboardEntry) 40 41 def challenge_stats(self, offset: int = 0, limit: int = 50) -> PagedList[ChallengeStats]: 42 resp = self._http.request( 43 "GET", "/scoreboard/challenges", params={"offset": offset, "limit": limit} 44 ) 45 _raise_for_status(resp) 46 return _extract_items(resp.json(), ChallengeStats) 47 48 def users(self, offset: int = 0, limit: int = 50) -> PagedList[UserScoreboardEntry]: 49 """Per-user (not per-team) global standings.""" 50 resp = self._http.request( 51 "GET", "/scoreboard/users", params={"offset": offset, "limit": limit} 52 ) 53 _raise_for_status(resp) 54 return _extract_items(resp.json(), UserScoreboardEntry)
Global (admin/snapshot) scoreboard surface. Per-competition standings
live on CompetitionsResource.scoreboard().
def
get( self, category: str = '', offset: int = 0, limit: int = 50, division: str = '') -> ctfy.sdk._helpers.PagedList[ctfy.server.models.ScoreboardEntry]:
20 def get( 21 self, category: str = "", offset: int = 0, limit: int = 50, division: str = "" 22 ) -> PagedList[ScoreboardEntry]: 23 """The global standings. 24 25 ``division`` narrows to one side of a mixed board: ``"human"`` 26 or ``"agent"``. Empty — the default — is everybody, which is the 27 board this method has always returned. ⚠️ Split here rather than 28 by filtering the result: the rows are paginated server-side, so a 29 client-side filter yields whichever of one page matched, still 30 carrying their *merged* ranks. 31 """ 32 params: dict[str, Any] = {"offset": offset, "limit": limit} 33 if category: 34 params["category"] = category 35 if division: 36 params["division"] = division 37 resp = self._http.request("GET", "/scoreboard", params=params) 38 _raise_for_status(resp) 39 return _extract_items(resp.json(), ScoreboardEntry)
The global standings.
division narrows to one side of a mixed board: "human"
or "agent". Empty — the default — is everybody, which is the
board this method has always returned. ⚠️ Split here rather than
by filtering the result: the rows are paginated server-side, so a
client-side filter yields whichever of one page matched, still
carrying their merged ranks.
def
challenge_stats( self, offset: int = 0, limit: int = 50) -> ctfy.sdk._helpers.PagedList[ctfy.server.models.ChallengeStats]:
def
users( self, offset: int = 0, limit: int = 50) -> ctfy.sdk._helpers.PagedList[ctfy.server.models.UserScoreboardEntry]:
48 def users(self, offset: int = 0, limit: int = 50) -> PagedList[UserScoreboardEntry]: 49 """Per-user (not per-team) global standings.""" 50 resp = self._http.request( 51 "GET", "/scoreboard/users", params={"offset": offset, "limit": limit} 52 ) 53 _raise_for_status(resp) 54 return _extract_items(resp.json(), UserScoreboardEntry)
Per-user (not per-team) global standings.