xcpcio 0.69.1__tar.gz → 0.69.3__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.

Files changed (56) hide show
  1. {xcpcio-0.69.1 → xcpcio-0.69.3}/PKG-INFO +1 -1
  2. {xcpcio-0.69.1 → xcpcio-0.69.3}/xcpcio/__version__.py +1 -1
  3. {xcpcio-0.69.1 → xcpcio-0.69.3}/xcpcio/clics/contest_uploader.py +26 -2
  4. {xcpcio-0.69.1 → xcpcio-0.69.3}/xcpcio/clics/reader/contest_package_reader.py +32 -20
  5. {xcpcio-0.69.1 → xcpcio-0.69.3}/.gitignore +0 -0
  6. {xcpcio-0.69.1 → xcpcio-0.69.3}/.python-version +0 -0
  7. {xcpcio-0.69.1 → xcpcio-0.69.3}/README.md +0 -0
  8. {xcpcio-0.69.1 → xcpcio-0.69.3}/pyproject.toml +0 -0
  9. {xcpcio-0.69.1 → xcpcio-0.69.3}/scripts/generate_ccs_models.sh +0 -0
  10. {xcpcio-0.69.1 → xcpcio-0.69.3}/tests/__init__.py +0 -0
  11. {xcpcio-0.69.1 → xcpcio-0.69.3}/tests/test_contest.py +0 -0
  12. {xcpcio-0.69.1 → xcpcio-0.69.3}/tests/test_submission.py +0 -0
  13. {xcpcio-0.69.1 → xcpcio-0.69.3}/tests/test_team.py +0 -0
  14. {xcpcio-0.69.1 → xcpcio-0.69.3}/tests/test_types.py +0 -0
  15. {xcpcio-0.69.1 → xcpcio-0.69.3}/uv.lock +0 -0
  16. {xcpcio-0.69.1 → xcpcio-0.69.3}/xcpcio/__init__.py +0 -0
  17. {xcpcio-0.69.1 → xcpcio-0.69.3}/xcpcio/api/__init__.py +0 -0
  18. {xcpcio-0.69.1 → xcpcio-0.69.3}/xcpcio/api/client.py +0 -0
  19. {xcpcio-0.69.1 → xcpcio-0.69.3}/xcpcio/api/models.py +0 -0
  20. {xcpcio-0.69.1 → xcpcio-0.69.3}/xcpcio/app/clics_archiver.py +0 -0
  21. {xcpcio-0.69.1 → xcpcio-0.69.3}/xcpcio/app/clics_server.py +0 -0
  22. {xcpcio-0.69.1 → xcpcio-0.69.3}/xcpcio/app/clics_uploader.py +0 -0
  23. {xcpcio-0.69.1 → xcpcio-0.69.3}/xcpcio/clics/__init__.py +0 -0
  24. {xcpcio-0.69.1 → xcpcio-0.69.3}/xcpcio/clics/api_server/__init__.py +0 -0
  25. {xcpcio-0.69.1 → xcpcio-0.69.3}/xcpcio/clics/api_server/app.py +0 -0
  26. {xcpcio-0.69.1 → xcpcio-0.69.3}/xcpcio/clics/api_server/dependencies.py +0 -0
  27. {xcpcio-0.69.1 → xcpcio-0.69.3}/xcpcio/clics/api_server/routes/__init__.py +0 -0
  28. {xcpcio-0.69.1 → xcpcio-0.69.3}/xcpcio/clics/api_server/routes/access.py +0 -0
  29. {xcpcio-0.69.1 → xcpcio-0.69.3}/xcpcio/clics/api_server/routes/accounts.py +0 -0
  30. {xcpcio-0.69.1 → xcpcio-0.69.3}/xcpcio/clics/api_server/routes/awards.py +0 -0
  31. {xcpcio-0.69.1 → xcpcio-0.69.3}/xcpcio/clics/api_server/routes/clarifications.py +0 -0
  32. {xcpcio-0.69.1 → xcpcio-0.69.3}/xcpcio/clics/api_server/routes/contests.py +0 -0
  33. {xcpcio-0.69.1 → xcpcio-0.69.3}/xcpcio/clics/api_server/routes/general.py +0 -0
  34. {xcpcio-0.69.1 → xcpcio-0.69.3}/xcpcio/clics/api_server/routes/groups.py +0 -0
  35. {xcpcio-0.69.1 → xcpcio-0.69.3}/xcpcio/clics/api_server/routes/judgement_types.py +0 -0
  36. {xcpcio-0.69.1 → xcpcio-0.69.3}/xcpcio/clics/api_server/routes/judgements.py +0 -0
  37. {xcpcio-0.69.1 → xcpcio-0.69.3}/xcpcio/clics/api_server/routes/languages.py +0 -0
  38. {xcpcio-0.69.1 → xcpcio-0.69.3}/xcpcio/clics/api_server/routes/organizations.py +0 -0
  39. {xcpcio-0.69.1 → xcpcio-0.69.3}/xcpcio/clics/api_server/routes/problems.py +0 -0
  40. {xcpcio-0.69.1 → xcpcio-0.69.3}/xcpcio/clics/api_server/routes/runs.py +0 -0
  41. {xcpcio-0.69.1 → xcpcio-0.69.3}/xcpcio/clics/api_server/routes/submissions.py +0 -0
  42. {xcpcio-0.69.1 → xcpcio-0.69.3}/xcpcio/clics/api_server/routes/teams.py +0 -0
  43. {xcpcio-0.69.1 → xcpcio-0.69.3}/xcpcio/clics/api_server/server.py +0 -0
  44. {xcpcio-0.69.1 → xcpcio-0.69.3}/xcpcio/clics/api_server/services/__init__.py +0 -0
  45. {xcpcio-0.69.1 → xcpcio-0.69.3}/xcpcio/clics/api_server/services/contest_service.py +0 -0
  46. {xcpcio-0.69.1 → xcpcio-0.69.3}/xcpcio/clics/base/__init__.py +0 -0
  47. {xcpcio-0.69.1 → xcpcio-0.69.3}/xcpcio/clics/base/types.py +0 -0
  48. {xcpcio-0.69.1 → xcpcio-0.69.3}/xcpcio/clics/clics_api_client.py +0 -0
  49. {xcpcio-0.69.1 → xcpcio-0.69.3}/xcpcio/clics/contest_archiver.py +0 -0
  50. {xcpcio-0.69.1 → xcpcio-0.69.3}/xcpcio/clics/model/__init__.py +0 -0
  51. {xcpcio-0.69.1 → xcpcio-0.69.3}/xcpcio/clics/model/model_2023_06/__init__.py +0 -0
  52. {xcpcio-0.69.1 → xcpcio-0.69.3}/xcpcio/clics/model/model_2023_06/model.py +0 -0
  53. {xcpcio-0.69.1 → xcpcio-0.69.3}/xcpcio/clics/reader/__init__.py +0 -0
  54. {xcpcio-0.69.1 → xcpcio-0.69.3}/xcpcio/clics/reader/interface.py +0 -0
  55. {xcpcio-0.69.1 → xcpcio-0.69.3}/xcpcio/constants.py +0 -0
  56. {xcpcio-0.69.1 → xcpcio-0.69.3}/xcpcio/types.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xcpcio
3
- Version: 0.69.1
3
+ Version: 0.69.3
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
@@ -1,4 +1,4 @@
1
1
  # This file is auto-generated by Hatchling. As such, do not:
2
2
  # - modify
3
3
  # - track in version control e.g. be sure to add to .gitignore
4
- __version__ = VERSION = '0.69.1'
4
+ __version__ = VERSION = '0.69.3'
@@ -193,6 +193,24 @@ class ContestUploader:
193
193
  else:
194
194
  logger.warning(f"No validation result for {file_key}, will retry on next poll")
195
195
 
196
+ async def _fetch_api_data(
197
+ self,
198
+ api_files: Dict[str, FileData],
199
+ changed_api_files: list[str],
200
+ unchanged_files: list[str],
201
+ ):
202
+ api_data = await self._clics_client.fetch_api_info()
203
+ if api_data:
204
+ filename = "api.json"
205
+ content = json.dumps(api_data, ensure_ascii=False)
206
+
207
+ if self._cache.has_changed(filename, content):
208
+ checksum = self._cache.calculate_checksum(content)
209
+ api_files[filename] = FileData(content=content, checksum=checksum)
210
+ changed_api_files.append(filename)
211
+ else:
212
+ unchanged_files.append(filename)
213
+
196
214
  async def _fetch_contest_data(
197
215
  self,
198
216
  ) -> tuple[Dict[str, FileData], list[str], list[str]]:
@@ -248,9 +266,15 @@ class ContestUploader:
248
266
  return response
249
267
 
250
268
  async def fetch_and_upload(self) -> Optional[Dict]:
251
- await self._clics_client.fetch_api_info()
269
+ api_files, changed_api_files, unchanged_files = ({}, [], [])
270
+
271
+ await self._fetch_api_data(api_files, changed_api_files, unchanged_files)
272
+
273
+ contest_files, contest_changed, contest_unchanged = await self._fetch_contest_data()
274
+ api_files.update(contest_files)
275
+ changed_api_files.extend(contest_changed)
276
+ unchanged_files.extend(contest_unchanged)
252
277
 
253
- api_files, changed_api_files, unchanged_files = await self._fetch_contest_data()
254
278
  await self._fetch_endpoint_data(api_files, changed_api_files, unchanged_files)
255
279
 
256
280
  if changed_api_files:
@@ -27,17 +27,26 @@ class ContestPackageReader(BaseContestReader):
27
27
  res[item[id_name]].append(item)
28
28
  return res
29
29
 
30
- def _load_json_file(self, filepath: str) -> Union[Dict[str, Any], List[Any]]:
30
+ def _load_json_file(
31
+ self,
32
+ filepath: str,
33
+ default_value: Optional[Union[Dict[str, Any], List[Any]]] = None,
34
+ ) -> Union[Dict[str, Any], List[Any]]:
31
35
  full_path = self.contest_package_dir / filepath
32
36
  try:
33
37
  with open(full_path, "r", encoding="utf-8") as f:
34
38
  return json.load(f)
35
39
  except FileNotFoundError:
40
+ if default_value is not None:
41
+ logger.warning(f"File not found, will return default value. [full_path={full_path}]")
42
+ return default_value
36
43
  raise HTTPException(status_code=404, detail=f"File not found: {filepath}")
37
44
  except json.JSONDecodeError as e:
38
45
  raise HTTPException(status_code=500, detail=f"Invalid JSON in file {filepath}: {e}")
39
46
 
40
- def _load_ndjson_file(self, filepath: str) -> List[Dict[str, Any]]:
47
+ def _load_ndjson_file(
48
+ self, filepath: str, default_value: Optional[List[Dict[str, Any]]] = None
49
+ ) -> List[Dict[str, Any]]:
41
50
  full_path = self.contest_package_dir / filepath
42
51
  try:
43
52
  data = list()
@@ -46,60 +55,63 @@ class ContestPackageReader(BaseContestReader):
46
55
  data.append(json.loads(line))
47
56
  return data
48
57
  except FileNotFoundError:
58
+ if default_value is not None:
59
+ logger.warning(f"File not found, will load default value. [full_path={full_path}]")
60
+ return default_value
49
61
  raise HTTPException(status_code=404, detail=f"File not found: {filepath}")
50
62
  except json.JSONDecodeError as e:
51
63
  raise HTTPException(status_code=500, detail=f"Invalid JSON in file {filepath}: {e}")
52
64
 
53
65
  def _load_indexes(self) -> None:
54
- self.access = self._load_json_file("access.json")
66
+ self.access = self._load_json_file("access.json", default_value={})
55
67
 
56
- self.accounts = self._load_json_file("accounts.json")
68
+ self.accounts = self._load_json_file("accounts.json", default_value=[])
57
69
  self.accounts_by_id = {account["id"]: account for account in self.accounts}
58
70
 
59
- self.api_info = self._load_json_file("api.json")
71
+ self.api_info = self._load_json_file("api.json", default_value={})
60
72
 
61
- self.awards = self._load_json_file("awards.json")
73
+ self.awards = self._load_json_file("awards.json", default_value=[])
62
74
  self.awards_by_id = {award["id"]: award for award in self.awards}
63
75
 
64
- self.clarifications = self._load_json_file("clarifications.json")
76
+ self.clarifications = self._load_json_file("clarifications.json", default_value=[])
65
77
  self.clarifications_by_id = {clarification["id"]: clarification for clarification in self.clarifications}
66
78
 
67
- self.contest = self._load_json_file("contest.json")
68
- self.contest_state = self._load_json_file("state.json")
79
+ self.contest = self._load_json_file("contest.json", default_value={})
80
+ self.contest_state = self._load_json_file("state.json", default_value={})
69
81
 
70
- self.groups = self._load_json_file("groups.json")
82
+ self.groups = self._load_json_file("groups.json", default_value=[])
71
83
  self.groups_by_id = {group["id"]: group for group in self.groups}
72
84
 
73
- self.judgement_types = self._load_json_file("judgement-types.json")
85
+ self.judgement_types = self._load_json_file("judgement-types.json", default_value=[])
74
86
  self.judgement_types_by_id = {judgement_type["id"]: judgement_type for judgement_type in self.judgement_types}
75
87
 
76
- self.judgements = self._load_json_file("judgements.json")
88
+ self.judgements = self._load_json_file("judgements.json", default_value=[])
77
89
  self.judgements_by_id = {judgement["id"]: judgement for judgement in self.judgements}
78
90
  self.judgements_by_submission_id = self._create_index_by_id(self.judgements, "submission_id")
79
91
 
80
- self.languages = self._load_json_file("languages.json")
92
+ self.languages = self._load_json_file("languages.json", default_value=[])
81
93
  self.languages_by_id = {language["id"]: language for language in self.languages}
82
94
 
83
- self.organizations = self._load_json_file("organizations.json")
95
+ self.organizations = self._load_json_file("organizations.json", default_value=[])
84
96
  self.organizations_by_id = {org["id"]: org for org in self.organizations}
85
97
 
86
- self.problems = self._load_json_file("problems.json")
98
+ self.problems = self._load_json_file("problems.json", default_value=[])
87
99
  self.problems_by_id = {problem["id"]: problem for problem in self.problems}
88
100
 
89
- self.runs = self._load_json_file("runs.json")
101
+ self.runs = self._load_json_file("runs.json", default_value=[])
90
102
  self.runs_by_id = {run["id"]: run for run in self.runs}
91
103
  self.runs_by_judgement_id = self._create_index_by_id(self.runs, "judgement_id")
92
104
 
93
- self.submissions = self._load_json_file("submissions.json")
105
+ self.submissions = self._load_json_file("submissions.json", default_value=[])
94
106
  self.submissions_by_id = {submission["id"]: submission for submission in self.submissions}
95
107
 
96
- self.teams = self._load_json_file("teams.json")
108
+ self.teams = self._load_json_file("teams.json", default_value=[])
97
109
  self.teams_by_id = {team["id"]: team for team in self.teams}
98
110
 
99
- self.event_feed = self._load_ndjson_file("event-feed.ndjson")
111
+ self.event_feed = self._load_ndjson_file("event-feed.ndjson", default_value=[])
100
112
  self.event_feed_tokens = [event["token"] for event in self.event_feed]
101
113
 
102
- self.contest_id = self.contest["id"]
114
+ self.contest_id = self.contest.get("id", "")
103
115
 
104
116
  def _get_file_attr(self, expected_href: str, base_path: Path, files: List[Dict]) -> FileAttr:
105
117
  for file in files:
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