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.

@@ -0,0 +1,331 @@
1
+ import bisect
2
+ import json
3
+ import logging
4
+ from collections import defaultdict
5
+ from pathlib import Path
6
+ from typing import Any, Dict, List, Optional, Union
7
+
8
+ from fastapi import HTTPException
9
+
10
+ from xcpcio.ccs.base.types import FileAttr
11
+ from xcpcio.ccs.reader.base_ccs_reader import BaseCCSReader
12
+
13
+ logger = logging.getLogger(__name__)
14
+
15
+
16
+ class ContestPackageReader(BaseCCSReader):
17
+ def __init__(self, contest_package_dir: Path):
18
+ self.contest_package_dir = contest_package_dir
19
+ if not self.contest_package_dir.exists():
20
+ raise ValueError(f"Contest package directory does not exist: {contest_package_dir}")
21
+
22
+ self._load_indexes()
23
+
24
+ def _create_index_by_id(self, data: List[Dict[str, Any]], id_name: str) -> Dict[str, List[Dict]]:
25
+ res = defaultdict(list)
26
+ for item in data:
27
+ res[item[id_name]].append(item)
28
+ return res
29
+
30
+ def _load_json_file(self, filepath: str) -> Union[Dict[str, Any], List[Any]]:
31
+ full_path = self.contest_package_dir / filepath
32
+ try:
33
+ with open(full_path, "r", encoding="utf-8") as f:
34
+ return json.load(f)
35
+ except FileNotFoundError:
36
+ raise HTTPException(status_code=404, detail=f"File not found: {filepath}")
37
+ except json.JSONDecodeError as e:
38
+ raise HTTPException(status_code=500, detail=f"Invalid JSON in file {filepath}: {e}")
39
+
40
+ def _load_ndjson_file(self, filepath: str) -> List[Dict[str, Any]]:
41
+ full_path = self.contest_package_dir / filepath
42
+ try:
43
+ data = list()
44
+ with open(full_path, "r", encoding="utf-8") as f:
45
+ for line in f.readlines():
46
+ data.append(json.loads(line))
47
+ return data
48
+ except FileNotFoundError:
49
+ raise HTTPException(status_code=404, detail=f"File not found: {filepath}")
50
+ except json.JSONDecodeError as e:
51
+ raise HTTPException(status_code=500, detail=f"Invalid JSON in file {filepath}: {e}")
52
+
53
+ def _load_indexes(self) -> None:
54
+ self.access = self._load_json_file("access.json")
55
+
56
+ self.accounts = self._load_json_file("accounts.json")
57
+ self.accounts_by_id = {account["id"] for account in self.accounts}
58
+
59
+ self.api_info = self._load_json_file("api.json")
60
+
61
+ self.awards = self._load_json_file("awards.json")
62
+ self.awards_by_id = {award["id"] for award in self.awards}
63
+
64
+ self.clarifications = self._load_json_file("clarifications.json")
65
+ self.clarifications_by_id = {clarification["id"] for clarification in self.clarifications}
66
+
67
+ self.contest = self._load_json_file("contest.json")
68
+ self.contest_state = self._load_json_file("state.json")
69
+
70
+ self.groups = self._load_json_file("groups.json")
71
+ self.groups_by_id = {group["id"]: group for group in self.groups}
72
+
73
+ self.judgement_types = self._load_json_file("judgement-types.json")
74
+ self.judgement_types_by_id = {judgement_type["id"] for judgement_type in self.judgement_types}
75
+
76
+ self.judgements = self._load_json_file("judgements.json")
77
+ self.judgements_by_id = {judgement["id"] for judgement in self.judgements}
78
+ self.judgements_by_submission_id = self._create_index_by_id(self.judgements, "submission_id")
79
+
80
+ self.languages = self._load_json_file("languages.json")
81
+ self.languages_by_id = {language["id"] for language in self.languages}
82
+
83
+ self.organizations = self._load_json_file("organizations.json")
84
+ self.organizations_by_id = {org["id"]: org for org in self.organizations}
85
+
86
+ self.problems = self._load_json_file("problems.json")
87
+ self.problems_by_id = {problem["id"]: problem for problem in self.problems}
88
+
89
+ self.runs = self._load_json_file("runs.json")
90
+ self.runs_by_id = {run["id"] for run in self.runs}
91
+ self.runs_by_judgement_id = self._create_index_by_id(self.runs, "judgement_id")
92
+
93
+ self.submissions = self._load_json_file("submissions.json")
94
+ self.submissions_by_id = {submission["id"]: submission for submission in self.submissions}
95
+
96
+ self.teams = self._load_json_file("teams.json")
97
+ self.teams_by_id = {team["id"]: team for team in self.teams}
98
+
99
+ self.event_feed = self._load_ndjson_file("event-feed.ndjson")
100
+ self.event_feed_tokens = [event["token"] for event in self.event_feed]
101
+
102
+ self.contest_id = self.contest["id"]
103
+
104
+ def _get_file_attr(self, expected_href: str, base_path: Path, files: List[Dict]) -> FileAttr:
105
+ for file in files:
106
+ href = file["href"]
107
+ if href == expected_href:
108
+ filename = file["filename"]
109
+ mime_type = file["mime"]
110
+ filepath: Path = base_path / filename
111
+ if not filepath.exists():
112
+ raise FileNotFoundError(f"File not found: {filepath}")
113
+ return FileAttr(path=filepath, media_type=mime_type, name=filename)
114
+ raise KeyError(f"Href not found: {expected_href}")
115
+
116
+ # API Information
117
+ def get_api_info(self) -> Dict[str, Any]:
118
+ return self.api_info
119
+
120
+ def get_access(self) -> Dict[str, Any]:
121
+ return self.access
122
+
123
+ # Account operations
124
+ def get_accounts(self) -> List[Dict[str, Any]]:
125
+ return self.accounts
126
+
127
+ def get_account(self, account_id: str) -> Dict[str, Any]:
128
+ if account_id not in self.accounts_by_id:
129
+ raise HTTPException(status_code=404, detail=f"Account {account_id} not found")
130
+ return self.accounts_by_id[account_id]
131
+
132
+ # Contest operations
133
+ def get_contest_id(self) -> str:
134
+ return self.contest["id"]
135
+
136
+ def get_contest(self) -> Dict[str, Any]:
137
+ return self.contest
138
+
139
+ def get_contest_state(self) -> Dict[str, Any]:
140
+ return self.contest_state
141
+
142
+ def get_contest_banner(self) -> FileAttr:
143
+ expected_href = f"contests/{self.contest_id}/banner"
144
+ base_path = self.contest_package_dir / "contest"
145
+ files = self.contest.get("banner", [])
146
+
147
+ try:
148
+ return self._get_file_attr(expected_href, base_path, files)
149
+ except Exception as e:
150
+ raise HTTPException(status_code=404, detail=f"Banner not found. [contest_id={self.contest_id}] [err={e}]")
151
+
152
+ def get_contest_problemset(self) -> FileAttr:
153
+ expected_href = f"contests/{self.contest_id}/problemset"
154
+ base_path = self.contest_package_dir / "contest"
155
+ files = self.contest.get("problemset", [])
156
+
157
+ try:
158
+ return self._get_file_attr(expected_href, base_path, files)
159
+ except Exception as e:
160
+ raise HTTPException(
161
+ status_code=404, detail=f"Problemset not found. [contest_id={self.contest_id}] [err={e}]"
162
+ )
163
+
164
+ # Problem operations
165
+ def get_problems(self) -> List[Dict[str, Any]]:
166
+ return self.problems
167
+
168
+ def get_problem(self, problem_id: str) -> Dict[str, Any]:
169
+ if problem_id not in self.problems_by_id:
170
+ raise HTTPException(status_code=404, detail=f"Problem {problem_id} not found")
171
+ return self.problems_by_id[problem_id]
172
+
173
+ def get_problem_statement(self, problem_id: str) -> FileAttr:
174
+ expected_href = f"contests/{self.contest_id}/problems/{problem_id}/statement"
175
+ base_path = self.contest_package_dir / "problems" / problem_id
176
+ files = self.get_problem(problem_id).get("statement", [])
177
+
178
+ try:
179
+ return self._get_file_attr(expected_href, base_path, files)
180
+ except Exception as e:
181
+ raise HTTPException(
182
+ status_code=404,
183
+ detail=f"Problem statement not found. [contest_id={self.contest_id}] [problem_id={problem_id}] [err={e}]",
184
+ )
185
+
186
+ # Team operations
187
+ def get_teams(self) -> List[Dict[str, Any]]:
188
+ return self.teams
189
+
190
+ def get_team(self, team_id: str) -> Dict[str, Any]:
191
+ if team_id not in self.teams_by_id:
192
+ raise HTTPException(status_code=404, detail=f"Team {team_id} not found")
193
+ return self.teams_by_id[team_id]
194
+
195
+ def get_team_photo(self, team_id: str) -> FileAttr:
196
+ expected_href = f"contests/{self.contest_id}/teams/{team_id}/photo"
197
+ base_path = self.contest_package_dir / "teams" / team_id
198
+ files = self.get_team(team_id).get("photo", [])
199
+
200
+ try:
201
+ return self._get_file_attr(expected_href, base_path, files)
202
+ except Exception as e:
203
+ raise HTTPException(
204
+ status_code=404,
205
+ detail=f"Team photo not found. [contest_id={self.contest_id}] [team_id={team_id}] [err={e}]",
206
+ )
207
+
208
+ # Organization operations
209
+ def get_organizations(self) -> List[Dict[str, Any]]:
210
+ return self.organizations
211
+
212
+ def get_organization(self, organization_id: str) -> Dict[str, Any]:
213
+ if organization_id not in self.organizations_by_id:
214
+ raise HTTPException(status_code=404, detail=f"Organization {organization_id} not found")
215
+ return self.organizations_by_id[organization_id]
216
+
217
+ def get_organization_logo(self, organization_id: str) -> FileAttr:
218
+ expected_href = f"contests/{self.contest_id}/organizations/{organization_id}/logo"
219
+ base_path = self.contest_package_dir / "organizations" / organization_id
220
+ files = self.get_organization(organization_id).get("logo", [])
221
+
222
+ try:
223
+ return self._get_file_attr(expected_href, base_path, files)
224
+ except Exception as e:
225
+ raise HTTPException(
226
+ status_code=404,
227
+ detail=f"Organization logo not found. [contest_id={self.contest_id}] [organization_id={organization_id}] [err={e}]",
228
+ )
229
+
230
+ # Group operations
231
+ def get_groups(self) -> List[Dict[str, Any]]:
232
+ return self.groups
233
+
234
+ def get_group(self, group_id: str) -> Dict[str, Any]:
235
+ if group_id not in self.groups_by_id:
236
+ raise HTTPException(status_code=404, detail=f"Group {group_id} not found")
237
+ return self.groups_by_id[group_id]
238
+
239
+ # Language operations
240
+ def get_languages(self) -> List[Dict[str, Any]]:
241
+ return self.languages
242
+
243
+ def get_language(self, language_id: str) -> Dict[str, Any]:
244
+ if language_id not in self.languages_by_id:
245
+ raise HTTPException(status_code=404, detail=f"Language {language_id} not found")
246
+ return self.languages_by_id[language_id]
247
+
248
+ # Judgement type operations
249
+ def get_judgement_types(self) -> List[Dict[str, Any]]:
250
+ return self.judgement_types
251
+
252
+ def get_judgement_type(self, judgement_type_id: str) -> Dict[str, Any]:
253
+ if judgement_type_id not in self.judgement_types_by_id:
254
+ raise HTTPException(status_code=404, detail=f"Judgement type {judgement_type_id} not found")
255
+ return self.judgement_types_by_id[judgement_type_id]
256
+
257
+ # Submission operations
258
+ def get_submissions(self) -> List[Dict[str, Any]]:
259
+ return self.submissions
260
+
261
+ def get_submission(self, submission_id: str) -> Dict[str, Any]:
262
+ if submission_id not in self.submissions_by_id:
263
+ raise HTTPException(status_code=404, detail=f"Submission {submission_id} not found")
264
+ return self.submissions_by_id[submission_id]
265
+
266
+ def get_submission_file(self, submission_id: str) -> FileAttr:
267
+ expected_href = f"contests/{self.contest_id}/submissions/{submission_id}/files"
268
+ base_path = self.contest_package_dir / "submissions" / submission_id
269
+ files = self.get_submission(submission_id).get("files", [])
270
+
271
+ try:
272
+ return self._get_file_attr(expected_href, base_path, files)
273
+ except Exception as e:
274
+ raise HTTPException(
275
+ status_code=404,
276
+ detail=f"Submission file not found. [contest_id={self.contest_id}] [submission_id={submission_id}] [err={e}]",
277
+ )
278
+
279
+ # Judgement operations
280
+ def get_judgements(self, submission_id: Optional[str] = None) -> List[Dict[str, Any]]:
281
+ if submission_id is not None:
282
+ if submission_id not in self.judgements_by_submission_id:
283
+ raise HTTPException(status_code=404, detail=f"Submission id not found: {submission_id}")
284
+ return self.judgements_by_submission_id[submission_id]
285
+
286
+ return self.judgements
287
+
288
+ def get_judgement(self, judgement_id: str) -> Dict[str, Any]:
289
+ if judgement_id not in self.judgements_by_id:
290
+ raise HTTPException(status_code=404, detail=f"Judgement {judgement_id} not found")
291
+ return self.judgements_by_id[judgement_id]
292
+
293
+ # Run operations
294
+ def get_runs(self, judgement_id: Optional[str] = None) -> List[Dict[str, Any]]:
295
+ if judgement_id is not None:
296
+ if judgement_id not in self.runs_by_judgement_id:
297
+ raise HTTPException(status_code=404, detail=f"Judgement id not found: {judgement_id}")
298
+ return self.runs_by_judgement_id[judgement_id]
299
+
300
+ return self.runs
301
+
302
+ def get_run(self, run_id: str) -> Dict[str, Any]:
303
+ if run_id not in self.runs_by_id:
304
+ raise HTTPException(status_code=404, detail=f"Run {run_id} not found")
305
+ return self.runs_by_id[run_id]
306
+
307
+ # Clarification operations
308
+ def get_clarifications(self) -> List[Dict[str, Any]]:
309
+ return self.clarifications
310
+
311
+ def get_clarification(self, clarification_id: str) -> Dict[str, Any]:
312
+ if clarification_id not in self.clarifications_by_id:
313
+ raise HTTPException(status_code=404, detail=f"Clarification {clarification_id} not found")
314
+ return self.clarifications_by_id[clarification_id]
315
+
316
+ # Award operations
317
+ def get_awards(self) -> List[Dict[str, Any]]:
318
+ return self.awards
319
+
320
+ def get_award(self, award_id: str) -> Dict[str, Any]:
321
+ if award_id not in self.awards_by_id:
322
+ raise HTTPException(status_code=404, detail=f"Award {award_id} not found")
323
+ return self.awards_by_id[award_id]
324
+
325
+ # Event Feed operations
326
+ def get_event_feed(self, since_token: Optional[str] = None) -> List[Dict[str, Any]]:
327
+ if since_token is None:
328
+ return self.event_feed
329
+
330
+ idx = bisect.bisect_left(self.event_feed_tokens, since_token)
331
+ return self.event_feed[idx:]
@@ -0,0 +1,86 @@
1
+ Metadata-Version: 2.4
2
+ Name: xcpcio
3
+ Version: 0.64.1
4
+ Summary: xcpcio python lib
5
+ Project-URL: homepage, https://github.com/xcpcio/xcpcio
6
+ Project-URL: documentation, https://github.com/xcpcio/xcpcio
7
+ Project-URL: repository, https://github.com/xcpcio/xcpcio
8
+ Author-email: Dup4 <hi@dup4.com>
9
+ Maintainer-email: Dup4 <hi@dup4.com>, cubercsl <hi@cubercsl.site>
10
+ License-Expression: MIT
11
+ Keywords: xcpcio
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Topic :: Software Development :: Build Tools
17
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
18
+ Requires-Python: >=3.11
19
+ Requires-Dist: aiofiles>=23.0.0
20
+ Requires-Dist: aiohttp>=3.8.0
21
+ Requires-Dist: click>=8.0.0
22
+ Requires-Dist: fastapi>=0.117.1
23
+ Requires-Dist: pydantic>=2.11.7
24
+ Requires-Dist: pyyaml>=6.0.0
25
+ Requires-Dist: semver>=3.0.0
26
+ Requires-Dist: tenacity>=8.0.0
27
+ Requires-Dist: uvicorn>=0.36.0
28
+ Requires-Dist: zstandard>=0.25.0
29
+ Description-Content-Type: text/markdown
30
+
31
+ # xcpcio-python
32
+
33
+ Python library and CLI tools for XCPCIO.
34
+
35
+ ## Features
36
+
37
+ - **Type Definitions**: Pydantic models for contest data structures (teams, submissions, problems, etc.)
38
+ - **Constants**: Shared constants for submission statuses, time units, and penalty calculations
39
+ - **CCS Archiver**: CLI tool to archive CCS API data to contest package format
40
+ - **Contest API Server**: CLI tool to serve contest packages via CCS API
41
+ - **Cross-Language Compatibility**: Mirrors TypeScript types for data consistency
42
+
43
+ ## Installation
44
+
45
+ ```bash
46
+ pip install xcpcio
47
+ ```
48
+
49
+ Or install with [uv](https://github.com/astral-sh/uv):
50
+
51
+ ```bash
52
+ uv add xcpcio
53
+ ```
54
+
55
+ ## Development
56
+
57
+ ### Setup
58
+
59
+ ```bash
60
+ # Clone repository
61
+ git clone https://github.com/xcpcio/xcpcio.git
62
+ cd xcpcio/python
63
+
64
+ # Install dependencies with uv
65
+ uv sync
66
+ ```
67
+
68
+ ### Testing
69
+
70
+ ```bash
71
+ # Run tests
72
+ uv run pytest
73
+
74
+ # Run specific test file
75
+ uv run pytest tests/test_types.py
76
+ ```
77
+
78
+ ## Documentation
79
+
80
+ For detailed documentation, visit:
81
+
82
+ - [CCS Utility Guide](https://xcpcio.com/guide/ccs-utility)
83
+
84
+ ## License
85
+
86
+ MIT License &copy; 2020-PRESENT [Dup4](https://github.com/Dup4)
@@ -1,34 +1,39 @@
1
1
  xcpcio/__init__.py,sha256=kjd6itqBRSQ-OT83qUJXHt81KQQDRUtaIuykzfaWXLM,121
2
- xcpcio/__version__.py,sha256=95oCDxj_oHcOcxQzEfotvdWgY-p_3xsHZ2AaFTYVdR4,172
2
+ xcpcio/__version__.py,sha256=9uK7N7Uv0mL-z1c9U6OvlGT6m76UrMFPIkjXt_zHSEY,172
3
3
  xcpcio/constants.py,sha256=MjpAgNXiBlUsx1S09m7JNT-nekNDR-aE6ggvGL3fg0I,2297
4
4
  xcpcio/types.py,sha256=AkYby2haJgxwtozlgaPMG2ryAZdvsSc3sH-p6qXcM4g,6575
5
5
  xcpcio/ccs/__init__.py,sha256=LSoKFblEuSoIVBYcUxOFF8fn2bH2R6kSg9xNrBfzC0g,99
6
6
  xcpcio/ccs/contest_archiver.py,sha256=ICogyPzKfFRoO7J5D2Eu-3JwIirC3etMev7hyTjn4z8,16198
7
7
  xcpcio/ccs/api_server/__init__.py,sha256=ASvVJ_ibGkXFDSNmy05eb9gESXRS8hjYHCrBecSnaS0,174
8
- xcpcio/ccs/api_server/dependencies.py,sha256=5nosGVwY-3Mq7eK9C5ZOMEJFozzzw_kLlpCraFbJ9wY,1225
8
+ xcpcio/ccs/api_server/dependencies.py,sha256=cbLHcP91SaRBj2W9OfC0yCQ1fasI2DofxGUhPPNs3F8,1518
9
9
  xcpcio/ccs/api_server/server.py,sha256=3gft3MqDXvKWug5UCLhdV681bcQMRrewDkwQ7qSPtRU,2598
10
10
  xcpcio/ccs/api_server/routes/__init__.py,sha256=uz65H4L5Wzef7QPi5PsLQM1xbMdG6FoZ0Np0y039_2k,1537
11
11
  xcpcio/ccs/api_server/routes/access.py,sha256=O-RGLmgLNBQ-ccu8rlHOgonTjl02fYOdi3VTsUa-T0w,434
12
12
  xcpcio/ccs/api_server/routes/accounts.py,sha256=nAwiIz-y4fGmqHBniMuQifk9jVt4i9YPZWBB7w40q5Q,994
13
13
  xcpcio/ccs/api_server/routes/awards.py,sha256=gBPSFlDj6PM6Poys6JbO7VMsfCljKz6QrTjKEqQcS8w,957
14
14
  xcpcio/ccs/api_server/routes/clarifications.py,sha256=vvMNMvQkZTOn1VJ8C5U86gpIkN3yNrxBll1VMTJjzQg,1046
15
- xcpcio/ccs/api_server/routes/contests.py,sha256=o-A5wMDy5UC9wM71K3ruMmP4p4lSoU9B3XmDGPV0M7g,4203
15
+ xcpcio/ccs/api_server/routes/contests.py,sha256=VbvegfP5ZnVNKdqoHmy7_Wd2L6xEBqON7UD2OQ6sQ8A,2979
16
16
  xcpcio/ccs/api_server/routes/general.py,sha256=xLH-sqyeC4dSd6SUYpjg-w-1ZtmSqZAgIupoVOyYwD4,763
17
17
  xcpcio/ccs/api_server/routes/groups.py,sha256=kWKFFty2iWckMN-j6G3NbgMVKCPQ478_mYKrYsHXgMA,949
18
18
  xcpcio/ccs/api_server/routes/judgement_types.py,sha256=S3Dt0VYjntVBQBvLYhJGcDNSy7O8Zh7b7NSfrronZSU,1057
19
19
  xcpcio/ccs/api_server/routes/judgements.py,sha256=3w0LHYbZazli_v587l98U23r5PuolRs_vtbNLMwgjTU,1127
20
20
  xcpcio/ccs/api_server/routes/languages.py,sha256=2BYqTaSBWx0E5XlaTjofElLb7z4HTsVs3rrozdyqz0s,985
21
- xcpcio/ccs/api_server/routes/organizations.py,sha256=608tPsK2bCUmqSalEFysC1zQf3FDDIeweT6PLLrOyLg,2515
22
- xcpcio/ccs/api_server/routes/problems.py,sha256=2YFw_ODw-6L015UgpL1p-lLItgCgnqDamni6_9Qmbdk,2458
21
+ xcpcio/ccs/api_server/routes/organizations.py,sha256=6xMl0Iqo7pjLaJbR7L1NWwo8JXwnXhH2O8hJKiLoRa0,1712
22
+ xcpcio/ccs/api_server/routes/problems.py,sha256=XAhXkShHwewZdugsffLj1UxvFNSorrxc-PyZymdBQTY,1632
23
23
  xcpcio/ccs/api_server/routes/runs.py,sha256=1L6fo1KPcDeKwOEkTxCwEetmyfNKY_Z0uxATqBJftIs,1046
24
- xcpcio/ccs/api_server/routes/submissions.py,sha256=UKgDt9yv45Hfpr8P_8-2Po0jF4HvzG87zgkcb1YIKVc,2542
25
- xcpcio/ccs/api_server/routes/teams.py,sha256=9nWz0tvMnccCNEzSN9DY-ZJtxlgaka9YSPe_5Gykbl8,2282
24
+ xcpcio/ccs/api_server/routes/submissions.py,sha256=ByVX_wzab0Q9EHFmuE8JQtikvboMXtt_kXecEy6yiRc,1675
25
+ xcpcio/ccs/api_server/routes/teams.py,sha256=euDs-GDHLqmJS3zPTNxm9LfKXQYEuhwAlX-BF2tcrk4,1556
26
26
  xcpcio/ccs/api_server/services/__init__.py,sha256=WQLNrLVomhtICl8HlFYaCoRewIHVZfUiiwrSBUOOWDg,171
27
- xcpcio/ccs/api_server/services/contest_service.py,sha256=rMbAm7HVw5dHMTwp1tLAP0QhlCKlEMWQ65LZLZzjbfg,13210
27
+ xcpcio/ccs/api_server/services/contest_service.py,sha256=Jmt7h4rQoZLXPAkHx3FdQNsRtYOPpdSOlh7QA41nIm4,7467
28
+ xcpcio/ccs/base/__init__.py,sha256=JYKVtcQG-VGM2OfCg6VhxcXCeCp7r2zxpg_7s9AThS0,39
29
+ xcpcio/ccs/base/types.py,sha256=NfAG-XJjex7p2DfFTRecLWhmwpeO8Ul42nNMnZH39sM,137
28
30
  xcpcio/ccs/model/__init__.py,sha256=cZE1q5JY-iHDEKZpsx0UZaMhH-23H4oAHaYOkW7dZ5s,43
29
31
  xcpcio/ccs/model/model_2023_06/__init__.py,sha256=OmDQZqmigBpL64LXk5lIOGoQ3Uqis8-2z6qQpOO5aJc,167
30
32
  xcpcio/ccs/model/model_2023_06/model.py,sha256=bVMDWpJTwPSpz1fHPxWrWerxCBIboH3LKVZpIZGQ2pY,15287
31
- xcpcio-0.63.7.dist-info/METADATA,sha256=oWlZ8fA8EO1Ly_wvo8JUlxbeDmlV7PVKPKum4ydxzDk,1079
32
- xcpcio-0.63.7.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
33
- xcpcio-0.63.7.dist-info/entry_points.txt,sha256=qvzh8oDJxIHqTN-rg2lRN6xR99AqxbWnlAQI7uzDibI,59
34
- xcpcio-0.63.7.dist-info/RECORD,,
33
+ xcpcio/ccs/reader/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
34
+ xcpcio/ccs/reader/base_ccs_reader.py,sha256=fS7M0hD3-3PAEV7EYyorVZsBhD4HtABkQeV4fXNldhA,3912
35
+ xcpcio/ccs/reader/contest_package_reader.py,sha256=DPuKp3eptRNMi0-Ssx_K2roN5_0xXILMltucWy1NTPI,14173
36
+ xcpcio-0.64.1.dist-info/METADATA,sha256=sB4H7oOFsFnceOyopwkOXsDfjPr1-LRcgQ_1jwRHDPE,2202
37
+ xcpcio-0.64.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
38
+ xcpcio-0.64.1.dist-info/entry_points.txt,sha256=qvzh8oDJxIHqTN-rg2lRN6xR99AqxbWnlAQI7uzDibI,59
39
+ xcpcio-0.64.1.dist-info/RECORD,,
@@ -1,30 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: xcpcio
3
- Version: 0.63.7
4
- Summary: xcpcio python lib
5
- Project-URL: homepage, https://github.com/xcpcio/xcpcio
6
- Project-URL: documentation, https://github.com/xcpcio/xcpcio
7
- Project-URL: repository, https://github.com/xcpcio/xcpcio
8
- Author-email: Dup4 <hi@dup4.com>
9
- Maintainer-email: Dup4 <hi@dup4.com>, cubercsl <hi@cubercsl.site>
10
- License-Expression: MIT
11
- Keywords: xcpcio
12
- Classifier: Programming Language :: Python :: 3
13
- Classifier: Programming Language :: Python :: 3.11
14
- Classifier: Programming Language :: Python :: 3.12
15
- Classifier: Programming Language :: Python :: 3.13
16
- Classifier: Topic :: Software Development :: Build Tools
17
- Classifier: Topic :: Software Development :: Libraries :: Python Modules
18
- Requires-Python: >=3.11
19
- Requires-Dist: aiofiles>=23.0.0
20
- Requires-Dist: aiohttp>=3.8.0
21
- Requires-Dist: click>=8.0.0
22
- Requires-Dist: fastapi>=0.117.1
23
- Requires-Dist: pydantic>=2.11.7
24
- Requires-Dist: pyyaml>=6.0.0
25
- Requires-Dist: semver>=3.0.0
26
- Requires-Dist: tenacity>=8.0.0
27
- Requires-Dist: uvicorn>=0.36.0
28
- Description-Content-Type: text/markdown
29
-
30
- # xcpcio-python