xcpcio 0.63.7__py3-none-any.whl → 0.64.1__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 +1 -1
- xcpcio/ccs/api_server/dependencies.py +8 -3
- xcpcio/ccs/api_server/routes/contests.py +5 -34
- xcpcio/ccs/api_server/routes/organizations.py +3 -24
- xcpcio/ccs/api_server/routes/problems.py +3 -22
- xcpcio/ccs/api_server/routes/submissions.py +3 -24
- xcpcio/ccs/api_server/routes/teams.py +3 -22
- xcpcio/ccs/api_server/services/contest_service.py +104 -235
- xcpcio/ccs/base/__init__.py +3 -0
- xcpcio/ccs/base/types.py +9 -0
- xcpcio/ccs/reader/__init__.py +0 -0
- xcpcio/ccs/reader/base_ccs_reader.py +165 -0
- xcpcio/ccs/reader/contest_package_reader.py +331 -0
- xcpcio-0.64.1.dist-info/METADATA +86 -0
- {xcpcio-0.63.7.dist-info → xcpcio-0.64.1.dist-info}/RECORD +17 -12
- xcpcio-0.63.7.dist-info/METADATA +0 -30
- {xcpcio-0.63.7.dist-info → xcpcio-0.64.1.dist-info}/WHEEL +0 -0
- {xcpcio-0.63.7.dist-info → xcpcio-0.64.1.dist-info}/entry_points.txt +0 -0
|
@@ -5,321 +5,190 @@ Business logic layer for Contest API operations.
|
|
|
5
5
|
Handles file reading, data validation, and business operations.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
-
import
|
|
9
|
-
import
|
|
10
|
-
from collections import defaultdict
|
|
11
|
-
from pathlib import Path
|
|
12
|
-
from typing import Any, Dict, List, Optional, Union
|
|
8
|
+
import logging
|
|
9
|
+
from typing import Any, Dict, List, Optional
|
|
13
10
|
|
|
14
11
|
from fastapi import HTTPException
|
|
15
12
|
|
|
13
|
+
from xcpcio.__version__ import __version__
|
|
14
|
+
from xcpcio.ccs.base.types import FileAttr
|
|
15
|
+
from xcpcio.ccs.reader.base_ccs_reader import BaseCCSReader
|
|
16
16
|
|
|
17
|
-
|
|
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 _create_index_by_id(self, data: List[Dict[str, Any]], id_name: str) -> Dict[str, List[Dict]]:
|
|
35
|
-
res = defaultdict(list)
|
|
36
|
-
for item in data:
|
|
37
|
-
res[item[id_name]].append(item)
|
|
38
|
-
return res
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
39
18
|
|
|
40
|
-
def _load_indexes(self) -> None:
|
|
41
|
-
"""Load and index commonly accessed data for faster lookups"""
|
|
42
|
-
self.access = self.load_json_file("access.json")
|
|
43
19
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
self.api_info = self.load_json_file("api.json")
|
|
48
|
-
|
|
49
|
-
self.awards = self.load_json_file("awards.json")
|
|
50
|
-
self.awards_by_id = {award["id"] for award in self.awards}
|
|
51
|
-
|
|
52
|
-
self.clarifications = self.load_json_file("clarifications.json")
|
|
53
|
-
self.clarifications_by_id = {clarification["id"] for clarification in self.clarifications}
|
|
54
|
-
|
|
55
|
-
self.contest = self.load_json_file("contest.json")
|
|
56
|
-
self.contest_state = self.load_json_file("state.json")
|
|
57
|
-
|
|
58
|
-
self.groups = self.load_json_file("groups.json")
|
|
59
|
-
self.groups_by_id = {group["id"]: group for group in self.groups}
|
|
60
|
-
|
|
61
|
-
self.judgement_types = self.load_json_file("judgement-types.json")
|
|
62
|
-
self.judgement_types_by_id = {judgement_type["id"] for judgement_type in self.judgement_types}
|
|
63
|
-
|
|
64
|
-
self.judgements = self.load_json_file("judgements.json")
|
|
65
|
-
self.judgements_by_id = {judgement["id"] for judgement in self.judgements}
|
|
66
|
-
self.judgements_by_submission_id = self._create_index_by_id(self.judgements, "submission_id")
|
|
67
|
-
|
|
68
|
-
self.languages = self.load_json_file("languages.json")
|
|
69
|
-
self.languages_by_id = {language["id"] for language in self.languages}
|
|
70
|
-
|
|
71
|
-
self.organizations = self.load_json_file("organizations.json")
|
|
72
|
-
self.organizations_by_id = {org["id"]: org for org in self.organizations}
|
|
73
|
-
|
|
74
|
-
self.problems = self.load_json_file("problems.json")
|
|
75
|
-
self.problems_by_id = {problem["id"]: problem for problem in self.problems}
|
|
76
|
-
|
|
77
|
-
self.runs = self.load_json_file("runs.json")
|
|
78
|
-
self.runs_by_id = {run["id"] for run in self.runs}
|
|
79
|
-
self.runs_by_judgement_id = self._create_index_by_id(self.runs, "judgement_id")
|
|
80
|
-
|
|
81
|
-
self.submissions = self.load_json_file("submissions.json")
|
|
82
|
-
self.submissions_by_id = {submission["id"]: submission for submission in self.submissions}
|
|
83
|
-
|
|
84
|
-
self.teams = self.load_json_file("teams.json")
|
|
85
|
-
self.teams_by_id = {team["id"]: team for team in self.teams}
|
|
86
|
-
|
|
87
|
-
self.event_feed = self.load_ndjson_file("event-feed.ndjson")
|
|
88
|
-
self.event_feed_tokens = [event["token"] for event in self.event_feed]
|
|
89
|
-
|
|
90
|
-
def load_json_file(self, filepath: str) -> Union[Dict[str, Any], List[Any]]:
|
|
91
|
-
"""
|
|
92
|
-
Load JSON data from contest package directory.
|
|
93
|
-
|
|
94
|
-
Args:
|
|
95
|
-
filepath: Relative path to JSON file within contest package
|
|
96
|
-
|
|
97
|
-
Returns:
|
|
98
|
-
Parsed JSON data
|
|
99
|
-
|
|
100
|
-
Raises:
|
|
101
|
-
HTTPException: If file not found or invalid JSON
|
|
102
|
-
"""
|
|
103
|
-
|
|
104
|
-
full_path = self.contest_package_dir / filepath
|
|
105
|
-
try:
|
|
106
|
-
with open(full_path, "r", encoding="utf-8") as f:
|
|
107
|
-
return json.load(f)
|
|
108
|
-
except FileNotFoundError:
|
|
109
|
-
raise HTTPException(status_code=404, detail=f"File not found: {filepath}")
|
|
110
|
-
except json.JSONDecodeError as e:
|
|
111
|
-
raise HTTPException(status_code=500, detail=f"Invalid JSON in file {filepath}: {e}")
|
|
112
|
-
|
|
113
|
-
def load_ndjson_file(self, filepath: str) -> List[Dict[str, Any]]:
|
|
114
|
-
full_path = self.contest_package_dir / filepath
|
|
115
|
-
try:
|
|
116
|
-
data = list()
|
|
117
|
-
with open(full_path, "r", encoding="utf-8") as f:
|
|
118
|
-
for line in f.readlines():
|
|
119
|
-
data.append(json.loads(line))
|
|
120
|
-
return data
|
|
121
|
-
except FileNotFoundError:
|
|
122
|
-
raise HTTPException(status_code=404, detail=f"File not found: {filepath}")
|
|
123
|
-
except json.JSONDecodeError as e:
|
|
124
|
-
raise HTTPException(status_code=500, detail=f"Invalid JSON in file {filepath}: {e}")
|
|
125
|
-
|
|
126
|
-
def get_contest_id(self) -> str:
|
|
127
|
-
"""
|
|
128
|
-
Get contest ID from contest.json.
|
|
129
|
-
|
|
130
|
-
Returns:
|
|
131
|
-
Contest ID string
|
|
132
|
-
"""
|
|
133
|
-
contest_data = self.load_json_file("contest.json")
|
|
134
|
-
return contest_data.get("id", "unknown")
|
|
135
|
-
|
|
136
|
-
def validate_contest_id(self, contest_id: str) -> None:
|
|
137
|
-
"""
|
|
138
|
-
Validate that the provided contest ID matches the expected one.
|
|
20
|
+
class ContestService:
|
|
21
|
+
"""Service class for contest-related operations"""
|
|
139
22
|
|
|
140
|
-
|
|
141
|
-
|
|
23
|
+
def __init__(self, reader_dict: Dict[str, BaseCCSReader]):
|
|
24
|
+
self.reader_dict = reader_dict
|
|
142
25
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
"""
|
|
146
|
-
expected_id = self.get_contest_id()
|
|
147
|
-
if contest_id != expected_id:
|
|
26
|
+
def _get_reader(self, contest_id: str) -> BaseCCSReader:
|
|
27
|
+
if contest_id not in self.reader_dict:
|
|
148
28
|
raise HTTPException(status_code=404, detail=f"Contest {contest_id} not found")
|
|
29
|
+
return self.reader_dict[contest_id]
|
|
149
30
|
|
|
150
31
|
# API Information
|
|
151
32
|
def get_api_info(self) -> Dict[str, Any]:
|
|
152
|
-
return
|
|
33
|
+
return {
|
|
34
|
+
"version": "2023-06",
|
|
35
|
+
"version_url": "https://ccs-specs.icpc.io/2023-06/contest_api",
|
|
36
|
+
"name": "XCPCIO",
|
|
37
|
+
"provider": {
|
|
38
|
+
"name": "XCPCIO",
|
|
39
|
+
"version": __version__,
|
|
40
|
+
},
|
|
41
|
+
}
|
|
153
42
|
|
|
154
43
|
def get_access(self, contest_id: str) -> Dict[str, Any]:
|
|
155
|
-
self.
|
|
156
|
-
return
|
|
44
|
+
reader = self._get_reader(contest_id)
|
|
45
|
+
return reader.get_access()
|
|
157
46
|
|
|
158
47
|
# Account operations
|
|
159
48
|
def get_accounts(self, contest_id: str) -> List[Dict[str, Any]]:
|
|
160
|
-
self.
|
|
161
|
-
return
|
|
49
|
+
reader = self._get_reader(contest_id)
|
|
50
|
+
return reader.get_accounts()
|
|
162
51
|
|
|
163
52
|
def get_account(self, contest_id: str, account_id: str) -> Dict[str, Any]:
|
|
164
|
-
self.
|
|
165
|
-
|
|
166
|
-
raise HTTPException(status_code=404, detail=f"Account {account_id} not found")
|
|
167
|
-
return self.accounts_by_id[account_id]
|
|
53
|
+
reader = self._get_reader(contest_id)
|
|
54
|
+
return reader.get_account(account_id)
|
|
168
55
|
|
|
169
56
|
# Contest operations
|
|
170
57
|
def get_contests(self) -> List[Dict[str, Any]]:
|
|
171
|
-
return [self.
|
|
58
|
+
return [reader.get_contest() for reader in self.reader_dict.values()]
|
|
172
59
|
|
|
173
60
|
def get_contest(self, contest_id: str) -> Dict[str, Any]:
|
|
174
|
-
self.
|
|
175
|
-
return
|
|
61
|
+
reader = self._get_reader(contest_id)
|
|
62
|
+
return reader.get_contest()
|
|
176
63
|
|
|
177
64
|
def get_contest_state(self, contest_id: str) -> Dict[str, Any]:
|
|
178
|
-
self.
|
|
179
|
-
return
|
|
65
|
+
reader = self._get_reader(contest_id)
|
|
66
|
+
return reader.get_contest_state()
|
|
67
|
+
|
|
68
|
+
def get_contest_banner(self, contest_id: str) -> FileAttr:
|
|
69
|
+
reader = self._get_reader(contest_id)
|
|
70
|
+
return reader.get_contest_banner()
|
|
71
|
+
|
|
72
|
+
def get_contest_problemset(self, contest_id: str) -> FileAttr:
|
|
73
|
+
reader = self._get_reader(contest_id)
|
|
74
|
+
return reader.get_contest_problemset()
|
|
180
75
|
|
|
181
76
|
# Problem operations
|
|
182
77
|
def get_problems(self, contest_id: str) -> List[Dict[str, Any]]:
|
|
183
|
-
self.
|
|
184
|
-
return
|
|
78
|
+
reader = self._get_reader(contest_id)
|
|
79
|
+
return reader.get_problems()
|
|
185
80
|
|
|
186
81
|
def get_problem(self, contest_id: str, problem_id: str) -> Dict[str, Any]:
|
|
187
|
-
self.
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
82
|
+
reader = self._get_reader(contest_id)
|
|
83
|
+
return reader.get_problem(problem_id)
|
|
84
|
+
|
|
85
|
+
def get_problem_statement(self, contest_id: str, problem_id: str) -> FileAttr:
|
|
86
|
+
reader = self._get_reader(contest_id)
|
|
87
|
+
return reader.get_problem_statement(problem_id)
|
|
191
88
|
|
|
192
89
|
# Team operations
|
|
193
90
|
def get_teams(self, contest_id: str) -> List[Dict[str, Any]]:
|
|
194
|
-
self.
|
|
195
|
-
return
|
|
91
|
+
reader = self._get_reader(contest_id)
|
|
92
|
+
return reader.get_teams()
|
|
196
93
|
|
|
197
94
|
def get_team(self, contest_id: str, team_id: str) -> Dict[str, Any]:
|
|
198
|
-
self.
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
95
|
+
reader = self._get_reader(contest_id)
|
|
96
|
+
return reader.get_team(team_id)
|
|
97
|
+
|
|
98
|
+
def get_team_photo(self, contest_id: str, team_id: str) -> FileAttr:
|
|
99
|
+
reader = self._get_reader(contest_id)
|
|
100
|
+
return reader.get_team_photo(team_id)
|
|
202
101
|
|
|
203
102
|
# Organization operations
|
|
204
103
|
def get_organizations(self, contest_id: str) -> List[Dict[str, Any]]:
|
|
205
|
-
self.
|
|
206
|
-
return
|
|
104
|
+
reader = self._get_reader(contest_id)
|
|
105
|
+
return reader.get_organizations()
|
|
207
106
|
|
|
208
107
|
def get_organization(self, contest_id: str, organization_id: str) -> Dict[str, Any]:
|
|
209
|
-
self.
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
108
|
+
reader = self._get_reader(contest_id)
|
|
109
|
+
return reader.get_organization(organization_id)
|
|
110
|
+
|
|
111
|
+
def get_organization_logo(self, contest_id: str, organization_id: str) -> FileAttr:
|
|
112
|
+
reader = self._get_reader(contest_id)
|
|
113
|
+
return reader.get_organization_logo(organization_id)
|
|
213
114
|
|
|
214
115
|
# Group operations
|
|
215
116
|
def get_groups(self, contest_id: str) -> List[Dict[str, Any]]:
|
|
216
|
-
self.
|
|
217
|
-
return
|
|
117
|
+
reader = self._get_reader(contest_id)
|
|
118
|
+
return reader.get_groups()
|
|
218
119
|
|
|
219
120
|
def get_group(self, contest_id: str, group_id: str) -> Dict[str, Any]:
|
|
220
|
-
self.
|
|
221
|
-
|
|
222
|
-
raise HTTPException(status_code=404, detail=f"Group {group_id} not found")
|
|
223
|
-
return self.groups_by_id[group_id]
|
|
121
|
+
reader = self._get_reader(contest_id)
|
|
122
|
+
return reader.get_group(group_id)
|
|
224
123
|
|
|
225
124
|
# Language operations
|
|
226
125
|
def get_languages(self, contest_id: str) -> List[Dict[str, Any]]:
|
|
227
|
-
self.
|
|
228
|
-
return
|
|
126
|
+
reader = self._get_reader(contest_id)
|
|
127
|
+
return reader.get_languages()
|
|
229
128
|
|
|
230
129
|
def get_language(self, contest_id: str, language_id: str) -> Dict[str, Any]:
|
|
231
|
-
self.
|
|
232
|
-
|
|
233
|
-
raise HTTPException(status_code=404, detail=f"Language {language_id} not found")
|
|
234
|
-
return self.languages_by_id[language_id]
|
|
130
|
+
reader = self._get_reader(contest_id)
|
|
131
|
+
return reader.get_language(language_id)
|
|
235
132
|
|
|
236
133
|
# Judgement type operations
|
|
237
134
|
def get_judgement_types(self, contest_id: str) -> List[Dict[str, Any]]:
|
|
238
|
-
self.
|
|
239
|
-
return
|
|
135
|
+
reader = self._get_reader(contest_id)
|
|
136
|
+
return reader.get_judgement_types()
|
|
240
137
|
|
|
241
138
|
def get_judgement_type(self, contest_id: str, judgement_type_id: str) -> Dict[str, Any]:
|
|
242
|
-
self.
|
|
243
|
-
|
|
244
|
-
raise HTTPException(status_code=404, detail=f"Judgement type {judgement_type_id} not found")
|
|
245
|
-
return self.judgement_types_by_id[judgement_type_id]
|
|
139
|
+
reader = self._get_reader(contest_id)
|
|
140
|
+
return reader.get_judgement_type(judgement_type_id)
|
|
246
141
|
|
|
247
142
|
# Submission operations
|
|
248
143
|
def get_submissions(self, contest_id: str) -> List[Dict[str, Any]]:
|
|
249
|
-
self.
|
|
250
|
-
return
|
|
144
|
+
reader = self._get_reader(contest_id)
|
|
145
|
+
return reader.get_submissions()
|
|
251
146
|
|
|
252
147
|
def get_submission(self, contest_id: str, submission_id: str) -> Dict[str, Any]:
|
|
253
|
-
self.
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
148
|
+
reader = self._get_reader(contest_id)
|
|
149
|
+
return reader.get_submission(submission_id)
|
|
150
|
+
|
|
151
|
+
def get_submission_file(self, contest_id: str, submission_id: str) -> FileAttr:
|
|
152
|
+
reader = self._get_reader(contest_id)
|
|
153
|
+
return reader.get_submission_file(submission_id)
|
|
257
154
|
|
|
258
155
|
# Judgement operations
|
|
259
156
|
def get_judgements(self, contest_id: str, submission_id: Optional[str] = None) -> List[Dict[str, Any]]:
|
|
260
|
-
self.
|
|
261
|
-
|
|
262
|
-
if submission_id is not None:
|
|
263
|
-
if submission_id not in self.judgements_by_submission_id:
|
|
264
|
-
raise HTTPException(status_code=404, detail=f"Submission id not found: {submission_id}")
|
|
265
|
-
return self.judgements_by_submission_id[submission_id]
|
|
266
|
-
|
|
267
|
-
return self.judgements
|
|
157
|
+
reader = self._get_reader(contest_id)
|
|
158
|
+
return reader.get_judgements(submission_id)
|
|
268
159
|
|
|
269
160
|
def get_judgement(self, contest_id: str, judgement_id: str) -> Dict[str, Any]:
|
|
270
|
-
self.
|
|
271
|
-
|
|
272
|
-
raise HTTPException(status_code=404, detail=f"Judgement {judgement_id} not found")
|
|
273
|
-
return self.judgements_by_id[judgement_id]
|
|
161
|
+
reader = self._get_reader(contest_id)
|
|
162
|
+
return reader.get_judgement(judgement_id)
|
|
274
163
|
|
|
275
164
|
# Run operations
|
|
276
165
|
def get_runs(self, contest_id: str, judgement_id: Optional[str] = None) -> List[Dict[str, Any]]:
|
|
277
|
-
self.
|
|
278
|
-
|
|
279
|
-
if judgement_id is not None:
|
|
280
|
-
if judgement_id not in self.runs_by_judgement_id:
|
|
281
|
-
raise HTTPException(status_code=404, detail=f"Judgement id not found: {judgement_id}")
|
|
282
|
-
return self.runs_by_judgement_id[judgement_id]
|
|
283
|
-
|
|
284
|
-
return self.runs
|
|
166
|
+
reader = self._get_reader(contest_id)
|
|
167
|
+
return reader.get_runs(judgement_id)
|
|
285
168
|
|
|
286
169
|
def get_run(self, contest_id: str, run_id: str) -> Dict[str, Any]:
|
|
287
|
-
self.
|
|
288
|
-
|
|
289
|
-
raise HTTPException(status_code=404, detail=f"Run {run_id} not found")
|
|
290
|
-
return self.runs_by_id[run_id]
|
|
170
|
+
reader = self._get_reader(contest_id)
|
|
171
|
+
return reader.get_run(run_id)
|
|
291
172
|
|
|
292
173
|
# Clarification operations
|
|
293
|
-
def get_clarifications(
|
|
294
|
-
self
|
|
295
|
-
|
|
296
|
-
) -> List[Dict[str, Any]]:
|
|
297
|
-
self.validate_contest_id(contest_id)
|
|
298
|
-
return self.clarifications
|
|
174
|
+
def get_clarifications(self, contest_id: str) -> List[Dict[str, Any]]:
|
|
175
|
+
reader = self._get_reader(contest_id)
|
|
176
|
+
return reader.get_clarifications()
|
|
299
177
|
|
|
300
178
|
def get_clarification(self, contest_id: str, clarification_id: str) -> Dict[str, Any]:
|
|
301
|
-
self.
|
|
302
|
-
|
|
303
|
-
raise HTTPException(status_code=404, detail=f"Clarification {clarification_id} not found")
|
|
304
|
-
return self.clarifications_by_id[clarification_id]
|
|
179
|
+
reader = self._get_reader(contest_id)
|
|
180
|
+
return reader.get_clarification(clarification_id)
|
|
305
181
|
|
|
306
182
|
# Award operations
|
|
307
183
|
def get_awards(self, contest_id: str) -> List[Dict[str, Any]]:
|
|
308
|
-
self.
|
|
309
|
-
return
|
|
184
|
+
reader = self._get_reader(contest_id)
|
|
185
|
+
return reader.get_awards()
|
|
310
186
|
|
|
311
187
|
def get_award(self, contest_id: str, award_id: str) -> Dict[str, Any]:
|
|
312
|
-
self.
|
|
313
|
-
|
|
314
|
-
raise HTTPException(status_code=404, detail=f"Award {award_id} not found")
|
|
315
|
-
return self.awards_by_id[award_id]
|
|
188
|
+
reader = self._get_reader(contest_id)
|
|
189
|
+
return reader.get_award(award_id)
|
|
316
190
|
|
|
317
191
|
# Event Feed operations
|
|
318
192
|
def get_event_feed(self, contest_id: str, since_token: Optional[str] = None) -> List[Dict[str, Any]]:
|
|
319
|
-
self.
|
|
320
|
-
|
|
321
|
-
if since_token is None:
|
|
322
|
-
return self.event_feed
|
|
323
|
-
|
|
324
|
-
idx = bisect.bisect_left(self.event_feed_tokens, since_token)
|
|
325
|
-
return self.event_feed[idx:]
|
|
193
|
+
reader = self._get_reader(contest_id)
|
|
194
|
+
return reader.get_event_feed(since_token)
|
xcpcio/ccs/base/types.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from typing import Any, Dict, List, Optional
|
|
3
|
+
|
|
4
|
+
from xcpcio.ccs.base.types import FileAttr
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class BaseCCSReader(ABC):
|
|
8
|
+
# API Information
|
|
9
|
+
@abstractmethod
|
|
10
|
+
def get_api_info(self) -> Dict[str, Any]:
|
|
11
|
+
pass
|
|
12
|
+
|
|
13
|
+
@abstractmethod
|
|
14
|
+
def get_access(self) -> Dict[str, Any]:
|
|
15
|
+
pass
|
|
16
|
+
|
|
17
|
+
# Account operations
|
|
18
|
+
@abstractmethod
|
|
19
|
+
def get_accounts(self) -> List[Dict[str, Any]]:
|
|
20
|
+
pass
|
|
21
|
+
|
|
22
|
+
@abstractmethod
|
|
23
|
+
def get_account(self, account_id: str) -> Dict[str, Any]:
|
|
24
|
+
pass
|
|
25
|
+
|
|
26
|
+
# Contest operations
|
|
27
|
+
@abstractmethod
|
|
28
|
+
def get_contest_id(self) -> str:
|
|
29
|
+
pass
|
|
30
|
+
|
|
31
|
+
@abstractmethod
|
|
32
|
+
def get_contest(self) -> Dict[str, Any]:
|
|
33
|
+
pass
|
|
34
|
+
|
|
35
|
+
@abstractmethod
|
|
36
|
+
def get_contest_state(self) -> Dict[str, Any]:
|
|
37
|
+
pass
|
|
38
|
+
|
|
39
|
+
@abstractmethod
|
|
40
|
+
def get_contest_banner(self) -> FileAttr:
|
|
41
|
+
pass
|
|
42
|
+
|
|
43
|
+
@abstractmethod
|
|
44
|
+
def get_contest_problemset(self) -> FileAttr:
|
|
45
|
+
pass
|
|
46
|
+
|
|
47
|
+
# Problem operations
|
|
48
|
+
@abstractmethod
|
|
49
|
+
def get_problems(self) -> List[Dict[str, Any]]:
|
|
50
|
+
pass
|
|
51
|
+
|
|
52
|
+
@abstractmethod
|
|
53
|
+
def get_problem(self, problem_id: str) -> Dict[str, Any]:
|
|
54
|
+
pass
|
|
55
|
+
|
|
56
|
+
@abstractmethod
|
|
57
|
+
def get_problem_statement(self, problem_id: str) -> FileAttr:
|
|
58
|
+
pass
|
|
59
|
+
|
|
60
|
+
# Team operations
|
|
61
|
+
@abstractmethod
|
|
62
|
+
def get_teams(self) -> List[Dict[str, Any]]:
|
|
63
|
+
pass
|
|
64
|
+
|
|
65
|
+
@abstractmethod
|
|
66
|
+
def get_team(self, team_id: str) -> Dict[str, Any]:
|
|
67
|
+
pass
|
|
68
|
+
|
|
69
|
+
@abstractmethod
|
|
70
|
+
def get_team_photo(self, team_id: str) -> FileAttr:
|
|
71
|
+
pass
|
|
72
|
+
|
|
73
|
+
# Organization operations
|
|
74
|
+
@abstractmethod
|
|
75
|
+
def get_organizations(self) -> List[Dict[str, Any]]:
|
|
76
|
+
pass
|
|
77
|
+
|
|
78
|
+
@abstractmethod
|
|
79
|
+
def get_organization(self, organization_id: str) -> Dict[str, Any]:
|
|
80
|
+
pass
|
|
81
|
+
|
|
82
|
+
@abstractmethod
|
|
83
|
+
def get_organization_logo(self, organization_id: str) -> FileAttr:
|
|
84
|
+
pass
|
|
85
|
+
|
|
86
|
+
# Group operations
|
|
87
|
+
@abstractmethod
|
|
88
|
+
def get_groups(self) -> List[Dict[str, Any]]:
|
|
89
|
+
pass
|
|
90
|
+
|
|
91
|
+
@abstractmethod
|
|
92
|
+
def get_group(self, group_id: str) -> Dict[str, Any]:
|
|
93
|
+
pass
|
|
94
|
+
|
|
95
|
+
# Language operations
|
|
96
|
+
@abstractmethod
|
|
97
|
+
def get_languages(self) -> List[Dict[str, Any]]:
|
|
98
|
+
pass
|
|
99
|
+
|
|
100
|
+
@abstractmethod
|
|
101
|
+
def get_language(self, language_id: str) -> Dict[str, Any]:
|
|
102
|
+
pass
|
|
103
|
+
|
|
104
|
+
# Judgement type operations
|
|
105
|
+
@abstractmethod
|
|
106
|
+
def get_judgement_types(self) -> List[Dict[str, Any]]:
|
|
107
|
+
pass
|
|
108
|
+
|
|
109
|
+
@abstractmethod
|
|
110
|
+
def get_judgement_type(self, judgement_type_id: str) -> Dict[str, Any]:
|
|
111
|
+
pass
|
|
112
|
+
|
|
113
|
+
# Submission operations
|
|
114
|
+
@abstractmethod
|
|
115
|
+
def get_submissions(self) -> List[Dict[str, Any]]:
|
|
116
|
+
pass
|
|
117
|
+
|
|
118
|
+
@abstractmethod
|
|
119
|
+
def get_submission(self, submission_id: str) -> Dict[str, Any]:
|
|
120
|
+
pass
|
|
121
|
+
|
|
122
|
+
@abstractmethod
|
|
123
|
+
def get_submission_file(self, submission_id: str) -> FileAttr:
|
|
124
|
+
pass
|
|
125
|
+
|
|
126
|
+
# Judgement operations
|
|
127
|
+
@abstractmethod
|
|
128
|
+
def get_judgements(self, submission_id: Optional[str] = None) -> List[Dict[str, Any]]:
|
|
129
|
+
pass
|
|
130
|
+
|
|
131
|
+
@abstractmethod
|
|
132
|
+
def get_judgement(self, judgement_id: str) -> Dict[str, Any]:
|
|
133
|
+
pass
|
|
134
|
+
|
|
135
|
+
# Run operations
|
|
136
|
+
@abstractmethod
|
|
137
|
+
def get_runs(self, judgement_id: Optional[str] = None) -> List[Dict[str, Any]]:
|
|
138
|
+
pass
|
|
139
|
+
|
|
140
|
+
@abstractmethod
|
|
141
|
+
def get_run(self, run_id: str) -> Dict[str, Any]:
|
|
142
|
+
pass
|
|
143
|
+
|
|
144
|
+
# Clarification operations
|
|
145
|
+
@abstractmethod
|
|
146
|
+
def get_clarifications(self) -> List[Dict[str, Any]]:
|
|
147
|
+
pass
|
|
148
|
+
|
|
149
|
+
@abstractmethod
|
|
150
|
+
def get_clarification(self, clarification_id: str) -> Dict[str, Any]:
|
|
151
|
+
pass
|
|
152
|
+
|
|
153
|
+
# Award operations
|
|
154
|
+
@abstractmethod
|
|
155
|
+
def get_awards(self) -> List[Dict[str, Any]]:
|
|
156
|
+
pass
|
|
157
|
+
|
|
158
|
+
@abstractmethod
|
|
159
|
+
def get_award(self, award_id: str) -> Dict[str, Any]:
|
|
160
|
+
pass
|
|
161
|
+
|
|
162
|
+
# Event Feed operations
|
|
163
|
+
@abstractmethod
|
|
164
|
+
def get_event_feed(self, since_token: Optional[str] = None) -> List[Dict[str, Any]]:
|
|
165
|
+
pass
|