xcpcio 0.65.1__tar.gz → 0.66.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.
Potentially problematic release.
This version of xcpcio might be problematic. Click here for more details.
- {xcpcio-0.65.1 → xcpcio-0.66.0}/PKG-INFO +1 -1
- {xcpcio-0.65.1 → xcpcio-0.66.0}/tests/test_contest.py +1 -1
- {xcpcio-0.65.1 → xcpcio-0.66.0}/tests/test_submission.py +1 -2
- {xcpcio-0.65.1 → xcpcio-0.66.0}/tests/test_team.py +33 -5
- {xcpcio-0.65.1 → xcpcio-0.66.0}/xcpcio/__version__.py +1 -1
- {xcpcio-0.65.1 → xcpcio-0.66.0}/xcpcio/clics/reader/contest_package_reader.py +7 -7
- {xcpcio-0.65.1 → xcpcio-0.66.0}/xcpcio/types.py +14 -6
- {xcpcio-0.65.1 → xcpcio-0.66.0}/.gitignore +0 -0
- {xcpcio-0.65.1 → xcpcio-0.66.0}/.python-version +0 -0
- {xcpcio-0.65.1 → xcpcio-0.66.0}/README.md +0 -0
- {xcpcio-0.65.1 → xcpcio-0.66.0}/pyproject.toml +0 -0
- {xcpcio-0.65.1 → xcpcio-0.66.0}/scripts/generate_ccs_models.sh +0 -0
- {xcpcio-0.65.1 → xcpcio-0.66.0}/tests/__init__.py +0 -0
- {xcpcio-0.65.1 → xcpcio-0.66.0}/tests/test_types.py +0 -0
- {xcpcio-0.65.1 → xcpcio-0.66.0}/uv.lock +0 -0
- {xcpcio-0.65.1 → xcpcio-0.66.0}/xcpcio/__init__.py +0 -0
- {xcpcio-0.65.1 → xcpcio-0.66.0}/xcpcio/api/__init__.py +0 -0
- {xcpcio-0.65.1 → xcpcio-0.66.0}/xcpcio/api/client.py +0 -0
- {xcpcio-0.65.1 → xcpcio-0.66.0}/xcpcio/api/models.py +0 -0
- {xcpcio-0.65.1 → xcpcio-0.66.0}/xcpcio/app/clics_archiver.py +0 -0
- {xcpcio-0.65.1 → xcpcio-0.66.0}/xcpcio/app/clics_server.py +0 -0
- {xcpcio-0.65.1 → xcpcio-0.66.0}/xcpcio/clics/__init__.py +0 -0
- {xcpcio-0.65.1 → xcpcio-0.66.0}/xcpcio/clics/api_server/__init__.py +0 -0
- {xcpcio-0.65.1 → xcpcio-0.66.0}/xcpcio/clics/api_server/app.py +0 -0
- {xcpcio-0.65.1 → xcpcio-0.66.0}/xcpcio/clics/api_server/dependencies.py +0 -0
- {xcpcio-0.65.1 → xcpcio-0.66.0}/xcpcio/clics/api_server/routes/__init__.py +0 -0
- {xcpcio-0.65.1 → xcpcio-0.66.0}/xcpcio/clics/api_server/routes/access.py +0 -0
- {xcpcio-0.65.1 → xcpcio-0.66.0}/xcpcio/clics/api_server/routes/accounts.py +0 -0
- {xcpcio-0.65.1 → xcpcio-0.66.0}/xcpcio/clics/api_server/routes/awards.py +0 -0
- {xcpcio-0.65.1 → xcpcio-0.66.0}/xcpcio/clics/api_server/routes/clarifications.py +0 -0
- {xcpcio-0.65.1 → xcpcio-0.66.0}/xcpcio/clics/api_server/routes/contests.py +0 -0
- {xcpcio-0.65.1 → xcpcio-0.66.0}/xcpcio/clics/api_server/routes/general.py +0 -0
- {xcpcio-0.65.1 → xcpcio-0.66.0}/xcpcio/clics/api_server/routes/groups.py +0 -0
- {xcpcio-0.65.1 → xcpcio-0.66.0}/xcpcio/clics/api_server/routes/judgement_types.py +0 -0
- {xcpcio-0.65.1 → xcpcio-0.66.0}/xcpcio/clics/api_server/routes/judgements.py +0 -0
- {xcpcio-0.65.1 → xcpcio-0.66.0}/xcpcio/clics/api_server/routes/languages.py +0 -0
- {xcpcio-0.65.1 → xcpcio-0.66.0}/xcpcio/clics/api_server/routes/organizations.py +0 -0
- {xcpcio-0.65.1 → xcpcio-0.66.0}/xcpcio/clics/api_server/routes/problems.py +0 -0
- {xcpcio-0.65.1 → xcpcio-0.66.0}/xcpcio/clics/api_server/routes/runs.py +0 -0
- {xcpcio-0.65.1 → xcpcio-0.66.0}/xcpcio/clics/api_server/routes/submissions.py +0 -0
- {xcpcio-0.65.1 → xcpcio-0.66.0}/xcpcio/clics/api_server/routes/teams.py +0 -0
- {xcpcio-0.65.1 → xcpcio-0.66.0}/xcpcio/clics/api_server/server.py +0 -0
- {xcpcio-0.65.1 → xcpcio-0.66.0}/xcpcio/clics/api_server/services/__init__.py +0 -0
- {xcpcio-0.65.1 → xcpcio-0.66.0}/xcpcio/clics/api_server/services/contest_service.py +0 -0
- {xcpcio-0.65.1 → xcpcio-0.66.0}/xcpcio/clics/base/__init__.py +0 -0
- {xcpcio-0.65.1 → xcpcio-0.66.0}/xcpcio/clics/base/types.py +0 -0
- {xcpcio-0.65.1 → xcpcio-0.66.0}/xcpcio/clics/clics_api_client.py +0 -0
- {xcpcio-0.65.1 → xcpcio-0.66.0}/xcpcio/clics/contest_archiver.py +0 -0
- {xcpcio-0.65.1 → xcpcio-0.66.0}/xcpcio/clics/model/__init__.py +0 -0
- {xcpcio-0.65.1 → xcpcio-0.66.0}/xcpcio/clics/model/model_2023_06/__init__.py +0 -0
- {xcpcio-0.65.1 → xcpcio-0.66.0}/xcpcio/clics/model/model_2023_06/model.py +0 -0
- {xcpcio-0.65.1 → xcpcio-0.66.0}/xcpcio/clics/reader/__init__.py +0 -0
- {xcpcio-0.65.1 → xcpcio-0.66.0}/xcpcio/clics/reader/interface.py +0 -0
- {xcpcio-0.65.1 → xcpcio-0.66.0}/xcpcio/constants.py +0 -0
|
@@ -30,7 +30,7 @@ class TestContest:
|
|
|
30
30
|
|
|
31
31
|
# Check default values
|
|
32
32
|
assert contest.status_time_display == constants.FULL_STATUS_TIME_DISPLAY
|
|
33
|
-
assert
|
|
33
|
+
assert contest.options is None
|
|
34
34
|
|
|
35
35
|
def test_contest_creation_with_values(self):
|
|
36
36
|
"""Test Contest creation with provided values"""
|
|
@@ -13,7 +13,6 @@ class TestSubmission:
|
|
|
13
13
|
"""Test Submission creation with default values"""
|
|
14
14
|
submission = Submission()
|
|
15
15
|
assert submission.id is None
|
|
16
|
-
assert submission.submission_id is None
|
|
17
16
|
assert submission.team_id == ""
|
|
18
17
|
assert submission.problem_id == 0
|
|
19
18
|
assert submission.timestamp == 0
|
|
@@ -215,7 +214,7 @@ class TestSubmissions:
|
|
|
215
214
|
problem_id=1,
|
|
216
215
|
timestamp=123,
|
|
217
216
|
),
|
|
218
|
-
Submission(), # All defaults
|
|
217
|
+
Submission(id="sub_default"), # All other defaults
|
|
219
218
|
]
|
|
220
219
|
)
|
|
221
220
|
|
|
@@ -93,14 +93,21 @@ class TestTeamSerialization:
|
|
|
93
93
|
assert team_dict["coaches"] == "Dr. Smith"
|
|
94
94
|
assert team_dict["location"] == "Building A"
|
|
95
95
|
assert team_dict["group"] == ["undergraduate", "local"]
|
|
96
|
-
assert
|
|
96
|
+
assert "extra" not in team_dict # extra field is excluded from serialization
|
|
97
97
|
|
|
98
98
|
def test_model_validate(self, sample_team: Team):
|
|
99
99
|
"""Test Team model_validate method"""
|
|
100
100
|
team_dict = sample_team.model_dump()
|
|
101
101
|
reconstructed_team = Team.model_validate(team_dict)
|
|
102
102
|
|
|
103
|
-
assert reconstructed_team == sample_team
|
|
103
|
+
assert reconstructed_team.id == sample_team.id
|
|
104
|
+
assert reconstructed_team.name == sample_team.name
|
|
105
|
+
assert reconstructed_team.organization == sample_team.organization
|
|
106
|
+
assert reconstructed_team.members == sample_team.members
|
|
107
|
+
assert reconstructed_team.coaches == sample_team.coaches
|
|
108
|
+
assert reconstructed_team.location == sample_team.location
|
|
109
|
+
assert reconstructed_team.group == sample_team.group
|
|
110
|
+
assert reconstructed_team.extra == {} # extra is not serialized
|
|
104
111
|
|
|
105
112
|
def test_model_dump_json(self, sample_team: Team):
|
|
106
113
|
"""Test Team model_dump_json method"""
|
|
@@ -117,21 +124,42 @@ class TestTeamSerialization:
|
|
|
117
124
|
team_json = sample_team.model_dump_json()
|
|
118
125
|
reconstructed_team = Team.model_validate_json(team_json)
|
|
119
126
|
|
|
120
|
-
assert reconstructed_team == sample_team
|
|
127
|
+
assert reconstructed_team.id == sample_team.id
|
|
128
|
+
assert reconstructed_team.name == sample_team.name
|
|
129
|
+
assert reconstructed_team.organization == sample_team.organization
|
|
130
|
+
assert reconstructed_team.members == sample_team.members
|
|
131
|
+
assert reconstructed_team.coaches == sample_team.coaches
|
|
132
|
+
assert reconstructed_team.location == sample_team.location
|
|
133
|
+
assert reconstructed_team.group == sample_team.group
|
|
134
|
+
assert reconstructed_team.extra == {} # extra is not serialized
|
|
121
135
|
|
|
122
136
|
def test_round_trip_dict(self, sample_team: Team):
|
|
123
137
|
"""Test complete round-trip through dict serialization"""
|
|
124
138
|
team_dict = sample_team.model_dump()
|
|
125
139
|
reconstructed_team = Team.model_validate(team_dict)
|
|
126
140
|
|
|
127
|
-
assert reconstructed_team == sample_team
|
|
141
|
+
assert reconstructed_team.id == sample_team.id
|
|
142
|
+
assert reconstructed_team.name == sample_team.name
|
|
143
|
+
assert reconstructed_team.organization == sample_team.organization
|
|
144
|
+
assert reconstructed_team.members == sample_team.members
|
|
145
|
+
assert reconstructed_team.coaches == sample_team.coaches
|
|
146
|
+
assert reconstructed_team.location == sample_team.location
|
|
147
|
+
assert reconstructed_team.group == sample_team.group
|
|
148
|
+
assert reconstructed_team.extra == {} # extra is excluded and won't round-trip
|
|
128
149
|
|
|
129
150
|
def test_round_trip_json(self, sample_team: Team):
|
|
130
151
|
"""Test complete round-trip through JSON serialization"""
|
|
131
152
|
team_json = sample_team.model_dump_json()
|
|
132
153
|
reconstructed_team = Team.model_validate_json(team_json)
|
|
133
154
|
|
|
134
|
-
assert reconstructed_team == sample_team
|
|
155
|
+
assert reconstructed_team.id == sample_team.id
|
|
156
|
+
assert reconstructed_team.name == sample_team.name
|
|
157
|
+
assert reconstructed_team.organization == sample_team.organization
|
|
158
|
+
assert reconstructed_team.members == sample_team.members
|
|
159
|
+
assert reconstructed_team.coaches == sample_team.coaches
|
|
160
|
+
assert reconstructed_team.location == sample_team.location
|
|
161
|
+
assert reconstructed_team.group == sample_team.group
|
|
162
|
+
assert reconstructed_team.extra == {} # extra is excluded and won't round-trip
|
|
135
163
|
|
|
136
164
|
def test_minimal_team_serialization(self):
|
|
137
165
|
"""Test serialization of team with default/minimal values"""
|
|
@@ -54,15 +54,15 @@ class ContestPackageReader(BaseContestReader):
|
|
|
54
54
|
self.access = self._load_json_file("access.json")
|
|
55
55
|
|
|
56
56
|
self.accounts = self._load_json_file("accounts.json")
|
|
57
|
-
self.accounts_by_id = {account["id"] for account in self.accounts}
|
|
57
|
+
self.accounts_by_id = {account["id"]: account for account in self.accounts}
|
|
58
58
|
|
|
59
59
|
self.api_info = self._load_json_file("api.json")
|
|
60
60
|
|
|
61
61
|
self.awards = self._load_json_file("awards.json")
|
|
62
|
-
self.awards_by_id = {award["id"] for award in self.awards}
|
|
62
|
+
self.awards_by_id = {award["id"]: award for award in self.awards}
|
|
63
63
|
|
|
64
64
|
self.clarifications = self._load_json_file("clarifications.json")
|
|
65
|
-
self.clarifications_by_id = {clarification["id"] for clarification in self.clarifications}
|
|
65
|
+
self.clarifications_by_id = {clarification["id"]: clarification for clarification in self.clarifications}
|
|
66
66
|
|
|
67
67
|
self.contest = self._load_json_file("contest.json")
|
|
68
68
|
self.contest_state = self._load_json_file("state.json")
|
|
@@ -71,14 +71,14 @@ class ContestPackageReader(BaseContestReader):
|
|
|
71
71
|
self.groups_by_id = {group["id"]: group for group in self.groups}
|
|
72
72
|
|
|
73
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}
|
|
74
|
+
self.judgement_types_by_id = {judgement_type["id"]: judgement_type for judgement_type in self.judgement_types}
|
|
75
75
|
|
|
76
76
|
self.judgements = self._load_json_file("judgements.json")
|
|
77
|
-
self.judgements_by_id = {judgement["id"] for judgement in self.judgements}
|
|
77
|
+
self.judgements_by_id = {judgement["id"]: judgement for judgement in self.judgements}
|
|
78
78
|
self.judgements_by_submission_id = self._create_index_by_id(self.judgements, "submission_id")
|
|
79
79
|
|
|
80
80
|
self.languages = self._load_json_file("languages.json")
|
|
81
|
-
self.languages_by_id = {language["id"] for language in self.languages}
|
|
81
|
+
self.languages_by_id = {language["id"]: language for language in self.languages}
|
|
82
82
|
|
|
83
83
|
self.organizations = self._load_json_file("organizations.json")
|
|
84
84
|
self.organizations_by_id = {org["id"]: org for org in self.organizations}
|
|
@@ -87,7 +87,7 @@ class ContestPackageReader(BaseContestReader):
|
|
|
87
87
|
self.problems_by_id = {problem["id"]: problem for problem in self.problems}
|
|
88
88
|
|
|
89
89
|
self.runs = self._load_json_file("runs.json")
|
|
90
|
-
self.runs_by_id = {run["id"] for run in self.runs}
|
|
90
|
+
self.runs_by_id = {run["id"]: run for run in self.runs}
|
|
91
91
|
self.runs_by_judgement_id = self._create_index_by_id(self.runs, "judgement_id")
|
|
92
92
|
|
|
93
93
|
self.submissions = self._load_json_file("submissions.json")
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import Dict, List, Literal, Optional, Union
|
|
1
|
+
from typing import Any, Dict, List, Literal, Optional, Union
|
|
2
2
|
|
|
3
3
|
from pydantic import BaseModel, Field, RootModel
|
|
4
4
|
|
|
@@ -88,6 +88,7 @@ class BalloonColor(BaseModel):
|
|
|
88
88
|
|
|
89
89
|
class Person(BaseModel):
|
|
90
90
|
name: Text
|
|
91
|
+
|
|
91
92
|
cf_id: Optional[str] = None
|
|
92
93
|
icpc_id: Optional[str] = None
|
|
93
94
|
|
|
@@ -98,9 +99,12 @@ Persons = List[Person]
|
|
|
98
99
|
class Problem(BaseModel):
|
|
99
100
|
id: str
|
|
100
101
|
label: str
|
|
102
|
+
|
|
101
103
|
name: Optional[Text] = None
|
|
104
|
+
|
|
102
105
|
time_limit: Optional[str] = None
|
|
103
106
|
memory_limit: Optional[str] = None
|
|
107
|
+
|
|
104
108
|
balloon_color: Optional[BalloonColor] = None
|
|
105
109
|
|
|
106
110
|
|
|
@@ -112,8 +116,7 @@ class SubmissionReaction(BaseModel):
|
|
|
112
116
|
|
|
113
117
|
|
|
114
118
|
class Submission(BaseModel):
|
|
115
|
-
id:
|
|
116
|
-
submission_id: Optional[str] = None
|
|
119
|
+
id: str = None
|
|
117
120
|
|
|
118
121
|
team_id: str = ""
|
|
119
122
|
problem_id: Union[int, str] = 0
|
|
@@ -144,11 +147,12 @@ class Team(BaseModel):
|
|
|
144
147
|
members: Optional[Union[Text, List[Text], Persons]] = None
|
|
145
148
|
|
|
146
149
|
badge: Optional[Image] = None
|
|
150
|
+
photo: Optional[Image] = None
|
|
147
151
|
|
|
148
152
|
location: Optional[str] = None
|
|
149
153
|
icpc_id: Optional[str] = None
|
|
150
154
|
|
|
151
|
-
extra: Dict[str,
|
|
155
|
+
extra: Dict[str, Any] = Field(default_factory=dict, exclude=True)
|
|
152
156
|
|
|
153
157
|
def add_group(self, group: str):
|
|
154
158
|
if group not in self.group:
|
|
@@ -166,9 +170,13 @@ class Teams(RootModel[List[Team]]):
|
|
|
166
170
|
class ContestOptions(BaseModel):
|
|
167
171
|
calculation_of_penalty: Optional[CalculationOfPenalty] = None
|
|
168
172
|
submission_timestamp_unit: Optional[TimeUnit] = None
|
|
173
|
+
|
|
169
174
|
has_reaction_videos: Optional[bool] = None
|
|
170
175
|
reaction_video_url_template: Optional[str] = None
|
|
171
176
|
|
|
177
|
+
has_team_photos: Optional[bool] = None
|
|
178
|
+
team_photo_url_template: Optional[Image] = None
|
|
179
|
+
|
|
172
180
|
|
|
173
181
|
class Contest(BaseModel):
|
|
174
182
|
contest_name: Text = ""
|
|
@@ -202,9 +210,9 @@ class Contest(BaseModel):
|
|
|
202
210
|
|
|
203
211
|
version: Optional[str] = None
|
|
204
212
|
|
|
205
|
-
options: ContestOptions =
|
|
213
|
+
options: Optional[ContestOptions] = None
|
|
206
214
|
|
|
207
|
-
unfrozen_time: int = 0x3F3F3F3F3F3F3F3F
|
|
215
|
+
unfrozen_time: int = Field(default=0x3F3F3F3F3F3F3F3F, exclude=True)
|
|
208
216
|
|
|
209
217
|
def append_balloon_color(self, color: BalloonColor):
|
|
210
218
|
if self.balloon_color is None:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|