xcpcio 0.63.2__tar.gz → 0.76.8__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.
Files changed (66) hide show
  1. xcpcio-0.76.8/.gitignore +10 -0
  2. xcpcio-0.76.8/.python-version +1 -0
  3. xcpcio-0.76.8/PKG-INFO +88 -0
  4. xcpcio-0.76.8/README.md +58 -0
  5. {xcpcio-0.63.2 → xcpcio-0.76.8}/pyproject.toml +34 -2
  6. xcpcio-0.76.8/scripts/generate_ccs_models.sh +69 -0
  7. xcpcio-0.76.8/tests/__init__.py +0 -0
  8. {xcpcio-0.63.2 → xcpcio-0.76.8}/tests/test_contest.py +53 -65
  9. {xcpcio-0.63.2 → xcpcio-0.76.8}/tests/test_submission.py +6 -6
  10. {xcpcio-0.63.2 → xcpcio-0.76.8}/tests/test_team.py +39 -11
  11. {xcpcio-0.63.2 → xcpcio-0.76.8}/tests/test_types.py +22 -22
  12. xcpcio-0.76.8/uv.lock +1607 -0
  13. xcpcio-0.76.8/xcpcio/__init__.py +4 -0
  14. xcpcio-0.76.8/xcpcio/__version__.py +4 -0
  15. xcpcio-0.76.8/xcpcio/api/__init__.py +17 -0
  16. xcpcio-0.76.8/xcpcio/api/client.py +74 -0
  17. xcpcio-0.76.8/xcpcio/api/models.py +36 -0
  18. xcpcio-0.76.8/xcpcio/app/clics_archiver.py +223 -0
  19. xcpcio-0.76.8/xcpcio/app/clics_server.py +138 -0
  20. xcpcio-0.76.8/xcpcio/app/clics_uploader.py +103 -0
  21. xcpcio-0.76.8/xcpcio/clics/__init__.py +9 -0
  22. xcpcio-0.76.8/xcpcio/clics/api_server/__init__.py +9 -0
  23. xcpcio-0.76.8/xcpcio/clics/api_server/app.py +43 -0
  24. xcpcio-0.76.8/xcpcio/clics/api_server/dependencies.py +52 -0
  25. xcpcio-0.76.8/xcpcio/clics/api_server/routes/__init__.py +52 -0
  26. xcpcio-0.76.8/xcpcio/clics/api_server/routes/access.py +18 -0
  27. xcpcio-0.76.8/xcpcio/clics/api_server/routes/accounts.py +35 -0
  28. xcpcio-0.76.8/xcpcio/clics/api_server/routes/awards.py +33 -0
  29. xcpcio-0.76.8/xcpcio/clics/api_server/routes/clarifications.py +34 -0
  30. xcpcio-0.76.8/xcpcio/clics/api_server/routes/contests.py +90 -0
  31. xcpcio-0.76.8/xcpcio/clics/api_server/routes/general.py +33 -0
  32. xcpcio-0.76.8/xcpcio/clics/api_server/routes/groups.py +34 -0
  33. xcpcio-0.76.8/xcpcio/clics/api_server/routes/judgement_types.py +34 -0
  34. xcpcio-0.76.8/xcpcio/clics/api_server/routes/judgements.py +35 -0
  35. xcpcio-0.76.8/xcpcio/clics/api_server/routes/languages.py +34 -0
  36. xcpcio-0.76.8/xcpcio/clics/api_server/routes/organizations.py +50 -0
  37. xcpcio-0.76.8/xcpcio/clics/api_server/routes/problems.py +50 -0
  38. xcpcio-0.76.8/xcpcio/clics/api_server/routes/runs.py +35 -0
  39. xcpcio-0.76.8/xcpcio/clics/api_server/routes/submissions.py +50 -0
  40. xcpcio-0.76.8/xcpcio/clics/api_server/routes/teams.py +50 -0
  41. xcpcio-0.76.8/xcpcio/clics/api_server/server.py +63 -0
  42. xcpcio-0.76.8/xcpcio/clics/api_server/services/__init__.py +11 -0
  43. xcpcio-0.76.8/xcpcio/clics/api_server/services/contest_service.py +194 -0
  44. xcpcio-0.76.8/xcpcio/clics/base/__init__.py +3 -0
  45. xcpcio-0.76.8/xcpcio/clics/base/types.py +9 -0
  46. xcpcio-0.76.8/xcpcio/clics/clics_api_client.py +251 -0
  47. xcpcio-0.76.8/xcpcio/clics/contest_archiver.py +297 -0
  48. xcpcio-0.76.8/xcpcio/clics/contest_uploader.py +304 -0
  49. {xcpcio-0.63.2/xcpcio/ccs → xcpcio-0.76.8/xcpcio/clics}/model/model_2023_06/__init__.py +1 -1
  50. xcpcio-0.76.8/xcpcio/clics/reader/__init__.py +7 -0
  51. xcpcio-0.76.8/xcpcio/clics/reader/contest_package_reader.py +343 -0
  52. xcpcio-0.76.8/xcpcio/clics/reader/interface.py +165 -0
  53. {xcpcio-0.63.2 → xcpcio-0.76.8}/xcpcio/types.py +115 -41
  54. xcpcio-0.63.2/PKG-INFO +0 -21
  55. xcpcio-0.63.2/README.md +0 -1
  56. xcpcio-0.63.2/setup.cfg +0 -4
  57. xcpcio-0.63.2/xcpcio/__init__.py +0 -5
  58. xcpcio-0.63.2/xcpcio/ccs/__init__.py +0 -3
  59. xcpcio-0.63.2/xcpcio.egg-info/PKG-INFO +0 -21
  60. xcpcio-0.63.2/xcpcio.egg-info/SOURCES.txt +0 -18
  61. xcpcio-0.63.2/xcpcio.egg-info/dependency_links.txt +0 -1
  62. xcpcio-0.63.2/xcpcio.egg-info/requires.txt +0 -1
  63. xcpcio-0.63.2/xcpcio.egg-info/top_level.txt +0 -1
  64. {xcpcio-0.63.2/xcpcio/ccs → xcpcio-0.76.8/xcpcio/clics}/model/__init__.py +0 -0
  65. {xcpcio-0.63.2/xcpcio/ccs → xcpcio-0.76.8/xcpcio/clics}/model/model_2023_06/model.py +0 -0
  66. {xcpcio-0.63.2 → xcpcio-0.76.8}/xcpcio/constants.py +0 -0
@@ -0,0 +1,10 @@
1
+ .venv/
2
+ .pytest_cache/
3
+ .ruff_cache/
4
+ __pycache__/
5
+ ccs-specs/
6
+ output/
7
+
8
+ .env
9
+
10
+ xcpcio/__version__.py
@@ -0,0 +1 @@
1
+ 3.12.11
xcpcio-0.76.8/PKG-INFO ADDED
@@ -0,0 +1,88 @@
1
+ Metadata-Version: 2.4
2
+ Name: xcpcio
3
+ Version: 0.76.8
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
+ - [Clics Utility Guide](https://xcpcio.com/guide/clics-utility)
83
+
84
+ ## License
85
+
86
+ [MIT](../LICENSE) License &copy; 2020 - PRESENT [XCPCIO][xcpcio]
87
+
88
+ [xcpcio]: https://xcpcio.com
@@ -0,0 +1,58 @@
1
+ # xcpcio-python
2
+
3
+ Python library and CLI tools for XCPCIO.
4
+
5
+ ## Features
6
+
7
+ - **Type Definitions**: Pydantic models for contest data structures (teams, submissions, problems, etc.)
8
+ - **Constants**: Shared constants for submission statuses, time units, and penalty calculations
9
+ - **CCS Archiver**: CLI tool to archive CCS API data to contest package format
10
+ - **Contest API Server**: CLI tool to serve contest packages via CCS API
11
+ - **Cross-Language Compatibility**: Mirrors TypeScript types for data consistency
12
+
13
+ ## Installation
14
+
15
+ ```bash
16
+ pip install xcpcio
17
+ ```
18
+
19
+ Or install with [uv](https://github.com/astral-sh/uv):
20
+
21
+ ```bash
22
+ uv add xcpcio
23
+ ```
24
+
25
+ ## Development
26
+
27
+ ### Setup
28
+
29
+ ```bash
30
+ # Clone repository
31
+ git clone https://github.com/xcpcio/xcpcio.git
32
+ cd xcpcio/python
33
+
34
+ # Install dependencies with uv
35
+ uv sync
36
+ ```
37
+
38
+ ### Testing
39
+
40
+ ```bash
41
+ # Run tests
42
+ uv run pytest
43
+
44
+ # Run specific test file
45
+ uv run pytest tests/test_types.py
46
+ ```
47
+
48
+ ## Documentation
49
+
50
+ For detailed documentation, visit:
51
+
52
+ - [Clics Utility Guide](https://xcpcio.com/guide/clics-utility)
53
+
54
+ ## License
55
+
56
+ [MIT](../LICENSE) License &copy; 2020 - PRESENT [XCPCIO][xcpcio]
57
+
58
+ [xcpcio]: https://xcpcio.com
@@ -1,9 +1,12 @@
1
1
  [project]
2
2
  name = "xcpcio"
3
- version = "0.63.2"
4
3
  description = "xcpcio python lib"
5
4
  readme = "README.md"
6
5
  authors = [ { name = "Dup4", email = "hi@dup4.com" } ]
6
+ maintainers = [
7
+ { name = "Dup4", email = "hi@dup4.com" },
8
+ { name = "cubercsl", email = "hi@cubercsl.site" },
9
+ ]
7
10
  license = "MIT"
8
11
  keywords = [ "xcpcio" ]
9
12
  classifiers = [
@@ -15,19 +18,48 @@ classifiers = [
15
18
  "Programming Language :: Python :: 3.13",
16
19
  ]
17
20
  requires-python = ">=3.11"
18
- dependencies = [ "pydantic>=2.11.7", ]
21
+ dependencies = [
22
+ "pydantic>=2.11.7",
23
+ "aiohttp>=3.8.0",
24
+ "aiofiles>=23.0.0",
25
+ "click>=8.0.0",
26
+ "pyyaml>=6.0.0",
27
+ "semver>=3.0.0",
28
+ "tenacity>=8.0.0",
29
+ "fastapi>=0.117.1",
30
+ "uvicorn>=0.36.0",
31
+ "zstandard>=0.25.0",
32
+ ]
33
+ dynamic = ["version"]
19
34
 
20
35
  [project.urls]
21
36
  homepage = "https://github.com/xcpcio/xcpcio"
22
37
  documentation = "https://github.com/xcpcio/xcpcio"
23
38
  repository = "https://github.com/xcpcio/xcpcio"
24
39
 
40
+ [project.scripts]
41
+ clics-archiver = "xcpcio.app.clics_archiver:main"
42
+ clics-server = "xcpcio.app.clics_server:main"
43
+ clics-uploader = "xcpcio.app.clics_uploader:main"
44
+
25
45
  [tool.ruff]
26
46
  line-length = 120
27
47
 
48
+ [build-system]
49
+ requires = ["hatchling", "hatch-nodejs-version"]
50
+ build-backend = "hatchling.build"
51
+
52
+ [tool.hatch.version]
53
+ source = "nodejs"
54
+ path = "../package.json"
55
+
56
+ [tool.hatch.build.hooks.version]
57
+ path = "xcpcio/__version__.py"
58
+
28
59
  [dependency-groups]
29
60
  dev = [
30
61
  "datamodel-code-generator[http]>=0.33.0",
62
+ "hatch>=1.14.2",
31
63
  "pytest>=8.4.2",
32
64
  "ruff>=0.4.0",
33
65
  ]
@@ -0,0 +1,69 @@
1
+ #!/bin/bash
2
+
3
+ set -euo pipefail
4
+
5
+ CUR_DIR="$(dirname "$(realpath "${BASH_SOURCE[0]}")")"
6
+ PYTHON_DIR="$(dirname "${CUR_DIR}")"
7
+ CCS_SPECS_DIR="${PYTHON_DIR}/ccs-specs"
8
+ CCS_MODELS_DIR="${PYTHON_DIR}/xcpcio/clics/model"
9
+
10
+ BRANCH="${1:-2023-06}"
11
+ CCS_REPO_URL="https://github.com/icpc/ccs-specs.git"
12
+
13
+ MODEL_DIR_NAME="model_${BRANCH//-/_}"
14
+ OUTPUT_DIR="${CCS_MODELS_DIR}/${MODEL_DIR_NAME}"
15
+
16
+ if [[ -d "${CCS_SPECS_DIR}" ]]; then
17
+ echo "Please remove existing ${CCS_SPECS_DIR} directory first"
18
+ exit 1
19
+ fi
20
+
21
+ if [[ -d "${OUTPUT_DIR}" ]]; then
22
+ echo "Please remove existing ${OUTPUT_DIR} directory first"
23
+ exit 1
24
+ fi
25
+
26
+ echo "Generating CCS models for branch: ${BRANCH}"
27
+
28
+ pushd "${PYTHON_DIR}"
29
+
30
+ echo "Cloning CCS specs repository (branch: ${BRANCH})..."
31
+ git clone --branch "${BRANCH}" --depth 1 "${CCS_REPO_URL}" ccs-specs
32
+
33
+ echo "Creating output directory: ${OUTPUT_DIR}"
34
+ mkdir -p "${OUTPUT_DIR}"
35
+
36
+ ENTRY_POINT="${CCS_SPECS_DIR}/json-schema/event-feed-array.json"
37
+
38
+ if [[ ! -f "${ENTRY_POINT}" ]]; then
39
+ echo "Error: ${ENTRY_POINT} not found in ccs-specs directory"
40
+ exit 2
41
+ fi
42
+
43
+ echo "Generating Python models using datamodel-codegen..."
44
+ uv run datamodel-codegen \
45
+ --input "${ENTRY_POINT}" \
46
+ --input-file-type jsonschema \
47
+ --output-model-type pydantic_v2.BaseModel \
48
+ --target-python-version "3.11" \
49
+ --output "${OUTPUT_DIR}/model.py"
50
+
51
+ echo "Creating __init__.py file..."
52
+ cat >"${OUTPUT_DIR}/__init__.py" <<EOF
53
+ """
54
+ CCS (Contest Control System) models for branch ${BRANCH}.
55
+
56
+ Auto-generated from https://github.com/icpc/ccs-specs/tree/${BRANCH}
57
+ """
58
+
59
+ from .model import * # noqa: F403
60
+ EOF
61
+
62
+ echo "CCS models generated successfully!"
63
+ echo "Output directory: ${OUTPUT_DIR}"
64
+ echo "Generated files:"
65
+ ls -la "${OUTPUT_DIR}"
66
+
67
+ echo "Done!"
68
+
69
+ popd
File without changes
@@ -1,7 +1,5 @@
1
-
2
-
3
1
  from xcpcio import constants
4
- from xcpcio.types import Color, Contest, ContestOptions, Image
2
+ from xcpcio.types import BalloonColor, Contest, ContestOptions, Image
5
3
 
6
4
 
7
5
  class TestContest:
@@ -10,15 +8,14 @@ class TestContest:
10
8
  def test_contest_creation_defaults(self):
11
9
  """Test Contest creation with default values"""
12
10
  contest = Contest()
13
-
11
+
14
12
  assert contest.contest_name == ""
15
13
  assert contest.start_time == 0
16
14
  assert contest.end_time == 0
17
15
  assert contest.frozen_time == 60 * 60 # 1 hour
18
- assert contest.unfrozen_time == 0x3F3F3F3F3F3F3F3F
16
+ assert contest.thaw_time == 0x3F3F3F3F3F3F3F3F
19
17
  assert contest.penalty == 20 * 60 # 20 minutes
20
- assert contest.problem_quantity == 0
21
- assert contest.problem_id == []
18
+ assert contest.problem_id is None
22
19
  assert contest.organization == "School"
23
20
  assert contest.medal is None
24
21
  assert contest.balloon_color is None
@@ -30,18 +27,17 @@ class TestContest:
30
27
  assert contest.tag is None
31
28
  assert contest.board_link is None
32
29
  assert contest.version is None
33
-
30
+
34
31
  # Check default values
35
32
  assert contest.status_time_display == constants.FULL_STATUS_TIME_DISPLAY
36
- assert isinstance(contest.options, ContestOptions)
33
+ assert contest.options is None
37
34
 
38
35
  def test_contest_creation_with_values(self):
39
36
  """Test Contest creation with provided values"""
40
37
  contest_options = ContestOptions(
41
- calculation_of_penalty=constants.CALCULATION_OF_PENALTY_IN_SECONDS,
42
- has_reaction_videos=True
38
+ calculation_of_penalty=constants.CALCULATION_OF_PENALTY_IN_SECONDS, has_reaction_videos=True
43
39
  )
44
-
40
+
45
41
  contest = Contest(
46
42
  contest_name="ICPC World Finals 2024",
47
43
  start_time=1234567890,
@@ -52,13 +48,12 @@ class TestContest:
52
48
  problem_id=["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L"],
53
49
  organization="ICPC",
54
50
  medal="icpc", # Use Literal value
55
- options=contest_options
51
+ options=contest_options,
56
52
  )
57
-
53
+
58
54
  assert contest.contest_name == "ICPC World Finals 2024"
59
55
  assert contest.start_time == 1234567890
60
56
  assert contest.end_time == 1234567890 + 5 * 60 * 60
61
- assert contest.problem_quantity == 12
62
57
  assert len(contest.problem_id) == 12
63
58
  assert contest.problem_id[0] == "A"
64
59
  assert contest.problem_id[-1] == "L"
@@ -72,18 +67,16 @@ class TestContest:
72
67
  contest_name="Test Contest",
73
68
  start_time=1000000000,
74
69
  end_time=1000000000 + 60 * 60 * 5,
75
- problem_quantity=5,
76
70
  problem_id=["A", "B", "C", "D", "E"],
77
- organization="Test Org"
71
+ organization="Test Org",
78
72
  )
79
-
73
+
80
74
  # Test model_dump
81
75
  contest_dict = contest.model_dump()
82
76
  assert contest_dict["contest_name"] == "Test Contest"
83
77
  assert contest_dict["start_time"] == 1000000000
84
- assert contest_dict["problem_quantity"] == 5
85
78
  assert contest_dict["organization"] == "Test Org"
86
-
79
+
87
80
  # Test JSON round-trip
88
81
  contest_json = contest.model_dump_json()
89
82
  reconstructed_contest = Contest.model_validate_json(contest_json)
@@ -92,21 +85,20 @@ class TestContest:
92
85
  def test_contest_with_colors_and_images(self):
93
86
  """Test Contest with balloon colors, logo, and banner"""
94
87
  colors = [
95
- Color(color="#fff", background_color="rgba(255, 0, 0, 0.7)"),
96
- Color(color="#000", background_color="rgba(0, 255, 0, 0.7)")
88
+ BalloonColor(color="#fff", background_color="rgba(255, 0, 0, 0.7)"),
89
+ BalloonColor(color="#000", background_color="rgba(0, 255, 0, 0.7)"),
97
90
  ]
98
91
  logo = Image(url="https://example.com/logo.png", type="png")
99
92
  banner = Image(url="https://example.com/banner.jpg", type="jpg")
100
-
93
+
101
94
  contest = Contest(
102
95
  contest_name="Contest with Media",
103
- problem_quantity=2,
104
96
  balloon_color=colors,
105
97
  logo=logo,
106
98
  banner=banner,
107
- banner_mode="ALL" # Use correct Literal value
99
+ banner_mode="ALL", # Use correct Literal value
108
100
  )
109
-
101
+
110
102
  assert len(contest.balloon_color) == 2
111
103
  assert contest.balloon_color[0].color == "#fff"
112
104
  assert contest.balloon_color[1].background_color == "rgba(0, 255, 0, 0.7)"
@@ -117,59 +109,59 @@ class TestContest:
117
109
  def test_append_balloon_color(self):
118
110
  """Test append_balloon_color method"""
119
111
  contest = Contest()
120
-
112
+
121
113
  # Initially no colors
122
114
  assert contest.balloon_color is None
123
-
115
+
124
116
  # Add first color
125
- red_color = Color(color="#fff", background_color="red")
117
+ red_color = BalloonColor(color="#fff", background_color="red")
126
118
  contest.append_balloon_color(red_color)
127
-
119
+
128
120
  assert contest.balloon_color is not None
129
121
  assert len(contest.balloon_color) == 1
130
122
  assert contest.balloon_color[0] == red_color
131
-
123
+
132
124
  # Add second color
133
- blue_color = Color(color="#fff", background_color="blue")
125
+ blue_color = BalloonColor(color="#fff", background_color="blue")
134
126
  contest.append_balloon_color(blue_color)
135
-
127
+
136
128
  assert len(contest.balloon_color) == 2
137
129
  assert contest.balloon_color[1] == blue_color
138
130
 
139
131
  def test_fill_problem_id(self):
140
132
  """Test fill_problem_id method"""
141
- contest = Contest(problem_quantity=5)
142
-
133
+ contest = Contest()
134
+
143
135
  # Initially empty
144
- assert contest.problem_id == []
145
-
136
+ assert contest.problem_id is None
137
+
146
138
  # Fill with A-E
147
- contest.fill_problem_id()
148
-
139
+ contest.fill_problem_id(5)
140
+
149
141
  assert len(contest.problem_id) == 5
150
142
  assert contest.problem_id == ["A", "B", "C", "D", "E"]
151
-
143
+
152
144
  # Test with larger quantity
153
- contest.problem_quantity = 10
154
- contest.fill_problem_id()
155
-
145
+ contest.fill_problem_id(10)
146
+
156
147
  assert len(contest.problem_id) == 10
157
148
  assert contest.problem_id == ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J"]
158
149
 
159
150
  def test_fill_balloon_color(self):
160
151
  """Test fill_balloon_color method"""
161
- contest = Contest(problem_quantity=3)
162
-
152
+ contest = Contest()
153
+
163
154
  # Initially no colors
164
155
  assert contest.balloon_color is None
165
-
166
- # Fill with default colors
156
+
157
+ # Fill problem IDs first, then balloon colors
158
+ contest.fill_problem_id(3)
167
159
  contest.fill_balloon_color()
168
-
160
+
169
161
  assert contest.balloon_color is not None
170
162
  assert len(contest.balloon_color) == 3
171
- assert all(isinstance(color, Color) for color in contest.balloon_color)
172
-
163
+ assert all(isinstance(color, BalloonColor) for color in contest.balloon_color)
164
+
173
165
  # Check first few default colors
174
166
  assert contest.balloon_color[0].background_color == "rgba(189, 14, 14, 0.7)"
175
167
  assert contest.balloon_color[0].color == "#fff"
@@ -183,22 +175,21 @@ class TestContest:
183
175
  contest_name="Complex Contest",
184
176
  start_time=1234567890,
185
177
  end_time=1234567890 + 5 * 60 * 60,
186
- problem_quantity=3,
187
178
  organization="Complex Org",
188
- medal="ccpc" # Use preset instead of dict
179
+ medal="ccpc", # Use preset instead of dict
189
180
  )
190
-
181
+
191
182
  # Add problem IDs and colors
192
- contest.fill_problem_id()
183
+ contest.fill_problem_id(3)
193
184
  contest.fill_balloon_color()
194
-
185
+
195
186
  # Add logo
196
187
  contest.logo = Image(url="https://example.com/logo.png")
197
-
188
+
198
189
  # Test serialization
199
190
  contest_json = contest.model_dump_json()
200
191
  reconstructed_contest = Contest.model_validate_json(contest_json)
201
-
192
+
202
193
  # Verify all data is preserved
203
194
  assert reconstructed_contest.contest_name == contest.contest_name
204
195
  assert reconstructed_contest.start_time == contest.start_time
@@ -212,18 +203,15 @@ class TestContest:
212
203
  """Test contest with custom group and status display"""
213
204
  custom_group = {"team_a": "Team A", "team_b": "Team B"}
214
205
  custom_status = {"show_penalty": True, "show_time": False}
215
-
216
- contest = Contest(
217
- group=custom_group,
218
- status_time_display=custom_status
219
- )
220
-
206
+
207
+ contest = Contest(group=custom_group, status_time_display=custom_status)
208
+
221
209
  # Custom values should override defaults
222
210
  assert contest.group == custom_group
223
211
  assert contest.status_time_display == custom_status
224
-
212
+
225
213
  # Test serialization preserves custom values
226
214
  contest_json = contest.model_dump_json()
227
215
  reconstructed = Contest.model_validate_json(contest_json)
228
216
  assert reconstructed.group == custom_group
229
- assert reconstructed.status_time_display == custom_status
217
+ assert reconstructed.status_time_display == custom_status
@@ -3,7 +3,7 @@ import json
3
3
  import pytest
4
4
 
5
5
  from xcpcio import constants
6
- from xcpcio.types import Reaction, Submission, Submissions
6
+ from xcpcio.types import SubmissionReaction, Submission, Submissions
7
7
 
8
8
 
9
9
  class TestSubmission:
@@ -12,7 +12,7 @@ class TestSubmission:
12
12
  def test_submission_creation_defaults(self):
13
13
  """Test Submission creation with default values"""
14
14
  submission = Submission()
15
- assert submission.id == ""
15
+ assert submission.id is None
16
16
  assert submission.team_id == ""
17
17
  assert submission.problem_id == 0
18
18
  assert submission.timestamp == 0
@@ -24,7 +24,7 @@ class TestSubmission:
24
24
 
25
25
  def test_submission_creation_with_values(self):
26
26
  """Test Submission creation with provided values"""
27
- reaction = Reaction(url="https://reaction.com/video.mp4")
27
+ reaction = SubmissionReaction(url="https://reaction.com/video.mp4")
28
28
  submission = Submission(
29
29
  id="sub_001",
30
30
  status=constants.SUBMISSION_STATUS_ACCEPTED,
@@ -79,7 +79,7 @@ class TestSubmission:
79
79
 
80
80
  def test_submission_with_reaction_serialization(self):
81
81
  """Test Submission with Reaction serialization"""
82
- reaction = Reaction(url="https://example.com/reaction.mp4")
82
+ reaction = SubmissionReaction(url="https://example.com/reaction.mp4")
83
83
  submission = Submission(
84
84
  id="sub_003",
85
85
  status=constants.SUBMISSION_STATUS_ACCEPTED,
@@ -125,7 +125,7 @@ class TestSubmissions:
125
125
  timestamp=1234567891,
126
126
  time=300,
127
127
  language="C++",
128
- reaction=Reaction(url="https://reaction.com/video.mp4"),
128
+ reaction=SubmissionReaction(url="https://reaction.com/video.mp4"),
129
129
  ),
130
130
  Submission(
131
131
  id="sub_003",
@@ -214,7 +214,7 @@ class TestSubmissions:
214
214
  problem_id=1,
215
215
  timestamp=123,
216
216
  ),
217
- Submission(), # All defaults
217
+ Submission(id="sub_default"), # All other defaults
218
218
  ]
219
219
  )
220
220