rbx.cp 0.5.0__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.
Files changed (164) hide show
  1. rbx/__init__.py +0 -0
  2. rbx/annotations.py +127 -0
  3. rbx/autoenum.py +333 -0
  4. rbx/box/__init__.py +0 -0
  5. rbx/box/builder.py +77 -0
  6. rbx/box/cd.py +37 -0
  7. rbx/box/checkers.py +134 -0
  8. rbx/box/code.py +185 -0
  9. rbx/box/compile.py +56 -0
  10. rbx/box/conftest.py +42 -0
  11. rbx/box/contest/__init__.py +0 -0
  12. rbx/box/contest/build_contest_statements.py +347 -0
  13. rbx/box/contest/contest_package.py +76 -0
  14. rbx/box/contest/contest_utils.py +20 -0
  15. rbx/box/contest/main.py +179 -0
  16. rbx/box/contest/schema.py +155 -0
  17. rbx/box/contest/statements.py +82 -0
  18. rbx/box/creation.py +72 -0
  19. rbx/box/download.py +64 -0
  20. rbx/box/environment.py +345 -0
  21. rbx/box/extensions.py +26 -0
  22. rbx/box/generators.py +478 -0
  23. rbx/box/generators_test.py +63 -0
  24. rbx/box/main.py +449 -0
  25. rbx/box/package.py +316 -0
  26. rbx/box/packaging/boca/extension.py +27 -0
  27. rbx/box/packaging/boca/packager.py +245 -0
  28. rbx/box/packaging/contest_main.py +82 -0
  29. rbx/box/packaging/main.py +68 -0
  30. rbx/box/packaging/packager.py +117 -0
  31. rbx/box/packaging/polygon/packager.py +320 -0
  32. rbx/box/packaging/polygon/test.py +81 -0
  33. rbx/box/packaging/polygon/xml_schema.py +106 -0
  34. rbx/box/presets/__init__.py +503 -0
  35. rbx/box/presets/fetch.py +70 -0
  36. rbx/box/presets/lock_schema.py +20 -0
  37. rbx/box/presets/schema.py +59 -0
  38. rbx/box/schema.py +394 -0
  39. rbx/box/solutions.py +792 -0
  40. rbx/box/solutions_test.py +41 -0
  41. rbx/box/statements/__init__.py +0 -0
  42. rbx/box/statements/build_statements.py +359 -0
  43. rbx/box/statements/builders.py +375 -0
  44. rbx/box/statements/joiners.py +113 -0
  45. rbx/box/statements/latex.py +47 -0
  46. rbx/box/statements/latex_jinja.py +214 -0
  47. rbx/box/statements/schema.py +138 -0
  48. rbx/box/stresses.py +292 -0
  49. rbx/box/stressing/__init__.py +0 -0
  50. rbx/box/stressing/finder_parser.py +359 -0
  51. rbx/box/stressing/generator_parser.py +258 -0
  52. rbx/box/testcases.py +54 -0
  53. rbx/box/ui/__init__.py +0 -0
  54. rbx/box/ui/captured_log.py +372 -0
  55. rbx/box/ui/css/app.tcss +48 -0
  56. rbx/box/ui/main.py +38 -0
  57. rbx/box/ui/run.py +209 -0
  58. rbx/box/validators.py +245 -0
  59. rbx/box/validators_test.py +15 -0
  60. rbx/checker.py +128 -0
  61. rbx/clone.py +197 -0
  62. rbx/config.py +271 -0
  63. rbx/conftest.py +38 -0
  64. rbx/console.py +27 -0
  65. rbx/create.py +37 -0
  66. rbx/edit.py +24 -0
  67. rbx/grading/__init__.py +0 -0
  68. rbx/grading/caching.py +356 -0
  69. rbx/grading/conftest.py +33 -0
  70. rbx/grading/judge/__init__.py +0 -0
  71. rbx/grading/judge/cacher.py +503 -0
  72. rbx/grading/judge/digester.py +35 -0
  73. rbx/grading/judge/sandbox.py +748 -0
  74. rbx/grading/judge/sandboxes/__init__.py +0 -0
  75. rbx/grading/judge/sandboxes/isolate.py +683 -0
  76. rbx/grading/judge/sandboxes/stupid_sandbox.py +310 -0
  77. rbx/grading/judge/sandboxes/timeit.py +217 -0
  78. rbx/grading/judge/storage.py +284 -0
  79. rbx/grading/judge/test.py +38 -0
  80. rbx/grading/judge/testiso.py +54 -0
  81. rbx/grading/steps.py +522 -0
  82. rbx/grading/steps_with_caching.py +59 -0
  83. rbx/grading/steps_with_caching_run_test.py +429 -0
  84. rbx/grading_utils.py +148 -0
  85. rbx/hydration.py +101 -0
  86. rbx/main.py +122 -0
  87. rbx/metadata.py +105 -0
  88. rbx/providers/__init__.py +43 -0
  89. rbx/providers/codeforces.py +73 -0
  90. rbx/providers/provider.py +26 -0
  91. rbx/resources/checkers/boilerplate.cpp +20 -0
  92. rbx/resources/default_config.json +48 -0
  93. rbx/resources/envs/default.rbx.yml +37 -0
  94. rbx/resources/envs/isolate.rbx.yml +37 -0
  95. rbx/resources/packagers/boca/checker.sh +43 -0
  96. rbx/resources/packagers/boca/compare +53 -0
  97. rbx/resources/packagers/boca/compile/c +172 -0
  98. rbx/resources/packagers/boca/compile/cc +173 -0
  99. rbx/resources/packagers/boca/compile/cpp +172 -0
  100. rbx/resources/packagers/boca/compile/java +194 -0
  101. rbx/resources/packagers/boca/compile/kt +155 -0
  102. rbx/resources/packagers/boca/compile/pas +172 -0
  103. rbx/resources/packagers/boca/compile/py2 +173 -0
  104. rbx/resources/packagers/boca/compile/py3 +173 -0
  105. rbx/resources/packagers/boca/run/c +128 -0
  106. rbx/resources/packagers/boca/run/cc +128 -0
  107. rbx/resources/packagers/boca/run/cpp +128 -0
  108. rbx/resources/packagers/boca/run/java +194 -0
  109. rbx/resources/packagers/boca/run/kt +159 -0
  110. rbx/resources/packagers/boca/run/py2 +166 -0
  111. rbx/resources/packagers/boca/run/py3 +166 -0
  112. rbx/resources/presets/default/contest/contest.rbx.yml +14 -0
  113. rbx/resources/presets/default/contest/statement/contest.rbx.tex +97 -0
  114. rbx/resources/presets/default/contest/statement/olymp.sty +250 -0
  115. rbx/resources/presets/default/contest/statement/template.rbx.tex +42 -0
  116. rbx/resources/presets/default/preset.rbx.yml +12 -0
  117. rbx/resources/presets/default/problem/.gitignore +6 -0
  118. rbx/resources/presets/default/problem/gen.cpp +9 -0
  119. rbx/resources/presets/default/problem/problem.rbx.yml +44 -0
  120. rbx/resources/presets/default/problem/random.py +3 -0
  121. rbx/resources/presets/default/problem/random.txt +2 -0
  122. rbx/resources/presets/default/problem/sols/main.cpp +9 -0
  123. rbx/resources/presets/default/problem/sols/slow.cpp +15 -0
  124. rbx/resources/presets/default/problem/sols/wa.cpp +9 -0
  125. rbx/resources/presets/default/problem/statement/olymp.sty +250 -0
  126. rbx/resources/presets/default/problem/statement/projecao.png +0 -0
  127. rbx/resources/presets/default/problem/statement/statement.rbx.tex +18 -0
  128. rbx/resources/presets/default/problem/statement/template.rbx.tex +89 -0
  129. rbx/resources/presets/default/problem/tests/samples/000.in +1 -0
  130. rbx/resources/presets/default/problem/tests/samples/001.in +1 -0
  131. rbx/resources/presets/default/problem/validator.cpp +16 -0
  132. rbx/resources/presets/default/problem/wcmp.cpp +34 -0
  133. rbx/resources/templates/template.cpp +19 -0
  134. rbx/run.py +45 -0
  135. rbx/schema.py +64 -0
  136. rbx/submit.py +61 -0
  137. rbx/submitors/__init__.py +18 -0
  138. rbx/submitors/codeforces.py +120 -0
  139. rbx/submitors/submitor.py +25 -0
  140. rbx/test.py +347 -0
  141. rbx/testcase.py +70 -0
  142. rbx/testcase_rendering.py +79 -0
  143. rbx/testdata/box1/gen1.cpp +7 -0
  144. rbx/testdata/box1/gen2.cpp +9 -0
  145. rbx/testdata/box1/genScript.py +2 -0
  146. rbx/testdata/box1/hard-tle.sol.cpp +26 -0
  147. rbx/testdata/box1/ole.cpp +17 -0
  148. rbx/testdata/box1/problem.rbx.yml +39 -0
  149. rbx/testdata/box1/re.sol.cpp +23 -0
  150. rbx/testdata/box1/sol.cpp +22 -0
  151. rbx/testdata/box1/tests/1.in +1 -0
  152. rbx/testdata/box1/tle-and-incorrect.sol.cpp +33 -0
  153. rbx/testdata/box1/tle.sol.cpp +35 -0
  154. rbx/testdata/box1/validator.cpp +11 -0
  155. rbx/testdata/box1/wa.sol.cpp +22 -0
  156. rbx/testdata/caching/executable.py +1 -0
  157. rbx/testdata/compatible +0 -0
  158. rbx/testing_utils.py +65 -0
  159. rbx/utils.py +162 -0
  160. rbx_cp-0.5.0.dist-info/LICENSE +201 -0
  161. rbx_cp-0.5.0.dist-info/METADATA +89 -0
  162. rbx_cp-0.5.0.dist-info/RECORD +164 -0
  163. rbx_cp-0.5.0.dist-info/WHEEL +4 -0
  164. rbx_cp-0.5.0.dist-info/entry_points.txt +4 -0
@@ -0,0 +1,68 @@
1
+ import pathlib
2
+ import tempfile
3
+ from typing import Type
4
+
5
+ import typer
6
+
7
+ from rbx import annotations, console
8
+ from rbx.box import builder, environment, package
9
+ from rbx.box.package import get_build_path
10
+ from rbx.box.packaging.boca.packager import BocaPackager
11
+ from rbx.box.packaging.packager import BasePackager, BuiltStatement
12
+ from rbx.box.packaging.polygon.packager import PolygonPackager
13
+ from rbx.box.statements.build_statements import build_statement
14
+
15
+ app = typer.Typer(no_args_is_help=True, cls=annotations.AliasGroup)
16
+
17
+
18
+ def run_packager(
19
+ packager_cls: Type[BasePackager],
20
+ verification: environment.VerificationParam,
21
+ ) -> pathlib.Path:
22
+ if not builder.verify(verification=verification):
23
+ console.console.print(
24
+ '[error]Build or verification failed, check the report.[/error]'
25
+ )
26
+ raise typer.Exit(1)
27
+
28
+ pkg = package.find_problem_package_or_die()
29
+ packager = packager_cls()
30
+
31
+ statement_types = packager.statement_types()
32
+ built_statements = []
33
+
34
+ for statement_type in statement_types:
35
+ languages = packager.languages()
36
+ for language in languages:
37
+ statement = packager.get_statement_for_language(language)
38
+ statement_path = build_statement(statement, pkg, statement_type)
39
+ built_statements.append(
40
+ BuiltStatement(statement, statement_path, statement_type)
41
+ )
42
+
43
+ console.console.print(f'Packaging problem for [item]{packager.name()}[/item]...')
44
+
45
+ with tempfile.TemporaryDirectory() as td:
46
+ result_path = packager.package(
47
+ get_build_path(), pathlib.Path(td), built_statements
48
+ )
49
+
50
+ console.console.print(
51
+ f'[success]Problem packaged for [item]{packager.name()}[/item]![/success]'
52
+ )
53
+ console.console.print(f'Package was saved at [item]{result_path}[/item].')
54
+ return result_path
55
+
56
+
57
+ @app.command('polygon', help='Build a package for Polygon.')
58
+ def polygon(
59
+ verification: environment.VerificationParam,
60
+ ):
61
+ run_packager(PolygonPackager, verification=verification)
62
+
63
+
64
+ @app.command('boca', help='Build a package for BOCA.')
65
+ def boca(
66
+ verification: environment.VerificationParam,
67
+ ):
68
+ run_packager(BocaPackager, verification=verification)
@@ -0,0 +1,117 @@
1
+ import dataclasses
2
+ import pathlib
3
+ from abc import ABC, abstractmethod
4
+ from typing import List, Tuple
5
+
6
+ from rbx.box import package
7
+ from rbx.box.contest import contest_package
8
+ from rbx.box.contest.schema import ContestProblem, ContestStatement
9
+ from rbx.box.generators import get_all_built_testcases
10
+ from rbx.box.schema import Package, Testcase, TestcaseGroup
11
+ from rbx.box.statements.schema import Statement, StatementType
12
+
13
+
14
+ @dataclasses.dataclass
15
+ class BuiltStatement:
16
+ statement: Statement
17
+ path: pathlib.Path
18
+ output_type: StatementType
19
+
20
+
21
+ @dataclasses.dataclass
22
+ class BuiltContestStatement:
23
+ statement: ContestStatement
24
+ path: pathlib.Path
25
+ output_type: StatementType
26
+
27
+
28
+ @dataclasses.dataclass
29
+ class BuiltProblemPackage:
30
+ path: pathlib.Path
31
+ package: Package
32
+ problem: ContestProblem
33
+
34
+
35
+ class BasePackager(ABC):
36
+ @abstractmethod
37
+ def name(self) -> str:
38
+ pass
39
+
40
+ def languages(self):
41
+ pkg = package.find_problem_package_or_die()
42
+
43
+ res = set()
44
+ for statement in pkg.statements:
45
+ res.add(statement.language)
46
+ return list(res)
47
+
48
+ def statement_types(self) -> List[StatementType]:
49
+ return [StatementType.PDF]
50
+
51
+ @abstractmethod
52
+ def package(
53
+ self,
54
+ build_path: pathlib.Path,
55
+ into_path: pathlib.Path,
56
+ built_statements: List[BuiltStatement],
57
+ ) -> pathlib.Path:
58
+ pass
59
+
60
+ # Helper methods.
61
+ def get_built_testcases_per_group(self):
62
+ return get_all_built_testcases()
63
+
64
+ def get_built_testcases(self) -> List[Tuple[TestcaseGroup, List[Testcase]]]:
65
+ pkg = package.find_problem_package_or_die()
66
+ tests_per_group = self.get_built_testcases_per_group()
67
+ return [(group, tests_per_group[group.name]) for group in pkg.testcases]
68
+
69
+ def get_flattened_built_testcases(self) -> List[Testcase]:
70
+ pkg = package.find_problem_package_or_die()
71
+ tests_per_group = self.get_built_testcases_per_group()
72
+
73
+ res = []
74
+ for group in pkg.testcases:
75
+ res.extend(tests_per_group[group.name])
76
+ return res
77
+
78
+ def get_statement_for_language(self, lang: str) -> Statement:
79
+ pkg = package.find_problem_package_or_die()
80
+ for statement in pkg.statements:
81
+ if statement.language == lang:
82
+ return statement
83
+ raise
84
+
85
+
86
+ class BaseContestPackager(ABC):
87
+ @abstractmethod
88
+ def name(self) -> str:
89
+ pass
90
+
91
+ @abstractmethod
92
+ def package(
93
+ self,
94
+ built_packages: List[BuiltProblemPackage],
95
+ build_path: pathlib.Path,
96
+ into_path: pathlib.Path,
97
+ built_statements: List[BuiltContestStatement],
98
+ ) -> pathlib.Path:
99
+ pass
100
+
101
+ def languages(self):
102
+ pkg = contest_package.find_contest_package_or_die()
103
+
104
+ res = set()
105
+ for statement in pkg.statements:
106
+ res.add(statement.language)
107
+ return list(res)
108
+
109
+ def statement_types(self) -> List[StatementType]:
110
+ return [StatementType.PDF]
111
+
112
+ def get_statement_for_language(self, lang: str) -> ContestStatement:
113
+ contest = contest_package.find_contest_package_or_die()
114
+ for statement in contest.statements:
115
+ if statement.language == lang:
116
+ return statement
117
+ raise
@@ -0,0 +1,320 @@
1
+ import functools
2
+ import pathlib
3
+ import shutil
4
+ from typing import List
5
+
6
+ import iso639
7
+ import typer
8
+
9
+ from rbx import console
10
+ from rbx.box import package
11
+ from rbx.box.packaging.packager import (
12
+ BaseContestPackager,
13
+ BasePackager,
14
+ BuiltContestStatement,
15
+ BuiltProblemPackage,
16
+ BuiltStatement,
17
+ )
18
+ from rbx.box.packaging.polygon import xml_schema as polygon_schema
19
+ from rbx.config import get_testlib
20
+
21
+ DAT_TEMPLATE = """
22
+ @contest "{name}"
23
+ @contlen 300
24
+ @problems {problems}
25
+ @teams 0
26
+ @submissions 0
27
+ """
28
+
29
+
30
+ def langs_to_code(langs: List[str]) -> List[str]:
31
+ return [iso639.Language.from_name(lang).part1 for lang in langs]
32
+
33
+
34
+ def code_to_langs(langs: List[str]) -> List[str]:
35
+ return [iso639.Language.from_part1(lang).name.lower() for lang in langs]
36
+
37
+
38
+ @functools.cache
39
+ def _is_valid_lang_code(lang: str) -> bool:
40
+ try:
41
+ code_to_langs([lang])
42
+ except iso639.LanguageNotFoundError:
43
+ console.console.print(
44
+ f'[warning]Language [item]{lang}[/item] is being skipped because it is not a iso639 language.[/warning]'
45
+ )
46
+ return False
47
+
48
+ return True
49
+
50
+
51
+ class PolygonPackager(BasePackager):
52
+ def _validate(self):
53
+ langs = self.languages()
54
+ pkg = package.find_problem_package_or_die()
55
+
56
+ lang_codes = set()
57
+ for statement in pkg.statements:
58
+ lang_codes.add(statement.title)
59
+
60
+ for lang in langs:
61
+ if lang not in lang_codes:
62
+ console.console.print(
63
+ f'[error]No statement from language [item]{lang}[/item] found. '
64
+ 'Polygon needs one statement for each language.[/error]'
65
+ )
66
+ raise typer.Exit(1)
67
+
68
+ def _get_names(self) -> List[polygon_schema.Name]:
69
+ return [
70
+ polygon_schema.Name(
71
+ language=code_to_langs([lang])[0],
72
+ value=self.get_statement_for_language(lang).title,
73
+ )
74
+ for lang in self.languages()
75
+ ]
76
+
77
+ def _get_checker(self) -> polygon_schema.Checker:
78
+ # TODO: support other checker languages
79
+ return polygon_schema.Checker(
80
+ name='rbx::checker',
81
+ type='testlib',
82
+ source=polygon_schema.File(path='files/check.cpp', type='cpp.g++17'),
83
+ cpy=polygon_schema.File(path='check.cpp'),
84
+ )
85
+
86
+ def _get_manual_test(self) -> polygon_schema.Test:
87
+ # TODO: return samples
88
+ return polygon_schema.Test(method='manual')
89
+
90
+ def _get_single_testset(self) -> polygon_schema.Testset:
91
+ pkg = package.find_problem_package_or_die()
92
+
93
+ if pkg.modifiers:
94
+ console.console.print(
95
+ f'[warning]Your package specify custom limit modifiers for languages {list(pkg.modifiers)}.[/warning]'
96
+ )
97
+ console.console.print(
98
+ '[warning]Polygon packages do not support language-based limit modifiers, so they will not be applied to the supplied limits.[/warning]'
99
+ )
100
+
101
+ console.console.print(
102
+ f'[warning]Polygon packages cannot honor the {pkg.outputLimit}kb output limit.'
103
+ )
104
+
105
+ testcases = self.get_flattened_built_testcases()
106
+
107
+ return polygon_schema.Testset(
108
+ name='tests',
109
+ timelimit=pkg.timeLimit,
110
+ memorylimit=pkg.memoryLimit * 1024 * 1024,
111
+ size=len(testcases),
112
+ inputPattern='tests/%03d',
113
+ answerPattern='tests/%03d.a',
114
+ tests=[self._get_manual_test() for _ in range(len(testcases))],
115
+ )
116
+
117
+ def _get_judging(self) -> polygon_schema.Judging:
118
+ return polygon_schema.Judging(testsets=[self._get_single_testset()])
119
+
120
+ def _get_files(self) -> List[polygon_schema.File]:
121
+ return [polygon_schema.File(path='files/testlib.h', type='h.g++')]
122
+
123
+ def _statement_application_type(self, statement: BuiltStatement) -> str:
124
+ return 'application/pdf'
125
+
126
+ def _process_statement(
127
+ self,
128
+ built_statement: BuiltStatement,
129
+ into_path: pathlib.Path,
130
+ ) -> polygon_schema.Statement:
131
+ language = code_to_langs([built_statement.statement.language])[0]
132
+ final_path = into_path / 'statements' / language / built_statement.path.name
133
+
134
+ final_path.parent.mkdir(parents=True, exist_ok=True)
135
+ shutil.copyfile(built_statement.path, final_path)
136
+
137
+ return polygon_schema.Statement(
138
+ path=str(final_path.resolve().relative_to(into_path.resolve())),
139
+ language=language,
140
+ type=self._statement_application_type(built_statement), # type: ignore
141
+ )
142
+
143
+ def _process_statements(
144
+ self, built_statements: List[BuiltStatement], into_path: pathlib.Path
145
+ ) -> List[polygon_schema.Statement]:
146
+ return [
147
+ self._process_statement(built_statement, into_path)
148
+ for built_statement in built_statements
149
+ ]
150
+
151
+ def name(self) -> str:
152
+ return 'polygon'
153
+
154
+ def package(
155
+ self,
156
+ build_path: pathlib.Path,
157
+ into_path: pathlib.Path,
158
+ built_statements: List[BuiltStatement],
159
+ ):
160
+ problem = polygon_schema.Problem(
161
+ names=self._get_names(),
162
+ checker=self._get_checker(),
163
+ judging=self._get_judging(),
164
+ files=self._get_files(),
165
+ # TODO: revisit polygon problem statements
166
+ # statements=self._process_statements(built_statements, into_path),
167
+ )
168
+
169
+ descriptor: str = problem.to_xml(
170
+ skip_empty=True,
171
+ encoding='utf-8',
172
+ pretty_print=True,
173
+ standalone=True,
174
+ ) # type: ignore
175
+ if isinstance(descriptor, bytes):
176
+ descriptor = descriptor.decode()
177
+
178
+ # Prepare files
179
+ files_path = into_path / 'files'
180
+ files_path.mkdir(parents=True, exist_ok=True)
181
+ shutil.copyfile(get_testlib(), files_path / 'testlib.h')
182
+ shutil.copyfile(package.get_checker().path, files_path / 'check.cpp')
183
+ shutil.copyfile(package.get_checker().path, into_path / 'check.cpp')
184
+
185
+ # Copy all testcases
186
+ (into_path / 'tests').mkdir(parents=True, exist_ok=True)
187
+ testcases = self.get_flattened_built_testcases()
188
+ for i, testcase in enumerate(testcases):
189
+ shutil.copyfile(
190
+ testcase.inputPath,
191
+ into_path / f'tests/{i+1:03d}',
192
+ )
193
+ if testcase.outputPath is not None:
194
+ shutil.copyfile(
195
+ testcase.outputPath,
196
+ into_path / f'tests/{i+1:03d}.a',
197
+ )
198
+ else:
199
+ (into_path / f'tests/{i+1:03d}.a').touch()
200
+
201
+ # Write problem.xml
202
+ (into_path / 'problem.xml').write_text(descriptor)
203
+
204
+ # Zip all.
205
+ shutil.make_archive(str(build_path / 'problem'), 'zip', into_path)
206
+
207
+ return build_path / 'problem.zip'
208
+
209
+
210
+ class PolygonContestPackager(BaseContestPackager):
211
+ def name(self) -> str:
212
+ return 'polygon'
213
+
214
+ def _get_names(self) -> List[polygon_schema.Name]:
215
+ names = [
216
+ polygon_schema.Name(
217
+ language=code_to_langs([lang])[0],
218
+ value=self.get_statement_for_language(lang).title,
219
+ )
220
+ for lang in self.languages()
221
+ if _is_valid_lang_code(lang)
222
+ ]
223
+ if names:
224
+ names[0].main = True
225
+ return names
226
+
227
+ def _process_statement(
228
+ self,
229
+ built_statement: BuiltContestStatement,
230
+ into_path: pathlib.Path,
231
+ ) -> polygon_schema.Statement:
232
+ language = code_to_langs([built_statement.statement.language])[0]
233
+ final_path = into_path / 'statements' / language / built_statement.path.name
234
+
235
+ final_path.parent.mkdir(parents=True, exist_ok=True)
236
+ shutil.copyfile(built_statement.path, final_path)
237
+
238
+ return polygon_schema.Statement(
239
+ path=str(final_path.resolve().relative_to(into_path.resolve())),
240
+ language=language,
241
+ type='application/pdf', # type: ignore
242
+ )
243
+
244
+ def _process_statements(
245
+ self, built_statements: List[BuiltContestStatement], into_path: pathlib.Path
246
+ ) -> List[polygon_schema.Statement]:
247
+ return [
248
+ self._process_statement(built_statement, into_path)
249
+ for built_statement in built_statements
250
+ ]
251
+
252
+ def _get_problems(
253
+ self, built_packages: List[BuiltProblemPackage]
254
+ ) -> List[polygon_schema.ContestProblem]:
255
+ return [
256
+ polygon_schema.ContestProblem(
257
+ index=built_package.problem.short_name,
258
+ path=str(pathlib.Path('problems') / built_package.problem.short_name),
259
+ )
260
+ for built_package in built_packages
261
+ ]
262
+
263
+ def _get_dat(self, built_packages: List[BuiltProblemPackage]) -> str:
264
+ dat = DAT_TEMPLATE.format(
265
+ name=self.get_statement_for_language(self.languages()[0]).title,
266
+ problems=len(built_packages),
267
+ )
268
+ return (
269
+ dat
270
+ + '\n'.join(
271
+ f'@p {pkg.problem.short_name},{pkg.problem.short_name},20,0'
272
+ for pkg in built_packages
273
+ )
274
+ + '\n'
275
+ )
276
+
277
+ def package(
278
+ self,
279
+ built_packages: List[BuiltProblemPackage],
280
+ build_path: pathlib.Path,
281
+ into_path: pathlib.Path,
282
+ built_statements: List[BuiltContestStatement],
283
+ ) -> pathlib.Path:
284
+ filtered_statements = []
285
+ for built_statement in built_statements:
286
+ if not _is_valid_lang_code(built_statement.statement.language):
287
+ continue
288
+ filtered_statements.append(built_statement)
289
+
290
+ for built_package in built_packages:
291
+ pkg_path = into_path / 'problems' / built_package.problem.short_name
292
+ pkg_path.mkdir(parents=True, exist_ok=True)
293
+
294
+ shutil.unpack_archive(built_package.path, pkg_path, format='zip')
295
+
296
+ # Build contest descriptor.
297
+ contest = polygon_schema.Contest(
298
+ names=self._get_names(),
299
+ statements=self._process_statements(filtered_statements, into_path),
300
+ problems=self._get_problems(built_packages),
301
+ )
302
+ descriptor: str = contest.to_xml(
303
+ skip_empty=True,
304
+ encoding='utf-8',
305
+ pretty_print=True,
306
+ standalone=True,
307
+ ) # type: ignore
308
+ if isinstance(descriptor, bytes):
309
+ descriptor = descriptor.decode()
310
+
311
+ # Write contest.xml
312
+ (into_path / 'contest.xml').write_text(descriptor)
313
+
314
+ # Write contest.dat
315
+ (into_path / 'contest.dat').write_text(self._get_dat(built_packages))
316
+
317
+ # Zip all.
318
+ shutil.make_archive(str(build_path / 'contest'), 'zip', into_path)
319
+
320
+ return pathlib.Path('contest.zip')
@@ -0,0 +1,81 @@
1
+ from rbx.box.packaging.polygon import xml_schema as polygon_schema
2
+
3
+ if __name__ == '__main__':
4
+ print(
5
+ polygon_schema.Problem.from_xml("""
6
+ <problem>
7
+ <names>
8
+ <name language="english" value="Poliedro"/>
9
+ <name language="portuguese" value="Poliedro"/>
10
+ </names>
11
+ <statements>
12
+ <statement charset="UTF-8" language="english" mathjax="true" path="statements/english/problem.tex" type="application/x-tex"/>
13
+ <statement charset="UTF-8" language="portuguese" mathjax="true" path="statements/portuguese/problem.tex" type="application/x-tex"/>
14
+ <statement charset="UTF-8" language="english" mathjax="true" path="statements/.html/english/problem.html" type="text/html"/>
15
+ <statement charset="UTF-8" language="portuguese" mathjax="true" path="statements/.html/portuguese/problem.html" type="text/html"/>
16
+ <statement language="english" path="statements/.pdf/english/problem.pdf" type="application/pdf"/>
17
+ <statement language="portuguese" path="statements/.pdf/portuguese/problem.pdf" type="application/pdf"/>
18
+ </statements>
19
+ <judging input-file="" output-file="">
20
+ <testset name="tests">
21
+ <time-limit>1000</time-limit>
22
+ <memory-limit>268435456</memory-limit>
23
+ <test-count>13</test-count>
24
+ <input-path-pattern>tests/%02d</input-path-pattern>
25
+ <answer-path-pattern>tests/%02d.a</answer-path-pattern>
26
+ <tests>
27
+ <test method="manual"/>
28
+ <test method="manual"/>
29
+ <test method="manual"/>
30
+ <test method="manual"/>
31
+ <test method="manual"/>
32
+ <test method="manual"/>
33
+ <test method="manual"/>
34
+ <test method="manual"/>
35
+ <test method="manual"/>
36
+ <test method="manual"/>
37
+ <test method="manual"/>
38
+ <test method="manual"/>
39
+ <test method="manual"/>
40
+ </tests>
41
+ </testset>
42
+ </judging>
43
+ <files>
44
+ <resources>
45
+ <file path="files/jngen.h" type="h.g++"/>
46
+ <file path="files/olymp.sty"/>
47
+ <file path="files/problem.tex"/>
48
+ <file path="files/statements.ftl"/>
49
+ <file path="files/testlib.h" type="h.g++"/>
50
+ </resources>
51
+ </files>
52
+ <assets>
53
+ <checker name="std::ncmp.cpp" type="testlib">
54
+ <source path="files/check.cpp" type="cpp.g++17"/>
55
+ <binary path="check.exe" type="exe.win32"/>
56
+ <copy path="check.cpp"/>
57
+ </checker>
58
+ </assets>
59
+ </problem>
60
+ """)
61
+ )
62
+
63
+ print(
64
+ polygon_schema.Contest.from_xml("""
65
+ <contest>
66
+ <names>
67
+ <name language="russian" main="true" value="2012-2013 Тренировка СПбГУ B #12 Бинарный поиск, тернарный поиск"/>
68
+ </names>
69
+ <statements>
70
+ <statement language="russian" path="statements/russian/20122013-tryenirovka-spbgu-b-12-binarnyy-poisk-tyernarnyy-poisk-ru.pdf" type="application/pdf"/>
71
+ </statements>
72
+ <problems>
73
+ <problem index="A" path="problems/A"/>
74
+ <problem index="B" path="problems/B"/>
75
+ <problem index="C" path="problems/C"/>
76
+ <problem index="D" path="problems/D"/>
77
+ <problem index="E" path="problems/E"/>
78
+ </problems>
79
+ </contest>
80
+ """)
81
+ )
@@ -0,0 +1,106 @@
1
+ from typing import List, Literal, Optional
2
+
3
+ from pydantic_xml import BaseXmlModel, attr, element, wrapped
4
+
5
+
6
+ class Name(BaseXmlModel):
7
+ language: str = attr()
8
+ value: str = attr()
9
+ main: bool = attr(default=False)
10
+
11
+
12
+ class Statement(BaseXmlModel):
13
+ charset: Optional[Literal['UTF-8']] = attr(default=None)
14
+
15
+ language: str = attr()
16
+
17
+ mathjax: bool = attr(default=False)
18
+
19
+ path: str = attr()
20
+
21
+ type: Literal['application/x-tex', 'application/pdf', 'text/html'] = attr()
22
+
23
+
24
+ class File(BaseXmlModel):
25
+ path: str = attr()
26
+ type: Optional[str] = attr(default=None)
27
+
28
+
29
+ class Test(BaseXmlModel):
30
+ method: Literal['manual', 'generated'] = attr()
31
+
32
+ sample: Optional[bool] = attr(default=None)
33
+
34
+ description: Optional[str] = attr(default=None)
35
+
36
+
37
+ class Testset(BaseXmlModel):
38
+ name: Optional[str] = attr(default=None)
39
+
40
+ timelimit: Optional[int] = element('time-limit', default=1000)
41
+ memorylimit: Optional[int] = element('memory-limit', default=256 * 1024 * 1024)
42
+
43
+ size: int = element('test-count', default=None)
44
+
45
+ inputPattern: str = element('input-path-pattern')
46
+ answerPattern: str = element('answer-path-pattern')
47
+
48
+ tests: List[Test] = wrapped('tests', element(tag='test'), default_factory=list)
49
+
50
+
51
+ class Judging(BaseXmlModel):
52
+ inputFile: str = attr(default='')
53
+ outputFile: str = attr(default='')
54
+
55
+ testsets: List[Testset] = element(tag='testset', default_factory=list)
56
+
57
+
58
+ class Checker(BaseXmlModel):
59
+ name: str = attr()
60
+ type: Literal['testlib'] = attr()
61
+ source: File = element()
62
+ binary: Optional[File] = element(default=None)
63
+ cpy: Optional[File] = element(tag='copy', default=None)
64
+
65
+ testset: Optional[Testset] = element(default=None)
66
+
67
+
68
+ class Problem(BaseXmlModel, tag='problem'):
69
+ names: List[Name] = wrapped('names', element(tag='name'), default_factory=list)
70
+
71
+ statements: List[Statement] = wrapped(
72
+ 'statements',
73
+ element(tag='statement', default=[]),
74
+ default=[],
75
+ )
76
+
77
+ judging: Judging = element()
78
+
79
+ files: List[File] = wrapped(
80
+ 'files/resources',
81
+ element(tag='file', default=[]),
82
+ default=[],
83
+ )
84
+
85
+ checker: Checker = wrapped('assets', element(tag='checker'))
86
+
87
+
88
+ class ContestProblem(BaseXmlModel):
89
+ index: str = attr()
90
+ path: str = attr()
91
+
92
+
93
+ class Contest(BaseXmlModel, tag='contest'):
94
+ names: List[Name] = wrapped('names', element(tag='name'), default_factory=list)
95
+
96
+ statements: List[Statement] = wrapped(
97
+ 'statements',
98
+ element(tag='statement', default=[]),
99
+ default=[],
100
+ )
101
+
102
+ problems: List[ContestProblem] = wrapped(
103
+ 'problems',
104
+ element(tag='problem', default=[]),
105
+ default=[],
106
+ )