ctftime-api 0.1.5__tar.gz → 0.2.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.
Files changed (47) hide show
  1. {ctftime_api-0.1.5 → ctftime_api-0.2.0}/.github/workflows/codeql.yml +1 -1
  2. {ctftime_api-0.1.5 → ctftime_api-0.2.0}/.github/workflows/docs.yml +2 -2
  3. {ctftime_api-0.1.5 → ctftime_api-0.2.0}/.github/workflows/publish.yml +3 -3
  4. {ctftime_api-0.1.5 → ctftime_api-0.2.0}/.github/workflows/test.yml +1 -1
  5. {ctftime_api-0.1.5 → ctftime_api-0.2.0}/PKG-INFO +8 -11
  6. {ctftime_api-0.1.5 → ctftime_api-0.2.0}/ctftime_api/client.py +12 -14
  7. ctftime_api-0.2.0/ctftime_api/models/country.py +259 -0
  8. ctftime_api-0.2.0/ctftime_api/models/duration.py +12 -0
  9. ctftime_api-0.2.0/ctftime_api/models/event.py +53 -0
  10. ctftime_api-0.2.0/ctftime_api/models/rating.py +16 -0
  11. ctftime_api-0.2.0/ctftime_api/models/team.py +61 -0
  12. ctftime_api-0.2.0/ctftime_api/models/vote.py +16 -0
  13. {ctftime_api-0.1.5 → ctftime_api-0.2.0}/pyproject.toml +10 -12
  14. ctftime_api-0.2.0/tests/__init__.py +0 -0
  15. {ctftime_api-0.1.5 → ctftime_api-0.2.0}/tests/test_client.py +16 -15
  16. ctftime_api-0.2.0/uv.lock +1709 -0
  17. ctftime_api-0.1.5/ctftime_api/models/duration.py +0 -8
  18. ctftime_api-0.1.5/ctftime_api/models/event.py +0 -42
  19. ctftime_api-0.1.5/ctftime_api/models/rating.py +0 -12
  20. ctftime_api-0.1.5/ctftime_api/models/team.py +0 -50
  21. ctftime_api-0.1.5/ctftime_api/models/vote.py +0 -13
  22. ctftime_api-0.1.5/uv.lock +0 -1452
  23. {ctftime_api-0.1.5 → ctftime_api-0.2.0}/.github/dependabot.yml +0 -0
  24. {ctftime_api-0.1.5 → ctftime_api-0.2.0}/.github/workflows/cleanup.yml +0 -0
  25. {ctftime_api-0.1.5 → ctftime_api-0.2.0}/.gitignore +0 -0
  26. {ctftime_api-0.1.5 → ctftime_api-0.2.0}/LICENSE +0 -0
  27. {ctftime_api-0.1.5 → ctftime_api-0.2.0}/README.md +0 -0
  28. {ctftime_api-0.1.5 → ctftime_api-0.2.0}/ctftime_api/__init__.py +0 -0
  29. {ctftime_api-0.1.5 → ctftime_api-0.2.0}/ctftime_api/models/__init__.py +0 -0
  30. /ctftime_api-0.1.5/tests/__init__.py → /ctftime_api-0.2.0/ctftime_api/py.typed +0 -0
  31. {ctftime_api-0.1.5 → ctftime_api-0.2.0}/docs/api/client.md +0 -0
  32. {ctftime_api-0.1.5 → ctftime_api-0.2.0}/docs/api/models/duration.md +0 -0
  33. {ctftime_api-0.1.5 → ctftime_api-0.2.0}/docs/api/models/event.md +0 -0
  34. {ctftime_api-0.1.5 → ctftime_api-0.2.0}/docs/api/models/rating.md +0 -0
  35. {ctftime_api-0.1.5 → ctftime_api-0.2.0}/docs/api/models/team.md +0 -0
  36. {ctftime_api-0.1.5 → ctftime_api-0.2.0}/docs/api/models/vote.md +0 -0
  37. {ctftime_api-0.1.5 → ctftime_api-0.2.0}/docs/assets/fonts/inter/italic.woff2 +0 -0
  38. {ctftime_api-0.1.5 → ctftime_api-0.2.0}/docs/assets/fonts/inter/italic_ext.woff2 +0 -0
  39. {ctftime_api-0.1.5 → ctftime_api-0.2.0}/docs/assets/fonts/inter/normal.woff2 +0 -0
  40. {ctftime_api-0.1.5 → ctftime_api-0.2.0}/docs/assets/fonts/inter/normal_ext.woff2 +0 -0
  41. {ctftime_api-0.1.5 → ctftime_api-0.2.0}/docs/assets/fonts/jetbrains_mono/normal.woff2 +0 -0
  42. {ctftime_api-0.1.5 → ctftime_api-0.2.0}/docs/assets/fonts/jetbrains_mono/normal_ext.woff2 +0 -0
  43. {ctftime_api-0.1.5 → ctftime_api-0.2.0}/docs/index.md +0 -0
  44. {ctftime_api-0.1.5 → ctftime_api-0.2.0}/docs/stylesheets/fonts.css +0 -0
  45. {ctftime_api-0.1.5 → ctftime_api-0.2.0}/examples/test_methods/main.py +0 -0
  46. {ctftime_api-0.1.5 → ctftime_api-0.2.0}/justfile +0 -0
  47. {ctftime_api-0.1.5 → ctftime_api-0.2.0}/mkdocs.yml +0 -0
@@ -21,7 +21,7 @@ jobs:
21
21
  build-mode: none
22
22
  steps:
23
23
  - name: Checkout repository
24
- uses: actions/checkout@v4
24
+ uses: actions/checkout@v5
25
25
 
26
26
  # Initializes the CodeQL tools for scanning.
27
27
  - name: Initialize CodeQL
@@ -21,7 +21,7 @@ jobs:
21
21
 
22
22
  steps:
23
23
  - name: Checkout code
24
- uses: actions/checkout@v4
24
+ uses: actions/checkout@v5
25
25
 
26
26
  - name: Install uv
27
27
  uses: astral-sh/setup-uv@v6
@@ -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@v3
49
+ uses: actions/upload-pages-artifact@v4
50
50
  with:
51
51
  path: site/
52
52
 
@@ -27,7 +27,7 @@ jobs:
27
27
 
28
28
  steps:
29
29
  - name: Checkout code
30
- uses: actions/checkout@v4
30
+ uses: actions/checkout@v5
31
31
 
32
32
  - name: Install uv
33
33
  uses: astral-sh/setup-uv@v6
@@ -50,7 +50,7 @@ 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@v2
53
+ uses: actions/attest-build-provenance@v3
54
54
  with:
55
55
  subject-path: "dist/*"
56
56
 
@@ -78,7 +78,7 @@ jobs:
78
78
  steps:
79
79
  - name: Retrieve release distributions
80
80
  id: download
81
- uses: actions/download-artifact@v4
81
+ uses: actions/download-artifact@v5
82
82
  with:
83
83
  name: release-dists
84
84
  path: dist/
@@ -22,7 +22,7 @@ jobs:
22
22
  contents: read
23
23
 
24
24
  steps:
25
- - uses: actions/checkout@v4
25
+ - uses: actions/checkout@v5
26
26
 
27
27
  - name: Install uv
28
28
  uses: astral-sh/setup-uv@v6
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ctftime_api
3
- Version: 0.1.5
3
+ Version: 0.2.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: httpx<0.29.0,>=0.28.1
24
- Requires-Dist: pycountry<25.0.0,>=24.6.1
25
- Requires-Dist: pydantic-extra-types<3.0.0,>=2.10.2
26
- Requires-Dist: pydantic<3.0.0,>=2.10.6
27
- Provides-Extra: speed
28
- Requires-Dist: brotli<2.0.0,>=1.1.0; (platform_python_implementation == 'CPython') and extra == 'speed'
29
- Requires-Dist: brotlicffi<2.0.0.0,>=1.1.0.0; (platform_python_implementation != 'CPython') and extra == 'speed'
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
@@ -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 TeamRank, Team, TeamComplete
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.model_validate(team) for team in teams]
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 | CountryAlpha2
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,9 +78,7 @@ 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, CountryAlpha2):
82
- country = country
83
- elif isinstance(country, str):
81
+ if isinstance(country, str):
84
82
  if len(country) != 2:
85
83
  raise ValueError(
86
84
  "Country must be a two-letter country code or a pycountry Country object."
@@ -89,7 +87,7 @@ class CTFTimeClient:
89
87
  url = self._base_url.join("top-by-country/").join(f"{country}/")
90
88
  teams: list[dict[str, Any]] = await self._get(url)
91
89
 
92
- return [TeamRank.model_validate(team) for team in teams]
90
+ return [TeamRank.from_dict(team) for team in teams]
93
91
 
94
92
  async def get_events_information(
95
93
  self, start: int | datetime, end: int | datetime, limit: int = 10
@@ -118,7 +116,7 @@ class CTFTimeClient:
118
116
  url, params={"start": start, "finish": end, "limit": limit}
119
117
  )
120
118
 
121
- return [Event.model_validate(event) for event in events]
119
+ return [Event.from_dict(event) for event in events]
122
120
 
123
121
  async def get_event_information(self, event_id: int) -> Event:
124
122
  """
@@ -130,7 +128,7 @@ class CTFTimeClient:
130
128
  url = self._base_url.join(f"events/{event_id}/")
131
129
  event: dict[str, Any] = await self._get(url)
132
130
 
133
- return Event.model_validate(event)
131
+ return Event.from_dict(event)
134
132
 
135
133
  async def get_teams_information(
136
134
  self, limit: int = 100, offset: int = 0
@@ -149,7 +147,7 @@ class CTFTimeClient:
149
147
  )
150
148
  teams: list[dict[str, Any]] = response.get("results", [])
151
149
 
152
- return [Team.model_validate(team) for team in teams]
150
+ return [Team.from_dict(team) for team in teams]
153
151
 
154
152
  async def get_team_information(self, team_id: int) -> TeamComplete:
155
153
  """
@@ -161,7 +159,7 @@ class CTFTimeClient:
161
159
  url = self._base_url.join(f"teams/{team_id}/")
162
160
  team: dict[str, Any] = await self._get(url)
163
161
 
164
- return TeamComplete.model_validate(team)
162
+ return TeamComplete.from_dict(team)
165
163
 
166
164
  async def get_event_results(
167
165
  self, year: int | None = None
@@ -181,7 +179,7 @@ class CTFTimeClient:
181
179
  event: dict[str, dict] = await self._get(url)
182
180
 
183
181
  return {
184
- int(ctf_id): EventResult(**result, ctf_id=int(ctf_id))
182
+ int(ctf_id): EventResult.from_dict(result)
185
183
  for ctf_id, result in event.items()
186
184
  }
187
185
 
@@ -206,4 +204,4 @@ class CTFTimeClient:
206
204
  url = self._base_url.join(f"votes/{year}/")
207
205
  votes: list[dict] = await self._get(url, timeout=timeout)
208
206
 
209
- return [Vote(**vote) for vote in votes]
207
+ 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", "TeamRank", "TeamComplete", "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
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "ctftime_api"
3
- version = "0.1.5"
3
+ version = "0.2.0"
4
4
  description = "CTFTime API wrapper"
5
5
  authors = [{ name = "John Toniutti", email = "john.toniutti@gmail.com" }]
6
6
  requires-python = ">=3.11"
@@ -25,19 +25,17 @@ classifiers = [
25
25
  "Typing :: Typed",
26
26
  ]
27
27
  dependencies = [
28
- "httpx (>=0.28.1,<0.29.0)",
29
- "pydantic (>=2.10.6,<3.0.0)",
30
- "pydantic-extra-types (>=2.10.2,<3.0.0)",
31
- "pycountry (>=24.6.1,<25.0.0)",
28
+ "dataclasses-json==0.6.7",
29
+ "httpx[http2] (>=0.28.1,<0.29.0)",
30
+ "yarl>=1.20.1",
32
31
  ]
33
32
 
34
33
  [project.optional-dependencies]
35
- speed = [
36
- "uvloop (>=0.21.0,<1.0.0) ; sys_platform != 'win32'",
37
- "brotli (>=1.1.0,<2.0.0) ; platform_python_implementation == 'CPython' ",
38
- "brotlicffi (>=1.1.0.0,<2.0.0.0) ; platform_python_implementation != 'CPython' ",
39
- "h2 (>=4.2.0,<5.0.0)",
40
- "zstandard (>=0.23.0,<0.24.0)",
34
+ brotli = [
35
+ "httpx[brotli]",
36
+ ]
37
+ zstd = [
38
+ "httpx[zstd]",
41
39
  ]
42
40
 
43
41
  [project.urls]
@@ -49,7 +47,7 @@ dev = [
49
47
  "ruff~=0.9",
50
48
  "pytest~=8.3",
51
49
  "pytest-cov~=6.1",
52
- "pytest-asyncio~=0.24",
50
+ "pytest-asyncio>=0.24,<2.0",
53
51
  "mypy~=1.11",
54
52
  ]
55
53
  docs = [
File without changes