ctftime-api 0.1.0__tar.gz → 0.1.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {ctftime_api-0.1.0 → ctftime_api-0.1.2}/PKG-INFO +4 -2
- {ctftime_api-0.1.0 → ctftime_api-0.1.2}/ctftime_api/client.py +58 -30
- ctftime_api-0.1.2/ctftime_api/models/vote.py +14 -0
- {ctftime_api-0.1.0 → ctftime_api-0.1.2}/pyproject.toml +4 -2
- {ctftime_api-0.1.0 → ctftime_api-0.1.2}/LICENSE +0 -0
- {ctftime_api-0.1.0 → ctftime_api-0.1.2}/README.md +0 -0
- {ctftime_api-0.1.0 → ctftime_api-0.1.2}/ctftime_api/__init__.py +0 -0
- {ctftime_api-0.1.0 → ctftime_api-0.1.2}/ctftime_api/models/__init__.py +0 -0
- {ctftime_api-0.1.0 → ctftime_api-0.1.2}/ctftime_api/models/duration.py +0 -0
- {ctftime_api-0.1.0 → ctftime_api-0.1.2}/ctftime_api/models/event.py +0 -0
- {ctftime_api-0.1.0 → ctftime_api-0.1.2}/ctftime_api/models/rating.py +0 -0
- {ctftime_api-0.1.0 → ctftime_api-0.1.2}/ctftime_api/models/team.py +0 -0
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: ctftime_api
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: CTFTime API wrapper
|
|
5
5
|
Keywords: ctftime,api,wrapper
|
|
6
6
|
Author: John Toniutti
|
|
7
7
|
Author-email: john.toniutti@gmail.com
|
|
8
8
|
Requires-Python: >=3.11
|
|
9
|
-
Classifier: Development Status ::
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
10
|
Classifier: Intended Audience :: Developers
|
|
11
11
|
Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
|
|
12
|
+
Classifier: Framework :: Pydantic
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
12
14
|
Classifier: Programming Language :: Python :: 3
|
|
13
15
|
Classifier: Programming Language :: Python :: 3.11
|
|
14
16
|
Classifier: Programming Language :: Python :: 3.12
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
from datetime import datetime
|
|
2
|
+
from typing import Any
|
|
2
3
|
|
|
3
4
|
import httpx
|
|
4
|
-
from httpx import URL
|
|
5
|
+
from httpx import URL, Timeout
|
|
5
6
|
|
|
6
7
|
from pydantic_extra_types.country import CountryAlpha2
|
|
7
8
|
|
|
9
|
+
from ctftime_api.models.vote import Vote
|
|
8
10
|
from ctftime_api.models.event import Event, EventResult
|
|
9
11
|
from ctftime_api.models.team import TeamRank, Team, TeamComplete
|
|
10
12
|
|
|
11
|
-
|
|
12
13
|
__all__ = ["CTFTimeClient"]
|
|
13
14
|
|
|
14
15
|
|
|
@@ -26,6 +27,18 @@ class CTFTimeClient:
|
|
|
26
27
|
self._client = httpx.AsyncClient(*args, **kwargs)
|
|
27
28
|
self._base_url = URL("https://ctftime.org/api/v1/")
|
|
28
29
|
|
|
30
|
+
async def _get(self, url: str | URL, **kwargs) -> Any:
|
|
31
|
+
"""
|
|
32
|
+
Perform a GET request.
|
|
33
|
+
:param url: The url to make the request to.
|
|
34
|
+
:param kwargs: Additional arguments to pass to the request.
|
|
35
|
+
:return: The response JSON.
|
|
36
|
+
:raise httpx.HTTPStatusError: If the response status code is not successful.
|
|
37
|
+
"""
|
|
38
|
+
response = await self._client.get(url, **kwargs)
|
|
39
|
+
response.raise_for_status()
|
|
40
|
+
return response.json()
|
|
41
|
+
|
|
29
42
|
async def close(self):
|
|
30
43
|
"""Close the underlying httpx.AsyncClient."""
|
|
31
44
|
await self._client.aclose()
|
|
@@ -38,16 +51,16 @@ class CTFTimeClient:
|
|
|
38
51
|
:param year: The year to get the top teams for. If None, the current year will be used.
|
|
39
52
|
:param limit: The number of teams to get.
|
|
40
53
|
:return: A list of the top teams.
|
|
54
|
+
:raise httpx.HTTPStatusError: If the response status code is not successful.
|
|
41
55
|
"""
|
|
42
56
|
if year is None:
|
|
43
57
|
url = self._base_url.join("top/")
|
|
44
58
|
year = datetime.now().year
|
|
45
59
|
else:
|
|
46
60
|
url = self._base_url.join(f"top/{year}/")
|
|
47
|
-
response = await self._client.get(url, params={"limit": limit})
|
|
48
|
-
response.raise_for_status()
|
|
49
61
|
|
|
50
|
-
|
|
62
|
+
response: dict[str, list[dict]] = await self._get(url, params={"limit": limit})
|
|
63
|
+
teams = response.get(f"{year}", [])
|
|
51
64
|
|
|
52
65
|
return [TeamRank.model_validate(team) for team in teams]
|
|
53
66
|
|
|
@@ -59,6 +72,8 @@ class CTFTimeClient:
|
|
|
59
72
|
:param country: The country to get the top teams for.
|
|
60
73
|
It can be a pycountry Country object or a two-letter country code.
|
|
61
74
|
:return: A list of the top teams.
|
|
75
|
+
:raise httpx.HTTPStatusError: If the response status code is not successful.
|
|
76
|
+
:raise ValueError: If the country is not a two-letter country code or a pycountry Country object.
|
|
62
77
|
"""
|
|
63
78
|
if isinstance(country, CountryAlpha2):
|
|
64
79
|
country = country
|
|
@@ -69,10 +84,7 @@ class CTFTimeClient:
|
|
|
69
84
|
)
|
|
70
85
|
|
|
71
86
|
url = self._base_url.join("top-by-country/").join(f"{country}/")
|
|
72
|
-
|
|
73
|
-
response.raise_for_status()
|
|
74
|
-
|
|
75
|
-
teams = response.json()
|
|
87
|
+
teams: list[dict[str, Any]] = await self._get(url)
|
|
76
88
|
|
|
77
89
|
return [TeamRank.model_validate(team) for team in teams]
|
|
78
90
|
|
|
@@ -87,6 +99,8 @@ class CTFTimeClient:
|
|
|
87
99
|
It can be a Unix timestamp or a datetime object.
|
|
88
100
|
:param limit: The number of events to get.
|
|
89
101
|
:return: A list of events.
|
|
102
|
+
:raise httpx.HTTPStatusError: If the response status code is not successful.
|
|
103
|
+
:raise ValueError: If the start date is after the end date.
|
|
90
104
|
"""
|
|
91
105
|
if isinstance(start, datetime):
|
|
92
106
|
start = int(start.timestamp())
|
|
@@ -97,12 +111,9 @@ class CTFTimeClient:
|
|
|
97
111
|
raise ValueError("The start date must be before the end date.")
|
|
98
112
|
|
|
99
113
|
url = self._base_url.join("events/")
|
|
100
|
-
|
|
114
|
+
events: list[dict[str, Any]] = await self._get(
|
|
101
115
|
url, params={"start": start, "finish": end, "limit": limit}
|
|
102
116
|
)
|
|
103
|
-
response.raise_for_status()
|
|
104
|
-
|
|
105
|
-
events = response.json()
|
|
106
117
|
|
|
107
118
|
return [Event.model_validate(event) for event in events]
|
|
108
119
|
|
|
@@ -111,12 +122,10 @@ class CTFTimeClient:
|
|
|
111
122
|
Get information about a specific event.
|
|
112
123
|
:param event_id: The ID of the event.
|
|
113
124
|
:return: The event information.
|
|
125
|
+
:raise httpx.HTTPStatusError: If the response status code is not successful.
|
|
114
126
|
"""
|
|
115
127
|
url = self._base_url.join(f"events/{event_id}/")
|
|
116
|
-
|
|
117
|
-
response.raise_for_status()
|
|
118
|
-
|
|
119
|
-
event = response.json()
|
|
128
|
+
event: dict[str, Any] = await self._get(url)
|
|
120
129
|
|
|
121
130
|
return Event.model_validate(event)
|
|
122
131
|
|
|
@@ -128,14 +137,14 @@ class CTFTimeClient:
|
|
|
128
137
|
:param limit: The number of teams to get.
|
|
129
138
|
:param offset: The offset to start from.
|
|
130
139
|
:return: A list of teams.
|
|
140
|
+
:raise httpx.HTTPStatusError: If the response status code is not successful.
|
|
131
141
|
"""
|
|
132
142
|
url = self._base_url.join("teams/")
|
|
133
|
-
|
|
143
|
+
|
|
144
|
+
response: dict[str, Any] = await self._get(
|
|
134
145
|
url, params={"limit": limit, "offset": offset}
|
|
135
146
|
)
|
|
136
|
-
response.
|
|
137
|
-
|
|
138
|
-
teams = response.json().get("results", [])
|
|
147
|
+
teams: list[dict[str, Any]] = response.get("results", [])
|
|
139
148
|
|
|
140
149
|
return [Team.model_validate(team) for team in teams]
|
|
141
150
|
|
|
@@ -144,12 +153,10 @@ class CTFTimeClient:
|
|
|
144
153
|
Get information about a specific team.
|
|
145
154
|
:param team_id: The ID of the team.
|
|
146
155
|
:return: The team information.
|
|
156
|
+
:raise httpx.HTTPStatusError: If the response status code is not successful.
|
|
147
157
|
"""
|
|
148
158
|
url = self._base_url.join(f"teams/{team_id}/")
|
|
149
|
-
|
|
150
|
-
response.raise_for_status()
|
|
151
|
-
|
|
152
|
-
team = response.json()
|
|
159
|
+
team: dict[str, Any] = await self._get(url)
|
|
153
160
|
|
|
154
161
|
return TeamComplete.model_validate(team)
|
|
155
162
|
|
|
@@ -161,18 +168,39 @@ class CTFTimeClient:
|
|
|
161
168
|
:param year: The year to get the results for.
|
|
162
169
|
If None, the current year will be used.
|
|
163
170
|
:return: A dictionary of event results.
|
|
171
|
+
:raise httpx.HTTPStatusError: If the response status code is not successful.
|
|
164
172
|
"""
|
|
165
173
|
if year is None:
|
|
166
174
|
url = self._base_url.join("results/")
|
|
167
175
|
else:
|
|
168
176
|
url = self._base_url.join(f"results/{year}/")
|
|
169
177
|
|
|
170
|
-
|
|
171
|
-
response.raise_for_status()
|
|
172
|
-
|
|
173
|
-
event = response.json()
|
|
178
|
+
event: dict[str, dict] = await self._get(url)
|
|
174
179
|
|
|
175
180
|
return {
|
|
176
|
-
int(ctf_id): EventResult(**result, ctf_id=ctf_id)
|
|
181
|
+
int(ctf_id): EventResult(**result, ctf_id=int(ctf_id))
|
|
177
182
|
for ctf_id, result in event.items()
|
|
178
183
|
}
|
|
184
|
+
|
|
185
|
+
async def get_votes_per_year(
|
|
186
|
+
self, year: int | None, timeout: Timeout | int | float | None = None
|
|
187
|
+
) -> list[Vote]:
|
|
188
|
+
"""
|
|
189
|
+
Get the votes for a specific year.
|
|
190
|
+
This API call may take a long time to complete.
|
|
191
|
+
:param year: The year to get the votes for or None for the current year.
|
|
192
|
+
:param timeout: The timeout for the request.
|
|
193
|
+
If None, the session timeout will be used.
|
|
194
|
+
:return: A list of votes.
|
|
195
|
+
:raise httpx.HTTPStatusError: If the response status code is not successful.
|
|
196
|
+
"""
|
|
197
|
+
if year is None:
|
|
198
|
+
year = datetime.now().year
|
|
199
|
+
|
|
200
|
+
if timeout is None:
|
|
201
|
+
timeout = self._client.timeout
|
|
202
|
+
|
|
203
|
+
url = self._base_url.join(f"votes/{year}/")
|
|
204
|
+
votes: list[dict] = await self._get(url, timeout=timeout)
|
|
205
|
+
|
|
206
|
+
return [Vote(**vote) for vote in votes]
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "ctftime_api"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.2"
|
|
4
4
|
description = "CTFTime API wrapper"
|
|
5
5
|
authors = [
|
|
6
6
|
{name = "John Toniutti",email = "john.toniutti@gmail.com"}
|
|
7
7
|
]
|
|
8
8
|
keywords = ["ctftime", "api", "wrapper"]
|
|
9
9
|
classifiers = [
|
|
10
|
-
"Development Status ::
|
|
10
|
+
"Development Status :: 4 - Beta",
|
|
11
11
|
"Intended Audience :: Developers",
|
|
12
12
|
"License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)",
|
|
13
|
+
"Framework :: Pydantic",
|
|
14
|
+
"Operating System :: OS Independent",
|
|
13
15
|
"Programming Language :: Python :: 3",
|
|
14
16
|
"Programming Language :: Python :: 3.11",
|
|
15
17
|
"Programming Language :: Python :: 3.12",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|