xcpcio 0.63.5__py3-none-any.whl → 0.63.6__py3-none-any.whl
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.
Potentially problematic release.
This version of xcpcio might be problematic. Click here for more details.
- xcpcio/__version__.py +4 -1
- xcpcio/ccs/__init__.py +2 -2
- xcpcio/ccs/api_server/__init__.py +9 -0
- xcpcio/ccs/api_server/dependencies.py +48 -0
- xcpcio/ccs/api_server/routes/__init__.py +50 -0
- xcpcio/ccs/api_server/routes/access.py +20 -0
- xcpcio/ccs/api_server/routes/awards.py +38 -0
- xcpcio/ccs/api_server/routes/clarifications.py +42 -0
- xcpcio/ccs/api_server/routes/contests.py +74 -0
- xcpcio/ccs/api_server/routes/general.py +32 -0
- xcpcio/ccs/api_server/routes/groups.py +41 -0
- xcpcio/ccs/api_server/routes/judgement_types.py +41 -0
- xcpcio/ccs/api_server/routes/judgements.py +40 -0
- xcpcio/ccs/api_server/routes/languages.py +41 -0
- xcpcio/ccs/api_server/routes/organizations.py +82 -0
- xcpcio/ccs/api_server/routes/problems.py +77 -0
- xcpcio/ccs/api_server/routes/runs.py +40 -0
- xcpcio/ccs/api_server/routes/submissions.py +82 -0
- xcpcio/ccs/api_server/routes/teams.py +82 -0
- xcpcio/ccs/api_server/server.py +83 -0
- xcpcio/ccs/api_server/services/__init__.py +9 -0
- xcpcio/ccs/api_server/services/contest_service.py +408 -0
- {xcpcio-0.63.5.dist-info → xcpcio-0.63.6.dist-info}/METADATA +4 -1
- xcpcio-0.63.6.dist-info/RECORD +33 -0
- xcpcio-0.63.5.dist-info/RECORD +0 -13
- {xcpcio-0.63.5.dist-info → xcpcio-0.63.6.dist-info}/WHEEL +0 -0
- {xcpcio-0.63.5.dist-info → xcpcio-0.63.6.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Contest Service
|
|
3
|
+
|
|
4
|
+
Business logic layer for Contest API operations.
|
|
5
|
+
Handles file reading, data validation, and business operations.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Any, Dict, List, Optional, Union
|
|
11
|
+
|
|
12
|
+
from fastapi import HTTPException
|
|
13
|
+
|
|
14
|
+
from xcpcio.__version__ import __version__
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ContestService:
|
|
18
|
+
"""Service class for contest-related operations"""
|
|
19
|
+
|
|
20
|
+
def __init__(self, contest_package_dir: Path):
|
|
21
|
+
"""
|
|
22
|
+
Initialize the contest service.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
contest_package_dir: Path to the contest package directory
|
|
26
|
+
"""
|
|
27
|
+
self.contest_package_dir = contest_package_dir
|
|
28
|
+
if not self.contest_package_dir.exists():
|
|
29
|
+
raise ValueError(f"Contest package directory does not exist: {contest_package_dir}")
|
|
30
|
+
|
|
31
|
+
# Initialize data indexes for faster lookups
|
|
32
|
+
self._load_indexes()
|
|
33
|
+
|
|
34
|
+
def _load_indexes(self) -> None:
|
|
35
|
+
"""Load and index commonly accessed data for faster lookups"""
|
|
36
|
+
# Load contest data
|
|
37
|
+
self.contest_data = self.load_json_file("contest.json")
|
|
38
|
+
|
|
39
|
+
# Load organizations and create index
|
|
40
|
+
organizations_data = self.load_json_file("organizations.json")
|
|
41
|
+
self.organizations_by_id = {org["id"]: org for org in organizations_data}
|
|
42
|
+
|
|
43
|
+
# Load teams and create index
|
|
44
|
+
teams_data = self.load_json_file("teams.json")
|
|
45
|
+
self.teams_by_id = {team["id"]: team for team in teams_data}
|
|
46
|
+
|
|
47
|
+
# Load problems and create index
|
|
48
|
+
problems_data = self.load_json_file("problems.json")
|
|
49
|
+
self.problems_by_id = {problem["id"]: problem for problem in problems_data}
|
|
50
|
+
|
|
51
|
+
# Load submissions and create index
|
|
52
|
+
submissions_data = self.load_json_file("submissions.json")
|
|
53
|
+
self.submissions_by_id = {submission["id"]: submission for submission in submissions_data}
|
|
54
|
+
|
|
55
|
+
def load_json_file(self, filepath: str) -> Union[Dict[str, Any], List[Any]]:
|
|
56
|
+
"""
|
|
57
|
+
Load JSON data from contest package directory.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
filepath: Relative path to JSON file within contest package
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
Parsed JSON data
|
|
64
|
+
|
|
65
|
+
Raises:
|
|
66
|
+
HTTPException: If file not found or invalid JSON
|
|
67
|
+
"""
|
|
68
|
+
full_path = self.contest_package_dir / filepath
|
|
69
|
+
try:
|
|
70
|
+
with open(full_path, "r", encoding="utf-8") as f:
|
|
71
|
+
return json.load(f)
|
|
72
|
+
except FileNotFoundError:
|
|
73
|
+
raise HTTPException(status_code=404, detail=f"File not found: {filepath}")
|
|
74
|
+
except json.JSONDecodeError as e:
|
|
75
|
+
raise HTTPException(status_code=500, detail=f"Invalid JSON in file {filepath}: {e}")
|
|
76
|
+
|
|
77
|
+
def get_contest_id(self) -> str:
|
|
78
|
+
"""
|
|
79
|
+
Get contest ID from contest.json.
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
Contest ID string
|
|
83
|
+
"""
|
|
84
|
+
contest_data = self.load_json_file("contest.json")
|
|
85
|
+
return contest_data.get("id", "unknown")
|
|
86
|
+
|
|
87
|
+
def validate_contest_id(self, contest_id: str) -> None:
|
|
88
|
+
"""
|
|
89
|
+
Validate that the provided contest ID matches the expected one.
|
|
90
|
+
|
|
91
|
+
Args:
|
|
92
|
+
contest_id: Contest ID to validate
|
|
93
|
+
|
|
94
|
+
Raises:
|
|
95
|
+
HTTPException: If contest ID doesn't match
|
|
96
|
+
"""
|
|
97
|
+
expected_id = self.get_contest_id()
|
|
98
|
+
if contest_id != expected_id:
|
|
99
|
+
raise HTTPException(status_code=404, detail=f"Contest {contest_id} not found")
|
|
100
|
+
|
|
101
|
+
# API Information
|
|
102
|
+
def get_api_info(self) -> Dict[str, Any]:
|
|
103
|
+
"""Get API information"""
|
|
104
|
+
return {
|
|
105
|
+
"version": "2023-06",
|
|
106
|
+
"version_url": "https://ccs-specs.icpc.io/2023-06/contest_api",
|
|
107
|
+
"name": "XCPCIO",
|
|
108
|
+
"provider": {
|
|
109
|
+
"name": "XCPCIO",
|
|
110
|
+
"version": __version__,
|
|
111
|
+
},
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
def get_access_info(self, contest_id: str) -> Dict[str, Any]:
|
|
115
|
+
"""Get access information for current client"""
|
|
116
|
+
self.validate_contest_id(contest_id)
|
|
117
|
+
return {
|
|
118
|
+
"capabilities": [],
|
|
119
|
+
"endpoints": [
|
|
120
|
+
{
|
|
121
|
+
"type": "contest",
|
|
122
|
+
"properties": [
|
|
123
|
+
"id",
|
|
124
|
+
"name",
|
|
125
|
+
"formal_name",
|
|
126
|
+
"start_time",
|
|
127
|
+
"duration",
|
|
128
|
+
"scoreboard_type",
|
|
129
|
+
"penalty_time",
|
|
130
|
+
],
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
"type": "problems",
|
|
134
|
+
"properties": ["id", "label", "name", "ordinal", "color", "rgb", "time_limit", "test_data_count"],
|
|
135
|
+
},
|
|
136
|
+
{"type": "teams", "properties": ["id", "name", "label", "organization_id", "group_ids", "hidden"]},
|
|
137
|
+
{"type": "organizations", "properties": ["id", "name", "formal_name"]},
|
|
138
|
+
{"type": "groups", "properties": ["id", "name"]},
|
|
139
|
+
{"type": "judgement-types", "properties": ["id", "name", "penalty", "solved"]},
|
|
140
|
+
{"type": "languages", "properties": ["id", "name", "extensions"]},
|
|
141
|
+
{
|
|
142
|
+
"type": "state",
|
|
143
|
+
"properties": ["started", "ended", "frozen", "thawed", "finalized", "end_of_updates"],
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
"type": "submissions",
|
|
147
|
+
"properties": ["id", "team_id", "problem_id", "language_id", "time", "contest_time"],
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
"type": "judgements",
|
|
151
|
+
"properties": ["id", "submission_id", "judgement_type_id", "start_time", "start_contest_time"],
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
"type": "runs",
|
|
155
|
+
"properties": [
|
|
156
|
+
"id",
|
|
157
|
+
"judgement_id",
|
|
158
|
+
"ordinal",
|
|
159
|
+
"judgement_type_id",
|
|
160
|
+
"time",
|
|
161
|
+
"contest_time",
|
|
162
|
+
"run_time",
|
|
163
|
+
],
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
"type": "clarifications",
|
|
167
|
+
"properties": ["id", "from_team_id", "to_team_id", "problem_id", "text", "time", "contest_time"],
|
|
168
|
+
},
|
|
169
|
+
{"type": "awards", "properties": ["id", "citation", "team_ids"]},
|
|
170
|
+
],
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
# Contest operations
|
|
174
|
+
def get_contests(self) -> List[Dict[str, Any]]:
|
|
175
|
+
"""Get all contests"""
|
|
176
|
+
contest_data = self.load_json_file("contest.json")
|
|
177
|
+
return [contest_data]
|
|
178
|
+
|
|
179
|
+
def get_contest(self, contest_id: str) -> Dict[str, Any]:
|
|
180
|
+
"""Get specific contest"""
|
|
181
|
+
self.validate_contest_id(contest_id)
|
|
182
|
+
return self.load_json_file("contest.json")
|
|
183
|
+
|
|
184
|
+
def get_contest_state(self, contest_id: str) -> Dict[str, Any]:
|
|
185
|
+
"""Get contest state"""
|
|
186
|
+
self.validate_contest_id(contest_id)
|
|
187
|
+
return self.load_json_file("state.json")
|
|
188
|
+
|
|
189
|
+
# Problem operations
|
|
190
|
+
def get_problems(self, contest_id: str) -> List[Dict[str, Any]]:
|
|
191
|
+
"""Get all problems"""
|
|
192
|
+
self.validate_contest_id(contest_id)
|
|
193
|
+
return self.load_json_file("problems.json")
|
|
194
|
+
|
|
195
|
+
def get_problem(self, contest_id: str, problem_id: str) -> Dict[str, Any]:
|
|
196
|
+
"""Get specific problem"""
|
|
197
|
+
self.validate_contest_id(contest_id)
|
|
198
|
+
problems = self.load_json_file("problems.json")
|
|
199
|
+
for problem in problems:
|
|
200
|
+
if problem["id"] == problem_id:
|
|
201
|
+
return problem
|
|
202
|
+
raise HTTPException(status_code=404, detail=f"Problem {problem_id} not found")
|
|
203
|
+
|
|
204
|
+
# Team operations
|
|
205
|
+
def get_teams(self, contest_id: str, group_id: Optional[str] = None) -> List[Dict[str, Any]]:
|
|
206
|
+
"""Get all teams, optionally filtered by group"""
|
|
207
|
+
self.validate_contest_id(contest_id)
|
|
208
|
+
teams = self.load_json_file("teams.json")
|
|
209
|
+
|
|
210
|
+
if group_id:
|
|
211
|
+
filtered_teams = []
|
|
212
|
+
for team in teams:
|
|
213
|
+
if group_id in team.get("group_ids", []):
|
|
214
|
+
filtered_teams.append(team)
|
|
215
|
+
return filtered_teams
|
|
216
|
+
|
|
217
|
+
return teams
|
|
218
|
+
|
|
219
|
+
def get_team(self, contest_id: str, team_id: str) -> Dict[str, Any]:
|
|
220
|
+
"""Get specific team"""
|
|
221
|
+
self.validate_contest_id(contest_id)
|
|
222
|
+
teams = self.load_json_file("teams.json")
|
|
223
|
+
for team in teams:
|
|
224
|
+
if team["id"] == team_id:
|
|
225
|
+
return team
|
|
226
|
+
raise HTTPException(status_code=404, detail=f"Team {team_id} not found")
|
|
227
|
+
|
|
228
|
+
# Organization operations
|
|
229
|
+
def get_organizations(self, contest_id: str) -> List[Dict[str, Any]]:
|
|
230
|
+
"""Get all organizations"""
|
|
231
|
+
self.validate_contest_id(contest_id)
|
|
232
|
+
return self.load_json_file("organizations.json")
|
|
233
|
+
|
|
234
|
+
def get_organization(self, contest_id: str, organization_id: str) -> Dict[str, Any]:
|
|
235
|
+
"""Get specific organization"""
|
|
236
|
+
self.validate_contest_id(contest_id)
|
|
237
|
+
organizations = self.load_json_file("organizations.json")
|
|
238
|
+
for org in organizations:
|
|
239
|
+
if org["id"] == organization_id:
|
|
240
|
+
return org
|
|
241
|
+
raise HTTPException(status_code=404, detail=f"Organization {organization_id} not found")
|
|
242
|
+
|
|
243
|
+
# Group operations
|
|
244
|
+
def get_groups(self, contest_id: str) -> List[Dict[str, Any]]:
|
|
245
|
+
"""Get all groups"""
|
|
246
|
+
self.validate_contest_id(contest_id)
|
|
247
|
+
return self.load_json_file("groups.json")
|
|
248
|
+
|
|
249
|
+
def get_group(self, contest_id: str, group_id: str) -> Dict[str, Any]:
|
|
250
|
+
"""Get specific group"""
|
|
251
|
+
self.validate_contest_id(contest_id)
|
|
252
|
+
groups = self.load_json_file("groups.json")
|
|
253
|
+
for group in groups:
|
|
254
|
+
if group["id"] == group_id:
|
|
255
|
+
return group
|
|
256
|
+
raise HTTPException(status_code=404, detail=f"Group {group_id} not found")
|
|
257
|
+
|
|
258
|
+
# Language operations
|
|
259
|
+
def get_languages(self, contest_id: str) -> List[Dict[str, Any]]:
|
|
260
|
+
"""Get all languages"""
|
|
261
|
+
self.validate_contest_id(contest_id)
|
|
262
|
+
return self.load_json_file("languages.json")
|
|
263
|
+
|
|
264
|
+
def get_language(self, contest_id: str, language_id: str) -> Dict[str, Any]:
|
|
265
|
+
"""Get specific language"""
|
|
266
|
+
self.validate_contest_id(contest_id)
|
|
267
|
+
languages = self.load_json_file("languages.json")
|
|
268
|
+
for lang in languages:
|
|
269
|
+
if lang["id"] == language_id:
|
|
270
|
+
return lang
|
|
271
|
+
raise HTTPException(status_code=404, detail=f"Language {language_id} not found")
|
|
272
|
+
|
|
273
|
+
# Judgement type operations
|
|
274
|
+
def get_judgement_types(self, contest_id: str) -> List[Dict[str, Any]]:
|
|
275
|
+
"""Get all judgement types"""
|
|
276
|
+
self.validate_contest_id(contest_id)
|
|
277
|
+
return self.load_json_file("judgement-types.json")
|
|
278
|
+
|
|
279
|
+
def get_judgement_type(self, contest_id: str, judgement_type_id: str) -> Dict[str, Any]:
|
|
280
|
+
"""Get specific judgement type"""
|
|
281
|
+
self.validate_contest_id(contest_id)
|
|
282
|
+
judgement_types = self.load_json_file("judgement-types.json")
|
|
283
|
+
for jt in judgement_types:
|
|
284
|
+
if jt["id"] == judgement_type_id:
|
|
285
|
+
return jt
|
|
286
|
+
raise HTTPException(status_code=404, detail=f"Judgement type {judgement_type_id} not found")
|
|
287
|
+
|
|
288
|
+
# Submission operations
|
|
289
|
+
def get_submissions(
|
|
290
|
+
self, contest_id: str, team_id: Optional[str] = None, problem_id: Optional[str] = None
|
|
291
|
+
) -> List[Dict[str, Any]]:
|
|
292
|
+
"""Get all submissions, optionally filtered"""
|
|
293
|
+
self.validate_contest_id(contest_id)
|
|
294
|
+
submissions = self.load_json_file("submissions.json")
|
|
295
|
+
|
|
296
|
+
# Apply filters
|
|
297
|
+
if team_id:
|
|
298
|
+
submissions = [s for s in submissions if s.get("team_id") == team_id]
|
|
299
|
+
if problem_id:
|
|
300
|
+
submissions = [s for s in submissions if s.get("problem_id") == problem_id]
|
|
301
|
+
|
|
302
|
+
return submissions
|
|
303
|
+
|
|
304
|
+
def get_submission(self, contest_id: str, submission_id: str) -> Dict[str, Any]:
|
|
305
|
+
"""Get specific submission"""
|
|
306
|
+
self.validate_contest_id(contest_id)
|
|
307
|
+
submissions = self.load_json_file("submissions.json")
|
|
308
|
+
for submission in submissions:
|
|
309
|
+
if submission["id"] == submission_id:
|
|
310
|
+
return submission
|
|
311
|
+
raise HTTPException(status_code=404, detail=f"Submission {submission_id} not found")
|
|
312
|
+
|
|
313
|
+
# Judgement operations
|
|
314
|
+
def get_judgements(self, contest_id: str, submission_id: Optional[str] = None) -> List[Dict[str, Any]]:
|
|
315
|
+
"""Get all judgements, optionally filtered by submission"""
|
|
316
|
+
self.validate_contest_id(contest_id)
|
|
317
|
+
judgements = self.load_json_file("judgements.json")
|
|
318
|
+
|
|
319
|
+
if submission_id:
|
|
320
|
+
judgements = [j for j in judgements if j.get("submission_id") == submission_id]
|
|
321
|
+
|
|
322
|
+
return judgements
|
|
323
|
+
|
|
324
|
+
def get_judgement(self, contest_id: str, judgement_id: str) -> Dict[str, Any]:
|
|
325
|
+
"""Get specific judgement"""
|
|
326
|
+
self.validate_contest_id(contest_id)
|
|
327
|
+
judgements = self.load_json_file("judgements.json")
|
|
328
|
+
for judgement in judgements:
|
|
329
|
+
if judgement["id"] == judgement_id:
|
|
330
|
+
return judgement
|
|
331
|
+
raise HTTPException(status_code=404, detail=f"Judgement {judgement_id} not found")
|
|
332
|
+
|
|
333
|
+
# Run operations
|
|
334
|
+
def get_runs(self, contest_id: str, judgement_id: Optional[str] = None) -> List[Dict[str, Any]]:
|
|
335
|
+
"""Get all runs, optionally filtered by judgement"""
|
|
336
|
+
self.validate_contest_id(contest_id)
|
|
337
|
+
runs = self.load_json_file("runs.json")
|
|
338
|
+
|
|
339
|
+
if judgement_id:
|
|
340
|
+
runs = [r for r in runs if r.get("judgement_id") == judgement_id]
|
|
341
|
+
|
|
342
|
+
return runs
|
|
343
|
+
|
|
344
|
+
def get_run(self, contest_id: str, run_id: str) -> Dict[str, Any]:
|
|
345
|
+
"""Get specific run"""
|
|
346
|
+
self.validate_contest_id(contest_id)
|
|
347
|
+
runs = self.load_json_file("runs.json")
|
|
348
|
+
for run in runs:
|
|
349
|
+
if run["id"] == run_id:
|
|
350
|
+
return run
|
|
351
|
+
raise HTTPException(status_code=404, detail=f"Run {run_id} not found")
|
|
352
|
+
|
|
353
|
+
# Clarification operations
|
|
354
|
+
def get_clarifications(
|
|
355
|
+
self,
|
|
356
|
+
contest_id: str,
|
|
357
|
+
from_team_id: Optional[str] = None,
|
|
358
|
+
to_team_id: Optional[str] = None,
|
|
359
|
+
problem_id: Optional[str] = None,
|
|
360
|
+
) -> List[Dict[str, Any]]:
|
|
361
|
+
"""Get all clarifications, optionally filtered"""
|
|
362
|
+
self.validate_contest_id(contest_id)
|
|
363
|
+
clarifications = self.load_json_file("clarifications.json")
|
|
364
|
+
|
|
365
|
+
# Apply filters (empty string means null)
|
|
366
|
+
if from_team_id is not None:
|
|
367
|
+
if from_team_id == "":
|
|
368
|
+
clarifications = [c for c in clarifications if c.get("from_team_id") is None]
|
|
369
|
+
else:
|
|
370
|
+
clarifications = [c for c in clarifications if c.get("from_team_id") == from_team_id]
|
|
371
|
+
|
|
372
|
+
if to_team_id is not None:
|
|
373
|
+
if to_team_id == "":
|
|
374
|
+
clarifications = [c for c in clarifications if c.get("to_team_id") is None]
|
|
375
|
+
else:
|
|
376
|
+
clarifications = [c for c in clarifications if c.get("to_team_id") == to_team_id]
|
|
377
|
+
|
|
378
|
+
if problem_id is not None:
|
|
379
|
+
if problem_id == "":
|
|
380
|
+
clarifications = [c for c in clarifications if c.get("problem_id") is None]
|
|
381
|
+
else:
|
|
382
|
+
clarifications = [c for c in clarifications if c.get("problem_id") == problem_id]
|
|
383
|
+
|
|
384
|
+
return clarifications
|
|
385
|
+
|
|
386
|
+
def get_clarification(self, contest_id: str, clarification_id: str) -> Dict[str, Any]:
|
|
387
|
+
"""Get specific clarification"""
|
|
388
|
+
self.validate_contest_id(contest_id)
|
|
389
|
+
clarifications = self.load_json_file("clarifications.json")
|
|
390
|
+
for clarification in clarifications:
|
|
391
|
+
if clarification["id"] == clarification_id:
|
|
392
|
+
return clarification
|
|
393
|
+
raise HTTPException(status_code=404, detail=f"Clarification {clarification_id} not found")
|
|
394
|
+
|
|
395
|
+
# Award operations
|
|
396
|
+
def get_awards(self, contest_id: str) -> List[Dict[str, Any]]:
|
|
397
|
+
"""Get all awards"""
|
|
398
|
+
self.validate_contest_id(contest_id)
|
|
399
|
+
return self.load_json_file("awards.json")
|
|
400
|
+
|
|
401
|
+
def get_award(self, contest_id: str, award_id: str) -> Dict[str, Any]:
|
|
402
|
+
"""Get specific award"""
|
|
403
|
+
self.validate_contest_id(contest_id)
|
|
404
|
+
awards = self.load_json_file("awards.json")
|
|
405
|
+
for award in awards:
|
|
406
|
+
if award["id"] == award_id:
|
|
407
|
+
return award
|
|
408
|
+
raise HTTPException(status_code=404, detail=f"Award {award_id} not found")
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: xcpcio
|
|
3
|
-
Version: 0.63.
|
|
3
|
+
Version: 0.63.6
|
|
4
4
|
Summary: xcpcio python lib
|
|
5
5
|
Project-URL: homepage, https://github.com/xcpcio/xcpcio
|
|
6
6
|
Project-URL: documentation, https://github.com/xcpcio/xcpcio
|
|
7
7
|
Project-URL: repository, https://github.com/xcpcio/xcpcio
|
|
8
8
|
Author-email: Dup4 <hi@dup4.com>
|
|
9
|
+
Maintainer-email: Dup4 <hi@dup4.com>, cubercsl <hi@cubercsl.site>
|
|
9
10
|
License-Expression: MIT
|
|
10
11
|
Keywords: xcpcio
|
|
11
12
|
Classifier: Programming Language :: Python :: 3
|
|
@@ -18,10 +19,12 @@ Requires-Python: >=3.11
|
|
|
18
19
|
Requires-Dist: aiofiles>=23.0.0
|
|
19
20
|
Requires-Dist: aiohttp>=3.8.0
|
|
20
21
|
Requires-Dist: click>=8.0.0
|
|
22
|
+
Requires-Dist: fastapi>=0.117.1
|
|
21
23
|
Requires-Dist: pydantic>=2.11.7
|
|
22
24
|
Requires-Dist: pyyaml>=6.0.0
|
|
23
25
|
Requires-Dist: semver>=3.0.0
|
|
24
26
|
Requires-Dist: tenacity>=8.0.0
|
|
27
|
+
Requires-Dist: uvicorn>=0.36.0
|
|
25
28
|
Description-Content-Type: text/markdown
|
|
26
29
|
|
|
27
30
|
# xcpcio-python
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
xcpcio/__init__.py,sha256=kjd6itqBRSQ-OT83qUJXHt81KQQDRUtaIuykzfaWXLM,121
|
|
2
|
+
xcpcio/__version__.py,sha256=rmZgql2iMibrRXQ70oij5TmONsjXyGZIxE3EUzW1_1E,172
|
|
3
|
+
xcpcio/constants.py,sha256=MjpAgNXiBlUsx1S09m7JNT-nekNDR-aE6ggvGL3fg0I,2297
|
|
4
|
+
xcpcio/types.py,sha256=AkYby2haJgxwtozlgaPMG2ryAZdvsSc3sH-p6qXcM4g,6575
|
|
5
|
+
xcpcio/ccs/__init__.py,sha256=LSoKFblEuSoIVBYcUxOFF8fn2bH2R6kSg9xNrBfzC0g,99
|
|
6
|
+
xcpcio/ccs/contest_archiver.py,sha256=FKpUn1IGfa-UNf63OJ5eff7rxOEqXCvFYRLsvkMbUJc,16203
|
|
7
|
+
xcpcio/ccs/api_server/__init__.py,sha256=ASvVJ_ibGkXFDSNmy05eb9gESXRS8hjYHCrBecSnaS0,174
|
|
8
|
+
xcpcio/ccs/api_server/dependencies.py,sha256=5nosGVwY-3Mq7eK9C5ZOMEJFozzzw_kLlpCraFbJ9wY,1225
|
|
9
|
+
xcpcio/ccs/api_server/server.py,sha256=3gft3MqDXvKWug5UCLhdV681bcQMRrewDkwQ7qSPtRU,2598
|
|
10
|
+
xcpcio/ccs/api_server/routes/__init__.py,sha256=imTyDjc9Md3OIr7GniHi3i1vmfzSTKppuUjVREtd8b4,1461
|
|
11
|
+
xcpcio/ccs/api_server/routes/access.py,sha256=8f21NmvVLaFopb5q4MnifOHbhadDZuNHr0BIYc9nmqY,579
|
|
12
|
+
xcpcio/ccs/api_server/routes/awards.py,sha256=uj56ty2k7N5KXfQuWTvvTXSa7HO29CUfEhO8S85hlM0,1069
|
|
13
|
+
xcpcio/ccs/api_server/routes/clarifications.py,sha256=JX7FNbv_4mRdpuV0aSzGwjjmW-4SwTbmAOlm51MNpf4,1644
|
|
14
|
+
xcpcio/ccs/api_server/routes/contests.py,sha256=bJeMaGegei59rD-5YE0ptYcNsPdJqc73KnntRpShzZk,2560
|
|
15
|
+
xcpcio/ccs/api_server/routes/general.py,sha256=9WsnjlomsU13vvozOVHSRIzgReuLGzPln3xwDb8i-gY,933
|
|
16
|
+
xcpcio/ccs/api_server/routes/groups.py,sha256=8hOT_2rz329M_7hcfggRxLU-g8aV2xp6w3aMYlfOPNs,1087
|
|
17
|
+
xcpcio/ccs/api_server/routes/judgement_types.py,sha256=pMOFBQ1LqKhCTLNBGzopCOEmTnWsuVg67dTuItJaCsM,1268
|
|
18
|
+
xcpcio/ccs/api_server/routes/judgements.py,sha256=9GMaEkD8erhqaSItlZQdLzKz-1XL7MD2SQinnD_CZFg,1339
|
|
19
|
+
xcpcio/ccs/api_server/routes/languages.py,sha256=DUNRd3q_Hm5F8-3nCiAMpqqxzG_9zcU5_N8ACefqjOE,1176
|
|
20
|
+
xcpcio/ccs/api_server/routes/organizations.py,sha256=fWnKTCmxd3c1leYsRuJHuuTs0CZr-l_-Jl-8_wY_Ojk,2889
|
|
21
|
+
xcpcio/ccs/api_server/routes/problems.py,sha256=0Ba3fz09UfTGYmYYYJYFGmIpgIvJaa1Fg-5pcLcxEMY,2774
|
|
22
|
+
xcpcio/ccs/api_server/routes/runs.py,sha256=uBPeM5ChI3LxR__pBSyDxsbcE4NyL6eKVdvF4TEOk5g,1248
|
|
23
|
+
xcpcio/ccs/api_server/routes/submissions.py,sha256=81-KbQLUKCqgGE-aCTO7-Pg2QyTJc_Mu_Ys8N0oF1tU,3161
|
|
24
|
+
xcpcio/ccs/api_server/routes/teams.py,sha256=y_aJykzgewmOQtXOJe2s0s7DQmnKjD3805Q5VTcdlIQ,2725
|
|
25
|
+
xcpcio/ccs/api_server/services/__init__.py,sha256=WQLNrLVomhtICl8HlFYaCoRewIHVZfUiiwrSBUOOWDg,171
|
|
26
|
+
xcpcio/ccs/api_server/services/contest_service.py,sha256=EbSDrkVg18Th9kAuYAin4InHCOI7hWkkDJR1u1xlEtk,16186
|
|
27
|
+
xcpcio/ccs/model/__init__.py,sha256=cZE1q5JY-iHDEKZpsx0UZaMhH-23H4oAHaYOkW7dZ5s,43
|
|
28
|
+
xcpcio/ccs/model/model_2023_06/__init__.py,sha256=OmDQZqmigBpL64LXk5lIOGoQ3Uqis8-2z6qQpOO5aJc,167
|
|
29
|
+
xcpcio/ccs/model/model_2023_06/model.py,sha256=bVMDWpJTwPSpz1fHPxWrWerxCBIboH3LKVZpIZGQ2pY,15287
|
|
30
|
+
xcpcio-0.63.6.dist-info/METADATA,sha256=uEBCmgoTkMCCrO-zLZGdzDjhYb9n51mabFs4lwEV0II,1079
|
|
31
|
+
xcpcio-0.63.6.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
32
|
+
xcpcio-0.63.6.dist-info/entry_points.txt,sha256=qvzh8oDJxIHqTN-rg2lRN6xR99AqxbWnlAQI7uzDibI,59
|
|
33
|
+
xcpcio-0.63.6.dist-info/RECORD,,
|
xcpcio-0.63.5.dist-info/RECORD
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
xcpcio/__init__.py,sha256=kjd6itqBRSQ-OT83qUJXHt81KQQDRUtaIuykzfaWXLM,121
|
|
2
|
-
xcpcio/__version__.py,sha256=0Inh2IrO1TDWHZQNNWZG7RQoT8GKZUEeRoZnIhcVdWI,23
|
|
3
|
-
xcpcio/constants.py,sha256=MjpAgNXiBlUsx1S09m7JNT-nekNDR-aE6ggvGL3fg0I,2297
|
|
4
|
-
xcpcio/types.py,sha256=AkYby2haJgxwtozlgaPMG2ryAZdvsSc3sH-p6qXcM4g,6575
|
|
5
|
-
xcpcio/ccs/__init__.py,sha256=qjkSo9lS8dJyNqiGljCnbS1cr7J8jj72pqcCzf_v0Ig,75
|
|
6
|
-
xcpcio/ccs/contest_archiver.py,sha256=FKpUn1IGfa-UNf63OJ5eff7rxOEqXCvFYRLsvkMbUJc,16203
|
|
7
|
-
xcpcio/ccs/model/__init__.py,sha256=cZE1q5JY-iHDEKZpsx0UZaMhH-23H4oAHaYOkW7dZ5s,43
|
|
8
|
-
xcpcio/ccs/model/model_2023_06/__init__.py,sha256=OmDQZqmigBpL64LXk5lIOGoQ3Uqis8-2z6qQpOO5aJc,167
|
|
9
|
-
xcpcio/ccs/model/model_2023_06/model.py,sha256=bVMDWpJTwPSpz1fHPxWrWerxCBIboH3LKVZpIZGQ2pY,15287
|
|
10
|
-
xcpcio-0.63.5.dist-info/METADATA,sha256=KK0d6SVxSWCkNyD-6fYSO5GZnV2L9dT8FA_hE7o2hp0,950
|
|
11
|
-
xcpcio-0.63.5.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
12
|
-
xcpcio-0.63.5.dist-info/entry_points.txt,sha256=qvzh8oDJxIHqTN-rg2lRN6xR99AqxbWnlAQI7uzDibI,59
|
|
13
|
-
xcpcio-0.63.5.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|