ctftime-api 0.1.6__tar.gz → 0.3.0__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.6 → ctftime_api-0.3.0}/.github/workflows/codeql.yml +3 -3
- {ctftime_api-0.1.6 → ctftime_api-0.3.0}/.github/workflows/docs.yml +3 -3
- {ctftime_api-0.1.6 → ctftime_api-0.3.0}/.github/workflows/publish.yml +5 -5
- {ctftime_api-0.1.6 → ctftime_api-0.3.0}/.github/workflows/test.yml +2 -2
- {ctftime_api-0.1.6 → ctftime_api-0.3.0}/PKG-INFO +11 -12
- {ctftime_api-0.1.6 → ctftime_api-0.3.0}/README.md +2 -0
- {ctftime_api-0.1.6 → ctftime_api-0.3.0}/ctftime_api/client.py +16 -19
- ctftime_api-0.3.0/ctftime_api/models/country.py +259 -0
- ctftime_api-0.3.0/ctftime_api/models/duration.py +12 -0
- ctftime_api-0.3.0/ctftime_api/models/event.py +53 -0
- ctftime_api-0.3.0/ctftime_api/models/rating.py +16 -0
- ctftime_api-0.3.0/ctftime_api/models/team.py +61 -0
- ctftime_api-0.3.0/ctftime_api/models/vote.py +16 -0
- {ctftime_api-0.1.6 → ctftime_api-0.3.0}/pyproject.toml +12 -14
- {ctftime_api-0.1.6 → ctftime_api-0.3.0}/tests/test_client.py +16 -15
- ctftime_api-0.3.0/uv.lock +2354 -0
- ctftime_api-0.1.6/ctftime_api/models/duration.py +0 -8
- ctftime_api-0.1.6/ctftime_api/models/event.py +0 -42
- ctftime_api-0.1.6/ctftime_api/models/rating.py +0 -12
- ctftime_api-0.1.6/ctftime_api/models/team.py +0 -50
- ctftime_api-0.1.6/ctftime_api/models/vote.py +0 -13
- ctftime_api-0.1.6/uv.lock +0 -1492
- {ctftime_api-0.1.6 → ctftime_api-0.3.0}/.github/dependabot.yml +0 -0
- {ctftime_api-0.1.6 → ctftime_api-0.3.0}/.github/workflows/cleanup.yml +0 -0
- {ctftime_api-0.1.6 → ctftime_api-0.3.0}/.gitignore +0 -0
- {ctftime_api-0.1.6 → ctftime_api-0.3.0}/LICENSE +0 -0
- {ctftime_api-0.1.6 → ctftime_api-0.3.0}/ctftime_api/__init__.py +0 -0
- {ctftime_api-0.1.6 → ctftime_api-0.3.0}/ctftime_api/models/__init__.py +0 -0
- {ctftime_api-0.1.6 → ctftime_api-0.3.0}/ctftime_api/py.typed +0 -0
- {ctftime_api-0.1.6 → ctftime_api-0.3.0}/docs/api/client.md +0 -0
- {ctftime_api-0.1.6 → ctftime_api-0.3.0}/docs/api/models/duration.md +0 -0
- {ctftime_api-0.1.6 → ctftime_api-0.3.0}/docs/api/models/event.md +0 -0
- {ctftime_api-0.1.6 → ctftime_api-0.3.0}/docs/api/models/rating.md +0 -0
- {ctftime_api-0.1.6 → ctftime_api-0.3.0}/docs/api/models/team.md +0 -0
- {ctftime_api-0.1.6 → ctftime_api-0.3.0}/docs/api/models/vote.md +0 -0
- {ctftime_api-0.1.6 → ctftime_api-0.3.0}/docs/assets/fonts/inter/italic.woff2 +0 -0
- {ctftime_api-0.1.6 → ctftime_api-0.3.0}/docs/assets/fonts/inter/italic_ext.woff2 +0 -0
- {ctftime_api-0.1.6 → ctftime_api-0.3.0}/docs/assets/fonts/inter/normal.woff2 +0 -0
- {ctftime_api-0.1.6 → ctftime_api-0.3.0}/docs/assets/fonts/inter/normal_ext.woff2 +0 -0
- {ctftime_api-0.1.6 → ctftime_api-0.3.0}/docs/assets/fonts/jetbrains_mono/normal.woff2 +0 -0
- {ctftime_api-0.1.6 → ctftime_api-0.3.0}/docs/assets/fonts/jetbrains_mono/normal_ext.woff2 +0 -0
- {ctftime_api-0.1.6 → ctftime_api-0.3.0}/docs/index.md +0 -0
- {ctftime_api-0.1.6 → ctftime_api-0.3.0}/docs/stylesheets/fonts.css +0 -0
- {ctftime_api-0.1.6 → ctftime_api-0.3.0}/examples/test_methods/main.py +0 -0
- {ctftime_api-0.1.6 → ctftime_api-0.3.0}/justfile +0 -0
- {ctftime_api-0.1.6 → ctftime_api-0.3.0}/mkdocs.yml +0 -0
- {ctftime_api-0.1.6 → ctftime_api-0.3.0}/tests/__init__.py +0 -0
|
@@ -21,17 +21,17 @@ jobs:
|
|
|
21
21
|
build-mode: none
|
|
22
22
|
steps:
|
|
23
23
|
- name: Checkout repository
|
|
24
|
-
uses: actions/checkout@
|
|
24
|
+
uses: actions/checkout@v6
|
|
25
25
|
|
|
26
26
|
# Initializes the CodeQL tools for scanning.
|
|
27
27
|
- name: Initialize CodeQL
|
|
28
|
-
uses: github/codeql-action/init@
|
|
28
|
+
uses: github/codeql-action/init@v4
|
|
29
29
|
with:
|
|
30
30
|
languages: ${{ matrix.language }}
|
|
31
31
|
build-mode: ${{ matrix.build-mode }}
|
|
32
32
|
queries: security-extended,security-and-quality
|
|
33
33
|
|
|
34
34
|
- name: Perform CodeQL Analysis
|
|
35
|
-
uses: github/codeql-action/analyze@
|
|
35
|
+
uses: github/codeql-action/analyze@v4
|
|
36
36
|
with:
|
|
37
37
|
category: "/language:${{matrix.language}}"
|
|
@@ -21,10 +21,10 @@ jobs:
|
|
|
21
21
|
|
|
22
22
|
steps:
|
|
23
23
|
- name: Checkout code
|
|
24
|
-
uses: actions/checkout@
|
|
24
|
+
uses: actions/checkout@v6
|
|
25
25
|
|
|
26
26
|
- name: Install uv
|
|
27
|
-
uses: astral-sh/setup-uv@
|
|
27
|
+
uses: astral-sh/setup-uv@v7
|
|
28
28
|
with:
|
|
29
29
|
enable-cache: true
|
|
30
30
|
cache-dependency-glob: "uv.lock"
|
|
@@ -46,7 +46,7 @@ jobs:
|
|
|
46
46
|
- name: Upload docs
|
|
47
47
|
if: ${{ (github.event.inputs.upload || github.ref_type == 'tag') }}
|
|
48
48
|
id: deployment
|
|
49
|
-
uses: actions/upload-pages-artifact@
|
|
49
|
+
uses: actions/upload-pages-artifact@v4
|
|
50
50
|
with:
|
|
51
51
|
path: site/
|
|
52
52
|
|
|
@@ -27,10 +27,10 @@ jobs:
|
|
|
27
27
|
|
|
28
28
|
steps:
|
|
29
29
|
- name: Checkout code
|
|
30
|
-
uses: actions/checkout@
|
|
30
|
+
uses: actions/checkout@v6
|
|
31
31
|
|
|
32
32
|
- name: Install uv
|
|
33
|
-
uses: astral-sh/setup-uv@
|
|
33
|
+
uses: astral-sh/setup-uv@v7
|
|
34
34
|
with:
|
|
35
35
|
enable-cache: true
|
|
36
36
|
cache-dependency-glob: "uv.lock"
|
|
@@ -50,13 +50,13 @@ jobs:
|
|
|
50
50
|
- name: Sign build artifacts
|
|
51
51
|
id: sign
|
|
52
52
|
if: ${{ (github.event.inputs.upload || github.ref_type == 'tag') }}
|
|
53
|
-
uses: actions/attest-build-provenance@
|
|
53
|
+
uses: actions/attest-build-provenance@v3
|
|
54
54
|
with:
|
|
55
55
|
subject-path: "dist/*"
|
|
56
56
|
|
|
57
57
|
- name: Upload build artifacts
|
|
58
58
|
id: upload
|
|
59
|
-
uses: actions/upload-artifact@
|
|
59
|
+
uses: actions/upload-artifact@v6
|
|
60
60
|
with:
|
|
61
61
|
name: release-dists
|
|
62
62
|
retention-days: 1
|
|
@@ -78,7 +78,7 @@ jobs:
|
|
|
78
78
|
steps:
|
|
79
79
|
- name: Retrieve release distributions
|
|
80
80
|
id: download
|
|
81
|
-
uses: actions/download-artifact@
|
|
81
|
+
uses: actions/download-artifact@v7
|
|
82
82
|
with:
|
|
83
83
|
name: release-dists
|
|
84
84
|
path: dist/
|
|
@@ -22,10 +22,10 @@ jobs:
|
|
|
22
22
|
contents: read
|
|
23
23
|
|
|
24
24
|
steps:
|
|
25
|
-
- uses: actions/checkout@
|
|
25
|
+
- uses: actions/checkout@v6
|
|
26
26
|
|
|
27
27
|
- name: Install uv
|
|
28
|
-
uses: astral-sh/setup-uv@
|
|
28
|
+
uses: astral-sh/setup-uv@v7
|
|
29
29
|
with:
|
|
30
30
|
enable-cache: true
|
|
31
31
|
cache-dependency-glob: "uv.lock"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: ctftime_api
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: CTFTime API wrapper
|
|
5
5
|
Project-URL: repository, https://github.com/jotonedev/ctftime_api
|
|
6
6
|
Project-URL: documentation, https://jotonedev.github.io/ctftime_api/
|
|
@@ -20,16 +20,13 @@ Classifier: Topic :: Internet :: WWW/HTTP
|
|
|
20
20
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
21
21
|
Classifier: Typing :: Typed
|
|
22
22
|
Requires-Python: >=3.11
|
|
23
|
-
Requires-Dist:
|
|
24
|
-
Requires-Dist:
|
|
25
|
-
Requires-Dist:
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
Requires-Dist:
|
|
30
|
-
Requires-Dist: h2<5.0.0,>=4.2.0; extra == 'speed'
|
|
31
|
-
Requires-Dist: uvloop<1.0.0,>=0.21.0; (sys_platform != 'win32') and extra == 'speed'
|
|
32
|
-
Requires-Dist: zstandard<0.24.0,>=0.23.0; extra == 'speed'
|
|
23
|
+
Requires-Dist: dataclasses-json==0.6.7
|
|
24
|
+
Requires-Dist: httpx[http2]<0.29.0,>=0.28.1
|
|
25
|
+
Requires-Dist: yarl>=1.20.1
|
|
26
|
+
Provides-Extra: brotli
|
|
27
|
+
Requires-Dist: httpx[brotli]; extra == 'brotli'
|
|
28
|
+
Provides-Extra: zstd
|
|
29
|
+
Requires-Dist: httpx[zstd]; extra == 'zstd'
|
|
33
30
|
Description-Content-Type: text/markdown
|
|
34
31
|
|
|
35
32
|
# ctftime_api
|
|
@@ -63,6 +60,7 @@ Create a client to interact with the CTFTime API. For example, to get the top te
|
|
|
63
60
|
import asyncio
|
|
64
61
|
from ctftime_api.client import CTFTimeClient
|
|
65
62
|
|
|
63
|
+
|
|
66
64
|
async def main():
|
|
67
65
|
client = CTFTimeClient()
|
|
68
66
|
top_teams = await client.get_top_teams_per_year()
|
|
@@ -70,6 +68,7 @@ async def main():
|
|
|
70
68
|
print(f"{team.name} (ID: {team.id}) - Points: {team.points}")
|
|
71
69
|
await client.close()
|
|
72
70
|
|
|
71
|
+
|
|
73
72
|
asyncio.run(main())
|
|
74
73
|
```
|
|
75
74
|
|
|
@@ -29,6 +29,7 @@ Create a client to interact with the CTFTime API. For example, to get the top te
|
|
|
29
29
|
import asyncio
|
|
30
30
|
from ctftime_api.client import CTFTimeClient
|
|
31
31
|
|
|
32
|
+
|
|
32
33
|
async def main():
|
|
33
34
|
client = CTFTimeClient()
|
|
34
35
|
top_teams = await client.get_top_teams_per_year()
|
|
@@ -36,6 +37,7 @@ async def main():
|
|
|
36
37
|
print(f"{team.name} (ID: {team.id}) - Points: {team.points}")
|
|
37
38
|
await client.close()
|
|
38
39
|
|
|
40
|
+
|
|
39
41
|
asyncio.run(main())
|
|
40
42
|
```
|
|
41
43
|
|
|
@@ -4,10 +4,10 @@ from typing import Any
|
|
|
4
4
|
|
|
5
5
|
import httpx
|
|
6
6
|
from httpx import URL, Timeout
|
|
7
|
-
from pydantic_extra_types.country import CountryAlpha2
|
|
8
7
|
|
|
8
|
+
from ctftime_api.models.country import CountryCode
|
|
9
9
|
from ctftime_api.models.event import Event, EventResult
|
|
10
|
-
from ctftime_api.models.team import
|
|
10
|
+
from ctftime_api.models.team import Team, TeamComplete, TeamRank
|
|
11
11
|
from ctftime_api.models.vote import Vote
|
|
12
12
|
|
|
13
13
|
__all__ = ["CTFTimeClient"]
|
|
@@ -65,10 +65,10 @@ class CTFTimeClient:
|
|
|
65
65
|
response: dict[str, list[dict]] = await self._get(url, params={"limit": limit})
|
|
66
66
|
teams = response.get(f"{year}", [])
|
|
67
67
|
|
|
68
|
-
return [TeamRank.
|
|
68
|
+
return [TeamRank.from_dict(team) for team in teams]
|
|
69
69
|
|
|
70
70
|
async def get_top_team_by_country(
|
|
71
|
-
self, country: str |
|
|
71
|
+
self, country: str | CountryCode
|
|
72
72
|
) -> list[TeamRank]:
|
|
73
73
|
"""
|
|
74
74
|
Get the top teams in the leaderboard for a specific country.
|
|
@@ -78,18 +78,15 @@ class CTFTimeClient:
|
|
|
78
78
|
:raise httpx.HTTPStatusError: If the response status code is not successful.
|
|
79
79
|
:raise ValueError: If the country is not a two-letter country code or a pycountry Country object.
|
|
80
80
|
"""
|
|
81
|
-
if isinstance(country,
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
raise ValueError(
|
|
86
|
-
"Country must be a two-letter country code or a pycountry Country object."
|
|
87
|
-
)
|
|
81
|
+
if isinstance(country, str) and len(country) != 2:
|
|
82
|
+
raise ValueError(
|
|
83
|
+
"Country must be a two-letter country code or a pycountry Country object."
|
|
84
|
+
)
|
|
88
85
|
|
|
89
86
|
url = self._base_url.join("top-by-country/").join(f"{country}/")
|
|
90
87
|
teams: list[dict[str, Any]] = await self._get(url)
|
|
91
88
|
|
|
92
|
-
return [TeamRank.
|
|
89
|
+
return [TeamRank.from_dict(team) for team in teams]
|
|
93
90
|
|
|
94
91
|
async def get_events_information(
|
|
95
92
|
self, start: int | datetime, end: int | datetime, limit: int = 10
|
|
@@ -118,7 +115,7 @@ class CTFTimeClient:
|
|
|
118
115
|
url, params={"start": start, "finish": end, "limit": limit}
|
|
119
116
|
)
|
|
120
117
|
|
|
121
|
-
return [Event.
|
|
118
|
+
return [Event.from_dict(event) for event in events]
|
|
122
119
|
|
|
123
120
|
async def get_event_information(self, event_id: int) -> Event:
|
|
124
121
|
"""
|
|
@@ -130,7 +127,7 @@ class CTFTimeClient:
|
|
|
130
127
|
url = self._base_url.join(f"events/{event_id}/")
|
|
131
128
|
event: dict[str, Any] = await self._get(url)
|
|
132
129
|
|
|
133
|
-
return Event.
|
|
130
|
+
return Event.from_dict(event)
|
|
134
131
|
|
|
135
132
|
async def get_teams_information(
|
|
136
133
|
self, limit: int = 100, offset: int = 0
|
|
@@ -149,7 +146,7 @@ class CTFTimeClient:
|
|
|
149
146
|
)
|
|
150
147
|
teams: list[dict[str, Any]] = response.get("results", [])
|
|
151
148
|
|
|
152
|
-
return [Team.
|
|
149
|
+
return [Team.from_dict(team) for team in teams]
|
|
153
150
|
|
|
154
151
|
async def get_team_information(self, team_id: int) -> TeamComplete:
|
|
155
152
|
"""
|
|
@@ -161,7 +158,7 @@ class CTFTimeClient:
|
|
|
161
158
|
url = self._base_url.join(f"teams/{team_id}/")
|
|
162
159
|
team: dict[str, Any] = await self._get(url)
|
|
163
160
|
|
|
164
|
-
return TeamComplete.
|
|
161
|
+
return TeamComplete.from_dict(team)
|
|
165
162
|
|
|
166
163
|
async def get_event_results(
|
|
167
164
|
self, year: int | None = None
|
|
@@ -181,12 +178,12 @@ class CTFTimeClient:
|
|
|
181
178
|
event: dict[str, dict] = await self._get(url)
|
|
182
179
|
|
|
183
180
|
return {
|
|
184
|
-
int(ctf_id): EventResult(
|
|
181
|
+
int(ctf_id): EventResult.from_dict(result)
|
|
185
182
|
for ctf_id, result in event.items()
|
|
186
183
|
}
|
|
187
184
|
|
|
188
185
|
async def get_votes_per_year(
|
|
189
|
-
self, year: int | None, timeout: Timeout |
|
|
186
|
+
self, year: int | None, timeout: Timeout | float | None = None
|
|
190
187
|
) -> list[Vote]:
|
|
191
188
|
"""
|
|
192
189
|
Get the votes for a specific year.
|
|
@@ -206,4 +203,4 @@ class CTFTimeClient:
|
|
|
206
203
|
url = self._base_url.join(f"votes/{year}/")
|
|
207
204
|
votes: list[dict] = await self._get(url, timeout=timeout)
|
|
208
205
|
|
|
209
|
-
return [Vote(
|
|
206
|
+
return [Vote.from_dict(vote) for vote in votes]
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
from enum import StrEnum
|
|
2
|
+
|
|
3
|
+
__all__ = ["CountryCode"]
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class CountryCode(StrEnum):
|
|
7
|
+
"""
|
|
8
|
+
An enum for ISO 3166-1 alpha-2 country codes.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
AFGHANISTAN = "AF"
|
|
12
|
+
ALAND_ISLANDS = "AX"
|
|
13
|
+
ALBANIA = "AL"
|
|
14
|
+
ALGERIA = "DZ"
|
|
15
|
+
AMERICAN_SAMOA = "AS"
|
|
16
|
+
ANDORRA = "AD"
|
|
17
|
+
ANGOLA = "AO"
|
|
18
|
+
ANGUILLA = "AI"
|
|
19
|
+
ANTARCTICA = "AQ"
|
|
20
|
+
ANTIGUA_AND_BARBUDA = "AG"
|
|
21
|
+
ARGENTINA = "AR"
|
|
22
|
+
ARMENIA = "AM"
|
|
23
|
+
ARUBA = "AW"
|
|
24
|
+
AUSTRALIA = "AU"
|
|
25
|
+
AUSTRIA = "AT"
|
|
26
|
+
AZERBAIJAN = "AZ"
|
|
27
|
+
BAHAMAS = "BS"
|
|
28
|
+
BAHRAIN = "BH"
|
|
29
|
+
BANGLADESH = "BD"
|
|
30
|
+
BARBADOS = "BB"
|
|
31
|
+
BELARUS = "BY"
|
|
32
|
+
BELGIUM = "BE"
|
|
33
|
+
BELIZE = "BZ"
|
|
34
|
+
BENIN = "BJ"
|
|
35
|
+
BERMUDA = "BM"
|
|
36
|
+
BHUTAN = "BT"
|
|
37
|
+
BOLIVIA_PLURINATIONAL_STATE_OF = "BO"
|
|
38
|
+
BONAIRE_SINT_EUSTATIUS_AND_SABA = "BQ"
|
|
39
|
+
BOSNIA_AND_HERZEGOVINA = "BA"
|
|
40
|
+
BOTSWANA = "BW"
|
|
41
|
+
BOUVET_ISLAND = "BV"
|
|
42
|
+
BRAZIL = "BR"
|
|
43
|
+
BRITISH_INDIAN_OCEAN_TERRITORY = "IO"
|
|
44
|
+
BRUNEI_DARUSSALAM = "BN"
|
|
45
|
+
BULGARIA = "BG"
|
|
46
|
+
BURKINA_FASO = "BF"
|
|
47
|
+
BURUNDI = "BI"
|
|
48
|
+
CABO_VERDE = "CV"
|
|
49
|
+
CAMBODIA = "KH"
|
|
50
|
+
CAMEROON = "CM"
|
|
51
|
+
CANADA = "CA"
|
|
52
|
+
CAYMAN_ISLANDS = "KY"
|
|
53
|
+
CENTRAL_AFRICAN_REPUBLIC = "CF"
|
|
54
|
+
CHAD = "TD"
|
|
55
|
+
CHILE = "CL"
|
|
56
|
+
CHINA = "CN"
|
|
57
|
+
CHRISTMAS_ISLAND = "CX"
|
|
58
|
+
COCOS_KEELING_ISLANDS = "CC"
|
|
59
|
+
COLOMBIA = "CO"
|
|
60
|
+
COMOROS = "KM"
|
|
61
|
+
CONGO_THE_DEMOCRATIC_REPUBLIC_OF_THE = "CD"
|
|
62
|
+
CONGO = "CG"
|
|
63
|
+
COOK_ISLANDS = "CK"
|
|
64
|
+
COSTA_RICA = "CR"
|
|
65
|
+
COTE_D_IVOIRE = "CI"
|
|
66
|
+
CROATIA = "HR"
|
|
67
|
+
CUBA = "CU"
|
|
68
|
+
CURACAO = "CW"
|
|
69
|
+
CYPRUS = "CY"
|
|
70
|
+
CZECHIA = "CZ"
|
|
71
|
+
DENMARK = "DK"
|
|
72
|
+
DJIBOUTI = "DJ"
|
|
73
|
+
DOMINICA = "DM"
|
|
74
|
+
DOMINICAN_REPUBLIC = "DO"
|
|
75
|
+
ECUADOR = "EC"
|
|
76
|
+
EGYPT = "EG"
|
|
77
|
+
EL_SALVADOR = "SV"
|
|
78
|
+
EQUATORIAL_GUINEA = "GQ"
|
|
79
|
+
ERITREA = "ER"
|
|
80
|
+
ESTONIA = "EE"
|
|
81
|
+
ESWATINI = "SZ"
|
|
82
|
+
ETHIOPIA = "ET"
|
|
83
|
+
FALKLAND_ISLANDS_MALVINAS = "FK"
|
|
84
|
+
FAROE_ISLANDS = "FO"
|
|
85
|
+
FIJI = "FJ"
|
|
86
|
+
FINLAND = "FI"
|
|
87
|
+
FRANCE = "FR"
|
|
88
|
+
FRENCH_GUIANA = "GF"
|
|
89
|
+
FRENCH_POLYNESIA = "PF"
|
|
90
|
+
FRENCH_SOUTHERN_TERRITORIES = "TF"
|
|
91
|
+
GABON = "GA"
|
|
92
|
+
GAMBIA = "GM"
|
|
93
|
+
GEORGIA = "GE"
|
|
94
|
+
GERMANY = "DE"
|
|
95
|
+
GHANA = "GH"
|
|
96
|
+
GIBRALTAR = "GI"
|
|
97
|
+
GREECE = "GR"
|
|
98
|
+
GREENLAND = "GL"
|
|
99
|
+
GRENADA = "GD"
|
|
100
|
+
GUADELOUPE = "GP"
|
|
101
|
+
GUAM = "GU"
|
|
102
|
+
GUATEMALA = "GT"
|
|
103
|
+
GUERNSEY = "GG"
|
|
104
|
+
GUINEA = "GN"
|
|
105
|
+
GUINEA_BISSAU = "GW"
|
|
106
|
+
GUYANA = "GY"
|
|
107
|
+
HAITI = "HT"
|
|
108
|
+
HEARD_ISLAND_AND_MCDONALD_ISLANDS = "HM"
|
|
109
|
+
HOLY_SEE = "VA"
|
|
110
|
+
HONDURAS = "HN"
|
|
111
|
+
HONG_KONG = "HK"
|
|
112
|
+
HUNGARY = "HU"
|
|
113
|
+
ICELAND = "IS"
|
|
114
|
+
INDIA = "IN"
|
|
115
|
+
INDONESIA = "ID"
|
|
116
|
+
IRAN_ISLAMIC_REPUBLIC_OF = "IR"
|
|
117
|
+
IRAQ = "IQ"
|
|
118
|
+
IRELAND = "IE"
|
|
119
|
+
ISLE_OF_MAN = "IM"
|
|
120
|
+
ISRAEL = "IL"
|
|
121
|
+
ITALY = "IT"
|
|
122
|
+
JAMAICA = "JM"
|
|
123
|
+
JAPAN = "JP"
|
|
124
|
+
JERSEY = "JE"
|
|
125
|
+
JORDAN = "JO"
|
|
126
|
+
KAZAKHSTAN = "KZ"
|
|
127
|
+
KENYA = "KE"
|
|
128
|
+
KIRIBATI = "KI"
|
|
129
|
+
KOREA_THE_DEMOCRATIC_PEOPLES_REPUBLIC_OF = "KP"
|
|
130
|
+
KOREA_THE_REPUBLIC_OF = "KR"
|
|
131
|
+
KUWAIT = "KW"
|
|
132
|
+
KYRGYZSTAN = "KG"
|
|
133
|
+
LAO_PEOPLES_DEMOCRATIC_REPUBLIC = "LA"
|
|
134
|
+
LATVIA = "LV"
|
|
135
|
+
LEBANON = "LB"
|
|
136
|
+
LESOTHO = "LS"
|
|
137
|
+
LIBERIA = "LR"
|
|
138
|
+
LIBYA = "LY"
|
|
139
|
+
LIECHTENSTEIN = "LI"
|
|
140
|
+
LITHUANIA = "LT"
|
|
141
|
+
LUXEMBOURG = "LU"
|
|
142
|
+
MACAO = "MO"
|
|
143
|
+
REPUBLIC_OF_NORTH_MACEDONIA = "MK"
|
|
144
|
+
MADAGASCAR = "MG"
|
|
145
|
+
MALAWI = "MW"
|
|
146
|
+
MALAYSIA = "MY"
|
|
147
|
+
MALDIVES = "MV"
|
|
148
|
+
MALI = "ML"
|
|
149
|
+
MALTA = "MT"
|
|
150
|
+
MARSHALL_ISLANDS = "MH"
|
|
151
|
+
MARTINIQUE = "MQ"
|
|
152
|
+
MAURITANIA = "MR"
|
|
153
|
+
MAURITIUS = "MU"
|
|
154
|
+
MAYOTTE = "YT"
|
|
155
|
+
MEXICO = "MX"
|
|
156
|
+
MICRONESIA_FEDERATED_STATES_OF = "FM"
|
|
157
|
+
MOLDOVA_THE_REPUBLIC_OF = "MD"
|
|
158
|
+
MONACO = "MC"
|
|
159
|
+
MONGOLIA = "MN"
|
|
160
|
+
MONTENEGRO = "ME"
|
|
161
|
+
MONTSERRAT = "MS"
|
|
162
|
+
MOROCCO = "MA"
|
|
163
|
+
MOZAMBIQUE = "MZ"
|
|
164
|
+
MYANMAR = "MM"
|
|
165
|
+
NAMIBIA = "NA"
|
|
166
|
+
NAURU = "NR"
|
|
167
|
+
NEPAL = "NP"
|
|
168
|
+
NETHERLANDS = "NL"
|
|
169
|
+
NEW_CALEDONIA = "NC"
|
|
170
|
+
NEW_ZEALAND = "NZ"
|
|
171
|
+
NICARAGUA = "NI"
|
|
172
|
+
NIGER = "NE"
|
|
173
|
+
NIGERIA = "NG"
|
|
174
|
+
NIUE = "NU"
|
|
175
|
+
NORFOLK_ISLAND = "NF"
|
|
176
|
+
NORTHERN_MARIANA_ISLANDS = "MP"
|
|
177
|
+
NORWAY = "NO"
|
|
178
|
+
OMAN = "OM"
|
|
179
|
+
PAKISTAN = "PK"
|
|
180
|
+
PALAU = "PW"
|
|
181
|
+
PALESTINE_STATE_OF = "PS"
|
|
182
|
+
PANAMA = "PA"
|
|
183
|
+
PAPUA_NEW_GUINEA = "PG"
|
|
184
|
+
PARAGUAY = "PY"
|
|
185
|
+
PERU = "PE"
|
|
186
|
+
PHILIPPINES = "PH"
|
|
187
|
+
PITCAIRN = "PN"
|
|
188
|
+
POLAND = "PL"
|
|
189
|
+
PORTUGAL = "PT"
|
|
190
|
+
PUERTO_RICO = "PR"
|
|
191
|
+
QATAR = "QA"
|
|
192
|
+
REUNION = "RE"
|
|
193
|
+
ROMANIA = "RO"
|
|
194
|
+
RUSSIAN_FEDERATION = "RU"
|
|
195
|
+
RWANDA = "RW"
|
|
196
|
+
SAINT_BARTHELEMY = "BL"
|
|
197
|
+
SAINT_HELENA_ASCENSION_AND_TRISTAN_DA_CUNHA = "SH"
|
|
198
|
+
SAINT_KITTS_AND_NEVIS = "KN"
|
|
199
|
+
SAINT_LUCIA = "LC"
|
|
200
|
+
SAINT_MARTIN_FRENCH_PART = "MF"
|
|
201
|
+
SAINT_PIERRE_AND_MIQUELON = "PM"
|
|
202
|
+
SAINT_VINCENT_AND_THE_GRENADINES = "VC"
|
|
203
|
+
SAMOA = "WS"
|
|
204
|
+
SAN_MARINO = "SM"
|
|
205
|
+
SAO_TOME_AND_PRINCIPE = "ST"
|
|
206
|
+
SAUDI_ARABIA = "SA"
|
|
207
|
+
SENEGAL = "SN"
|
|
208
|
+
SERBIA = "RS"
|
|
209
|
+
SEYCHELLES = "SC"
|
|
210
|
+
SIERRA_LEONE = "SL"
|
|
211
|
+
SINGAPORE = "SG"
|
|
212
|
+
SINT_MAARTEN_DUTCH_PART = "SX"
|
|
213
|
+
SLOVAKIA = "SK"
|
|
214
|
+
SLOVENIA = "SI"
|
|
215
|
+
SOLOMON_ISLANDS = "SB"
|
|
216
|
+
SOMALIA = "SO"
|
|
217
|
+
SOUTH_AFRICA = "ZA"
|
|
218
|
+
SOUTH_GEORGIA_AND_THE_SOUTH_SANDWICH_ISLANDS = "GS"
|
|
219
|
+
SOUTH_SUDAN = "SS"
|
|
220
|
+
SPAIN = "ES"
|
|
221
|
+
SRI_LANKA = "LK"
|
|
222
|
+
SUDAN = "SD"
|
|
223
|
+
SURINAME = "SR"
|
|
224
|
+
SVALBARD_AND_JAN_MAYEN = "SJ"
|
|
225
|
+
SWEDEN = "SE"
|
|
226
|
+
SWITZERLAND = "CH"
|
|
227
|
+
SYRIAN_ARAB_REPUBLIC = "SY"
|
|
228
|
+
TAIWAN_PROVINCE_OF_CHINA = "TW"
|
|
229
|
+
TAJIKISTAN = "TJ"
|
|
230
|
+
TANZANIA_UNITED_REPUBLIC_OF = "TZ"
|
|
231
|
+
THAILAND = "TH"
|
|
232
|
+
TIMOR_LESTE = "TL"
|
|
233
|
+
TOGO = "TG"
|
|
234
|
+
TOKELAU = "TK"
|
|
235
|
+
TONGA = "TO"
|
|
236
|
+
TRINIDAD_AND_TOBAGO = "TT"
|
|
237
|
+
TUNISIA = "TN"
|
|
238
|
+
TURKEY = "TR"
|
|
239
|
+
TURKMENISTAN = "TM"
|
|
240
|
+
TURKS_AND_CAICOS_ISLANDS = "TC"
|
|
241
|
+
TUVALU = "TV"
|
|
242
|
+
UGANDA = "UG"
|
|
243
|
+
UKRAINE = "UA"
|
|
244
|
+
UNITED_ARAB_EMIRATES = "AE"
|
|
245
|
+
UNITED_KINGDOM_OF_GREAT_BRITAIN_AND_NORTHERN_IRELAND = "GB"
|
|
246
|
+
UNITED_STATES_MINOR_OUTLYING_ISLANDS = "UM"
|
|
247
|
+
UNITED_STATES_OF_AMERICA = "US"
|
|
248
|
+
URUGUAY = "UY"
|
|
249
|
+
UZBEKISTAN = "UZ"
|
|
250
|
+
VANUATU = "VU"
|
|
251
|
+
VENEZUELA_BOLIVARIAN_REPUBLIC_OF = "VE"
|
|
252
|
+
VIET_NAM = "VN"
|
|
253
|
+
VIRGIN_ISLANDS_BRITISH = "VG"
|
|
254
|
+
VIRGIN_ISLANDS_US = "VI"
|
|
255
|
+
WALLIS_AND_FUTUNA = "WF"
|
|
256
|
+
WESTERN_SAHARA = "EH"
|
|
257
|
+
YEMEN = "YE"
|
|
258
|
+
ZAMBIA = "ZM"
|
|
259
|
+
ZIMBABWE = "ZW"
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
|
|
3
|
+
from dataclasses_json import DataClassJsonMixin, Undefined, dataclass_json
|
|
4
|
+
|
|
5
|
+
__all__ = ["Duration"]
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass_json(undefined=Undefined.EXCLUDE)
|
|
9
|
+
@dataclass(frozen=True)
|
|
10
|
+
class Duration(DataClassJsonMixin):
|
|
11
|
+
hours: int
|
|
12
|
+
days: int
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
|
|
4
|
+
from dataclasses_json import DataClassJsonMixin, Undefined, dataclass_json
|
|
5
|
+
from yarl import URL
|
|
6
|
+
|
|
7
|
+
from .duration import Duration
|
|
8
|
+
from .team import BaseTeam, TeamResult
|
|
9
|
+
|
|
10
|
+
__all__ = ["Event", "EventResult"]
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass_json(undefined=Undefined.EXCLUDE)
|
|
14
|
+
@dataclass(frozen=True)
|
|
15
|
+
class Event(DataClassJsonMixin):
|
|
16
|
+
"""Represents a CTF event."""
|
|
17
|
+
|
|
18
|
+
organizers: list[BaseTeam]
|
|
19
|
+
ctftime_url: URL
|
|
20
|
+
ctf_id: int
|
|
21
|
+
weight: float
|
|
22
|
+
duration: Duration
|
|
23
|
+
live_feed: str
|
|
24
|
+
logo: URL | str
|
|
25
|
+
id: int
|
|
26
|
+
title: str
|
|
27
|
+
participants: int
|
|
28
|
+
location: str
|
|
29
|
+
description: str
|
|
30
|
+
format: str
|
|
31
|
+
is_votable_now: bool
|
|
32
|
+
prizes: str
|
|
33
|
+
restrictions: str
|
|
34
|
+
url: URL | str
|
|
35
|
+
public_votable: bool
|
|
36
|
+
start: datetime | str
|
|
37
|
+
finish: datetime | str
|
|
38
|
+
|
|
39
|
+
def __post_init__(self):
|
|
40
|
+
if isinstance(self.start, str):
|
|
41
|
+
object.__setattr__(self, "start", datetime.fromisoformat(self.start))
|
|
42
|
+
if isinstance(self.finish, str):
|
|
43
|
+
object.__setattr__(self, "finish", datetime.fromisoformat(self.finish))
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@dataclass_json(undefined=Undefined.EXCLUDE)
|
|
47
|
+
@dataclass(frozen=True)
|
|
48
|
+
class EventResult(DataClassJsonMixin):
|
|
49
|
+
"""Represents a CTF event result."""
|
|
50
|
+
|
|
51
|
+
title: str
|
|
52
|
+
time: datetime
|
|
53
|
+
scores: list[TeamResult]
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
|
|
3
|
+
from dataclasses_json import DataClassJsonMixin, Undefined, dataclass_json
|
|
4
|
+
|
|
5
|
+
__all__ = ["Rating"]
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass_json(undefined=Undefined.EXCLUDE)
|
|
9
|
+
@dataclass(frozen=True)
|
|
10
|
+
class Rating(DataClassJsonMixin):
|
|
11
|
+
"""Represents a CTF team rating."""
|
|
12
|
+
|
|
13
|
+
rating_place: int | None = None
|
|
14
|
+
organizer_points: float | None = None
|
|
15
|
+
rating_points: float | None = None
|
|
16
|
+
country_place: int | None = None
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
|
|
3
|
+
from dataclasses_json import DataClassJsonMixin, Undefined, config, dataclass_json
|
|
4
|
+
from yarl import URL
|
|
5
|
+
|
|
6
|
+
from .country import CountryCode
|
|
7
|
+
from .rating import Rating
|
|
8
|
+
|
|
9
|
+
__all__ = ["BaseTeam", "Team", "TeamComplete", "TeamRank", "TeamResult"]
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass_json(undefined=Undefined.EXCLUDE)
|
|
13
|
+
@dataclass(frozen=True)
|
|
14
|
+
class BaseTeam(DataClassJsonMixin):
|
|
15
|
+
"""Represents a CTF team. Contains only the minimal information."""
|
|
16
|
+
|
|
17
|
+
team_id: int = field(metadata=config(field_name="id"))
|
|
18
|
+
team_name: str = field(metadata=config(field_name="name"))
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass_json(undefined=Undefined.EXCLUDE)
|
|
22
|
+
@dataclass(frozen=True)
|
|
23
|
+
class Team(BaseTeam):
|
|
24
|
+
"""Represents a CTF team"""
|
|
25
|
+
|
|
26
|
+
team_country: CountryCode | None = None
|
|
27
|
+
academic: bool = False
|
|
28
|
+
aliases: list[str] = field(default_factory=list)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@dataclass_json(undefined=Undefined.EXCLUDE)
|
|
32
|
+
@dataclass(frozen=True)
|
|
33
|
+
class TeamRank(BaseTeam):
|
|
34
|
+
"""Represents a CTF team in the leaderboard"""
|
|
35
|
+
|
|
36
|
+
points: float
|
|
37
|
+
country_place: int | None = None
|
|
38
|
+
place: int | None = None
|
|
39
|
+
events: int | None = None
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@dataclass_json(undefined=Undefined.EXCLUDE)
|
|
43
|
+
@dataclass(frozen=True)
|
|
44
|
+
class TeamResult(DataClassJsonMixin):
|
|
45
|
+
"""Represents a CTF team result"""
|
|
46
|
+
|
|
47
|
+
team_id: int
|
|
48
|
+
points: float
|
|
49
|
+
place: int
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@dataclass_json(undefined=Undefined.EXCLUDE)
|
|
53
|
+
@dataclass(frozen=True)
|
|
54
|
+
class TeamComplete(Team):
|
|
55
|
+
"""Represents a CTF team with complete information"""
|
|
56
|
+
|
|
57
|
+
primary_alias: str | None = None
|
|
58
|
+
logo: URL | str | None = None
|
|
59
|
+
university: str | None = None
|
|
60
|
+
university_website: URL | str | None = None
|
|
61
|
+
rating: dict[int, Rating | None] = field(default_factory=dict)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
|
|
4
|
+
from dataclasses_json import DataClassJsonMixin, Undefined, dataclass_json
|
|
5
|
+
|
|
6
|
+
__all__ = ["Vote"]
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass_json(undefined=Undefined.EXCLUDE)
|
|
10
|
+
@dataclass(frozen=True)
|
|
11
|
+
class Vote(DataClassJsonMixin):
|
|
12
|
+
event_id: int
|
|
13
|
+
user_id: int
|
|
14
|
+
user_teams: list[int] # list of team IDs
|
|
15
|
+
weight: str
|
|
16
|
+
creation_date: datetime
|