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.
- rbx/__init__.py +0 -0
- rbx/annotations.py +127 -0
- rbx/autoenum.py +333 -0
- rbx/box/__init__.py +0 -0
- rbx/box/builder.py +77 -0
- rbx/box/cd.py +37 -0
- rbx/box/checkers.py +134 -0
- rbx/box/code.py +185 -0
- rbx/box/compile.py +56 -0
- rbx/box/conftest.py +42 -0
- rbx/box/contest/__init__.py +0 -0
- rbx/box/contest/build_contest_statements.py +347 -0
- rbx/box/contest/contest_package.py +76 -0
- rbx/box/contest/contest_utils.py +20 -0
- rbx/box/contest/main.py +179 -0
- rbx/box/contest/schema.py +155 -0
- rbx/box/contest/statements.py +82 -0
- rbx/box/creation.py +72 -0
- rbx/box/download.py +64 -0
- rbx/box/environment.py +345 -0
- rbx/box/extensions.py +26 -0
- rbx/box/generators.py +478 -0
- rbx/box/generators_test.py +63 -0
- rbx/box/main.py +449 -0
- rbx/box/package.py +316 -0
- rbx/box/packaging/boca/extension.py +27 -0
- rbx/box/packaging/boca/packager.py +245 -0
- rbx/box/packaging/contest_main.py +82 -0
- rbx/box/packaging/main.py +68 -0
- rbx/box/packaging/packager.py +117 -0
- rbx/box/packaging/polygon/packager.py +320 -0
- rbx/box/packaging/polygon/test.py +81 -0
- rbx/box/packaging/polygon/xml_schema.py +106 -0
- rbx/box/presets/__init__.py +503 -0
- rbx/box/presets/fetch.py +70 -0
- rbx/box/presets/lock_schema.py +20 -0
- rbx/box/presets/schema.py +59 -0
- rbx/box/schema.py +394 -0
- rbx/box/solutions.py +792 -0
- rbx/box/solutions_test.py +41 -0
- rbx/box/statements/__init__.py +0 -0
- rbx/box/statements/build_statements.py +359 -0
- rbx/box/statements/builders.py +375 -0
- rbx/box/statements/joiners.py +113 -0
- rbx/box/statements/latex.py +47 -0
- rbx/box/statements/latex_jinja.py +214 -0
- rbx/box/statements/schema.py +138 -0
- rbx/box/stresses.py +292 -0
- rbx/box/stressing/__init__.py +0 -0
- rbx/box/stressing/finder_parser.py +359 -0
- rbx/box/stressing/generator_parser.py +258 -0
- rbx/box/testcases.py +54 -0
- rbx/box/ui/__init__.py +0 -0
- rbx/box/ui/captured_log.py +372 -0
- rbx/box/ui/css/app.tcss +48 -0
- rbx/box/ui/main.py +38 -0
- rbx/box/ui/run.py +209 -0
- rbx/box/validators.py +245 -0
- rbx/box/validators_test.py +15 -0
- rbx/checker.py +128 -0
- rbx/clone.py +197 -0
- rbx/config.py +271 -0
- rbx/conftest.py +38 -0
- rbx/console.py +27 -0
- rbx/create.py +37 -0
- rbx/edit.py +24 -0
- rbx/grading/__init__.py +0 -0
- rbx/grading/caching.py +356 -0
- rbx/grading/conftest.py +33 -0
- rbx/grading/judge/__init__.py +0 -0
- rbx/grading/judge/cacher.py +503 -0
- rbx/grading/judge/digester.py +35 -0
- rbx/grading/judge/sandbox.py +748 -0
- rbx/grading/judge/sandboxes/__init__.py +0 -0
- rbx/grading/judge/sandboxes/isolate.py +683 -0
- rbx/grading/judge/sandboxes/stupid_sandbox.py +310 -0
- rbx/grading/judge/sandboxes/timeit.py +217 -0
- rbx/grading/judge/storage.py +284 -0
- rbx/grading/judge/test.py +38 -0
- rbx/grading/judge/testiso.py +54 -0
- rbx/grading/steps.py +522 -0
- rbx/grading/steps_with_caching.py +59 -0
- rbx/grading/steps_with_caching_run_test.py +429 -0
- rbx/grading_utils.py +148 -0
- rbx/hydration.py +101 -0
- rbx/main.py +122 -0
- rbx/metadata.py +105 -0
- rbx/providers/__init__.py +43 -0
- rbx/providers/codeforces.py +73 -0
- rbx/providers/provider.py +26 -0
- rbx/resources/checkers/boilerplate.cpp +20 -0
- rbx/resources/default_config.json +48 -0
- rbx/resources/envs/default.rbx.yml +37 -0
- rbx/resources/envs/isolate.rbx.yml +37 -0
- rbx/resources/packagers/boca/checker.sh +43 -0
- rbx/resources/packagers/boca/compare +53 -0
- rbx/resources/packagers/boca/compile/c +172 -0
- rbx/resources/packagers/boca/compile/cc +173 -0
- rbx/resources/packagers/boca/compile/cpp +172 -0
- rbx/resources/packagers/boca/compile/java +194 -0
- rbx/resources/packagers/boca/compile/kt +155 -0
- rbx/resources/packagers/boca/compile/pas +172 -0
- rbx/resources/packagers/boca/compile/py2 +173 -0
- rbx/resources/packagers/boca/compile/py3 +173 -0
- rbx/resources/packagers/boca/run/c +128 -0
- rbx/resources/packagers/boca/run/cc +128 -0
- rbx/resources/packagers/boca/run/cpp +128 -0
- rbx/resources/packagers/boca/run/java +194 -0
- rbx/resources/packagers/boca/run/kt +159 -0
- rbx/resources/packagers/boca/run/py2 +166 -0
- rbx/resources/packagers/boca/run/py3 +166 -0
- rbx/resources/presets/default/contest/contest.rbx.yml +14 -0
- rbx/resources/presets/default/contest/statement/contest.rbx.tex +97 -0
- rbx/resources/presets/default/contest/statement/olymp.sty +250 -0
- rbx/resources/presets/default/contest/statement/template.rbx.tex +42 -0
- rbx/resources/presets/default/preset.rbx.yml +12 -0
- rbx/resources/presets/default/problem/.gitignore +6 -0
- rbx/resources/presets/default/problem/gen.cpp +9 -0
- rbx/resources/presets/default/problem/problem.rbx.yml +44 -0
- rbx/resources/presets/default/problem/random.py +3 -0
- rbx/resources/presets/default/problem/random.txt +2 -0
- rbx/resources/presets/default/problem/sols/main.cpp +9 -0
- rbx/resources/presets/default/problem/sols/slow.cpp +15 -0
- rbx/resources/presets/default/problem/sols/wa.cpp +9 -0
- rbx/resources/presets/default/problem/statement/olymp.sty +250 -0
- rbx/resources/presets/default/problem/statement/projecao.png +0 -0
- rbx/resources/presets/default/problem/statement/statement.rbx.tex +18 -0
- rbx/resources/presets/default/problem/statement/template.rbx.tex +89 -0
- rbx/resources/presets/default/problem/tests/samples/000.in +1 -0
- rbx/resources/presets/default/problem/tests/samples/001.in +1 -0
- rbx/resources/presets/default/problem/validator.cpp +16 -0
- rbx/resources/presets/default/problem/wcmp.cpp +34 -0
- rbx/resources/templates/template.cpp +19 -0
- rbx/run.py +45 -0
- rbx/schema.py +64 -0
- rbx/submit.py +61 -0
- rbx/submitors/__init__.py +18 -0
- rbx/submitors/codeforces.py +120 -0
- rbx/submitors/submitor.py +25 -0
- rbx/test.py +347 -0
- rbx/testcase.py +70 -0
- rbx/testcase_rendering.py +79 -0
- rbx/testdata/box1/gen1.cpp +7 -0
- rbx/testdata/box1/gen2.cpp +9 -0
- rbx/testdata/box1/genScript.py +2 -0
- rbx/testdata/box1/hard-tle.sol.cpp +26 -0
- rbx/testdata/box1/ole.cpp +17 -0
- rbx/testdata/box1/problem.rbx.yml +39 -0
- rbx/testdata/box1/re.sol.cpp +23 -0
- rbx/testdata/box1/sol.cpp +22 -0
- rbx/testdata/box1/tests/1.in +1 -0
- rbx/testdata/box1/tle-and-incorrect.sol.cpp +33 -0
- rbx/testdata/box1/tle.sol.cpp +35 -0
- rbx/testdata/box1/validator.cpp +11 -0
- rbx/testdata/box1/wa.sol.cpp +22 -0
- rbx/testdata/caching/executable.py +1 -0
- rbx/testdata/compatible +0 -0
- rbx/testing_utils.py +65 -0
- rbx/utils.py +162 -0
- rbx_cp-0.5.0.dist-info/LICENSE +201 -0
- rbx_cp-0.5.0.dist-info/METADATA +89 -0
- rbx_cp-0.5.0.dist-info/RECORD +164 -0
- rbx_cp-0.5.0.dist-info/WHEEL +4 -0
- rbx_cp-0.5.0.dist-info/entry_points.txt +4 -0
@@ -0,0 +1,41 @@
|
|
1
|
+
import pathlib
|
2
|
+
|
3
|
+
import pytest
|
4
|
+
|
5
|
+
from rbx.box.environment import VerificationLevel
|
6
|
+
from rbx.box.generators import (
|
7
|
+
generate_outputs_for_testcases,
|
8
|
+
generate_testcases,
|
9
|
+
)
|
10
|
+
from rbx.box.solutions import (
|
11
|
+
convert_list_of_solution_evaluations_to_dict,
|
12
|
+
run_solutions,
|
13
|
+
)
|
14
|
+
from rbx.grading.steps import Outcome
|
15
|
+
|
16
|
+
|
17
|
+
@pytest.mark.test_pkg('box1')
|
18
|
+
def test_solutions(pkg_from_testdata: pathlib.Path):
|
19
|
+
generate_testcases()
|
20
|
+
generate_outputs_for_testcases()
|
21
|
+
|
22
|
+
result = run_solutions(verification=VerificationLevel.FULL)
|
23
|
+
res = convert_list_of_solution_evaluations_to_dict(result.items)
|
24
|
+
|
25
|
+
# First solution should pass all tests.
|
26
|
+
assert all(chk.result.outcome == Outcome.ACCEPTED for chk in res[0]['gen1'])
|
27
|
+
# 25 test should be WA for the second solution.
|
28
|
+
assert res[1]['gen1'][3].result.outcome == Outcome.WRONG_ANSWER
|
29
|
+
# Runtime error for third solution.
|
30
|
+
assert all(chk.result.outcome == Outcome.RUNTIME_ERROR for chk in res[2]['gen1'])
|
31
|
+
# 1e9 test should be TLE for the fourth solution (soft TLE)
|
32
|
+
assert res[3]['gen1'][4].result.outcome == Outcome.TIME_LIMIT_EXCEEDED
|
33
|
+
# no TLE outcome should be WA (soft TLE)
|
34
|
+
assert res[4]['gen1'][4].result.no_tle_outcome == Outcome.WRONG_ANSWER
|
35
|
+
# hard TLE
|
36
|
+
assert res[5]['gen1'][4].result.outcome == Outcome.TIME_LIMIT_EXCEEDED
|
37
|
+
assert res[5]['gen1'][4].result.no_tle_outcome is None
|
38
|
+
# OLE
|
39
|
+
assert all(
|
40
|
+
chk.result.outcome == Outcome.OUTPUT_LIMIT_EXCEEDED for chk in res[6]['gen1']
|
41
|
+
)
|
File without changes
|
@@ -0,0 +1,359 @@
|
|
1
|
+
import pathlib
|
2
|
+
import tempfile
|
3
|
+
import typing
|
4
|
+
from typing import Annotated, Dict, List, Optional, Tuple
|
5
|
+
|
6
|
+
import typer
|
7
|
+
|
8
|
+
from rbx import annotations, console
|
9
|
+
from rbx.box import builder, environment, package
|
10
|
+
from rbx.box.schema import Package
|
11
|
+
from rbx.box.statements.builders import (
|
12
|
+
BUILDER_LIST,
|
13
|
+
PROBLEM_BUILDER_LIST,
|
14
|
+
StatementBuilder,
|
15
|
+
StatementBuilderContext,
|
16
|
+
StatementBuilderProblem,
|
17
|
+
StatementCodeLanguage,
|
18
|
+
prepare_assets,
|
19
|
+
)
|
20
|
+
from rbx.box.statements.schema import (
|
21
|
+
ConversionStep,
|
22
|
+
ConversionType,
|
23
|
+
Statement,
|
24
|
+
StatementType,
|
25
|
+
)
|
26
|
+
from rbx.box.testcases import get_samples
|
27
|
+
|
28
|
+
app = typer.Typer(no_args_is_help=True, cls=annotations.AliasGroup)
|
29
|
+
|
30
|
+
|
31
|
+
def get_environment_languages_for_statement() -> List[StatementCodeLanguage]:
|
32
|
+
env = environment.get_environment()
|
33
|
+
|
34
|
+
res = []
|
35
|
+
for language in env.languages:
|
36
|
+
cmd = ''
|
37
|
+
compilation_cfg = environment.get_compilation_config(language.name)
|
38
|
+
cmd = ' && '.join(compilation_cfg.commands or [])
|
39
|
+
if not cmd:
|
40
|
+
execution_cfg = environment.get_execution_config(language.name)
|
41
|
+
cmd = execution_cfg.command
|
42
|
+
|
43
|
+
res.append(
|
44
|
+
StatementCodeLanguage(
|
45
|
+
id=language.name,
|
46
|
+
name=language.readable_name or language.name,
|
47
|
+
command=cmd or '',
|
48
|
+
)
|
49
|
+
)
|
50
|
+
|
51
|
+
return res
|
52
|
+
|
53
|
+
|
54
|
+
def get_builder(
|
55
|
+
name: ConversionType, builder_list: List[StatementBuilder]
|
56
|
+
) -> StatementBuilder:
|
57
|
+
candidates = [builder for builder in builder_list if builder.name() == name]
|
58
|
+
if not candidates:
|
59
|
+
console.console.print(
|
60
|
+
f'[error]No statement builder found with name [name]{name}[/name][/error]'
|
61
|
+
)
|
62
|
+
raise typer.Exit(1)
|
63
|
+
return candidates[0]
|
64
|
+
|
65
|
+
|
66
|
+
def get_implicit_builders(
|
67
|
+
input_type: StatementType, output_type: StatementType
|
68
|
+
) -> Optional[List[StatementBuilder]]:
|
69
|
+
par: Dict[StatementType, Optional[StatementBuilder]] = {input_type: None}
|
70
|
+
|
71
|
+
def _iterate() -> bool:
|
72
|
+
nonlocal par
|
73
|
+
for bdr in BUILDER_LIST:
|
74
|
+
u = bdr.input_type()
|
75
|
+
if u not in par:
|
76
|
+
continue
|
77
|
+
v = bdr.output_type()
|
78
|
+
if v in par:
|
79
|
+
continue
|
80
|
+
par[v] = bdr
|
81
|
+
return True
|
82
|
+
return False
|
83
|
+
|
84
|
+
while _iterate() and output_type not in par:
|
85
|
+
pass
|
86
|
+
|
87
|
+
if output_type not in par:
|
88
|
+
return None
|
89
|
+
|
90
|
+
res = []
|
91
|
+
cur = output_type
|
92
|
+
while par[cur] is not None:
|
93
|
+
res.append(par[cur])
|
94
|
+
cur = typing.cast(StatementBuilder, par[cur]).input_type()
|
95
|
+
|
96
|
+
return list(reversed(res))
|
97
|
+
|
98
|
+
|
99
|
+
def _try_implicit_builders(
|
100
|
+
statement_id: str, input_type: StatementType, output_type: StatementType
|
101
|
+
) -> List[StatementBuilder]:
|
102
|
+
implicit_builders = get_implicit_builders(input_type, output_type)
|
103
|
+
if implicit_builders is None:
|
104
|
+
console.console.print(
|
105
|
+
f'[error]Cannot implicitly convert statement [item]{statement_id}[/item] '
|
106
|
+
f'from [item]{input_type}[/item] '
|
107
|
+
f'to specified output type [item]{output_type}[/item].[/error]'
|
108
|
+
)
|
109
|
+
raise typer.Exit(1)
|
110
|
+
console.console.print(
|
111
|
+
'Implicitly adding statement builders to convert statement '
|
112
|
+
f'from [item]{input_type}[/item] to [item]{output_type}[/item]...'
|
113
|
+
)
|
114
|
+
return implicit_builders
|
115
|
+
|
116
|
+
|
117
|
+
def _get_configured_params_for(
|
118
|
+
configure: List[ConversionStep], conversion_type: ConversionType
|
119
|
+
) -> Optional[ConversionStep]:
|
120
|
+
for step in configure:
|
121
|
+
if step.type == conversion_type:
|
122
|
+
return step
|
123
|
+
return None
|
124
|
+
|
125
|
+
|
126
|
+
def get_builders(
|
127
|
+
statement_id: str,
|
128
|
+
steps: List[ConversionStep],
|
129
|
+
configure: List[ConversionStep],
|
130
|
+
input_type: StatementType,
|
131
|
+
output_type: Optional[StatementType],
|
132
|
+
builder_list: List[StatementBuilder] = BUILDER_LIST,
|
133
|
+
) -> List[Tuple[StatementBuilder, ConversionStep]]:
|
134
|
+
last_output = input_type
|
135
|
+
builders: List[Tuple[StatementBuilder, ConversionStep]] = []
|
136
|
+
|
137
|
+
# Conversion steps to force during build.
|
138
|
+
for step in steps:
|
139
|
+
builder = get_builder(step.type, builder_list=builder_list)
|
140
|
+
if builder.input_type() != last_output:
|
141
|
+
implicit_builders = _try_implicit_builders(
|
142
|
+
statement_id, last_output, builder.input_type()
|
143
|
+
)
|
144
|
+
builders.extend(
|
145
|
+
(builder, builder.default_params()) for builder in implicit_builders
|
146
|
+
)
|
147
|
+
builders.append((builder, step))
|
148
|
+
last_output = builder.output_type()
|
149
|
+
|
150
|
+
if output_type is not None and last_output != output_type:
|
151
|
+
implicit_builders = _try_implicit_builders(
|
152
|
+
statement_id, last_output, output_type
|
153
|
+
)
|
154
|
+
builders.extend(
|
155
|
+
(builder, builder.default_params()) for builder in implicit_builders
|
156
|
+
)
|
157
|
+
|
158
|
+
# Override statement configs.
|
159
|
+
def reconfigure(params: ConversionStep) -> ConversionStep:
|
160
|
+
new_params = _get_configured_params_for(configure, params.type)
|
161
|
+
return new_params or params
|
162
|
+
|
163
|
+
reconfigured_builders = [
|
164
|
+
(builder, reconfigure(params)) for builder, params in builders
|
165
|
+
]
|
166
|
+
return reconfigured_builders
|
167
|
+
|
168
|
+
|
169
|
+
def get_relative_assets(
|
170
|
+
relative_to: pathlib.Path,
|
171
|
+
assets: List[str],
|
172
|
+
) -> List[Tuple[pathlib.Path, pathlib.Path]]:
|
173
|
+
relative_to = relative_to.resolve()
|
174
|
+
if not relative_to.is_dir():
|
175
|
+
relative_to = relative_to.parent
|
176
|
+
res = []
|
177
|
+
for asset in assets:
|
178
|
+
relative_path = pathlib.Path(asset)
|
179
|
+
if not relative_path.is_file():
|
180
|
+
globbed = list(
|
181
|
+
path
|
182
|
+
for path in pathlib.Path().glob(str(relative_path))
|
183
|
+
if path.is_file()
|
184
|
+
)
|
185
|
+
if not globbed and '*' not in str(relative_path):
|
186
|
+
console.console.print(
|
187
|
+
f'[error]Asset [item]{asset}[/item] does not exist.[/error]'
|
188
|
+
)
|
189
|
+
raise typer.Exit(1)
|
190
|
+
res.extend(get_relative_assets(relative_to, list(map(str, globbed))))
|
191
|
+
continue
|
192
|
+
if not relative_path.resolve().is_relative_to(relative_to):
|
193
|
+
console.console.print(
|
194
|
+
f'[error]Asset [item]{asset}[/item] is not relative to your statement.[/error]'
|
195
|
+
)
|
196
|
+
raise typer.Exit(1)
|
197
|
+
|
198
|
+
res.append(
|
199
|
+
(
|
200
|
+
relative_path.resolve(),
|
201
|
+
relative_path.resolve().relative_to(relative_to),
|
202
|
+
)
|
203
|
+
)
|
204
|
+
|
205
|
+
return res
|
206
|
+
|
207
|
+
|
208
|
+
def build_statement_bytes(
|
209
|
+
statement: Statement,
|
210
|
+
pkg: Package,
|
211
|
+
output_type: Optional[StatementType] = None,
|
212
|
+
short_name: Optional[str] = None,
|
213
|
+
overridden_params_root: pathlib.Path = pathlib.Path(),
|
214
|
+
overridden_params: Optional[Dict[ConversionType, ConversionStep]] = None,
|
215
|
+
overridden_assets: Optional[List[Tuple[pathlib.Path, pathlib.Path]]] = None,
|
216
|
+
use_samples: bool = True,
|
217
|
+
is_editorial: bool = False,
|
218
|
+
) -> Tuple[bytes, StatementType]:
|
219
|
+
overridden_params = overridden_params or {}
|
220
|
+
overridden_assets = overridden_assets or []
|
221
|
+
|
222
|
+
if not statement.path.is_file():
|
223
|
+
console.console.print(
|
224
|
+
f'[error]Statement file [item]{statement.path}[/item] does not exist.[/error]'
|
225
|
+
)
|
226
|
+
raise typer.Exit(1)
|
227
|
+
builders = get_builders(
|
228
|
+
str(statement.path),
|
229
|
+
statement.steps,
|
230
|
+
statement.configure,
|
231
|
+
statement.type,
|
232
|
+
output_type,
|
233
|
+
builder_list=PROBLEM_BUILDER_LIST,
|
234
|
+
)
|
235
|
+
last_output = statement.type
|
236
|
+
last_content = statement.path.read_bytes()
|
237
|
+
for bdr, params in builders:
|
238
|
+
with tempfile.TemporaryDirectory() as td:
|
239
|
+
# Here, create a new temp context for each builder call.
|
240
|
+
assets = get_relative_assets(statement.path, statement.assets)
|
241
|
+
|
242
|
+
# Use either overridden assets (by contest) or usual assets.
|
243
|
+
# Remember to modify the root to contest root if that's the case.
|
244
|
+
if bdr.name() in overridden_params:
|
245
|
+
assets.extend(
|
246
|
+
bdr.inject_assets(
|
247
|
+
overridden_params_root, overridden_params[bdr.name()]
|
248
|
+
)
|
249
|
+
)
|
250
|
+
else:
|
251
|
+
assets.extend(bdr.inject_assets(pathlib.Path(), params))
|
252
|
+
assets.extend(overridden_assets)
|
253
|
+
|
254
|
+
prepare_assets(assets, pathlib.Path(td))
|
255
|
+
output = bdr.build(
|
256
|
+
input=last_content,
|
257
|
+
context=StatementBuilderContext(
|
258
|
+
languages=get_environment_languages_for_statement(),
|
259
|
+
params=params,
|
260
|
+
root=pathlib.Path(td),
|
261
|
+
editorial=is_editorial,
|
262
|
+
),
|
263
|
+
item=StatementBuilderProblem(
|
264
|
+
package=pkg,
|
265
|
+
statement=statement,
|
266
|
+
samples=get_samples() if use_samples else [],
|
267
|
+
short_name=short_name,
|
268
|
+
),
|
269
|
+
verbose=False,
|
270
|
+
)
|
271
|
+
last_output = bdr.output_type()
|
272
|
+
last_content = output
|
273
|
+
|
274
|
+
return last_content, last_output
|
275
|
+
|
276
|
+
|
277
|
+
def build_statement(
|
278
|
+
statement: Statement,
|
279
|
+
pkg: Package,
|
280
|
+
output_type: Optional[StatementType] = None,
|
281
|
+
use_samples: bool = True,
|
282
|
+
is_editorial: bool = False,
|
283
|
+
) -> pathlib.Path:
|
284
|
+
last_content, last_output = build_statement_bytes(
|
285
|
+
statement,
|
286
|
+
pkg,
|
287
|
+
output_type=output_type,
|
288
|
+
use_samples=use_samples,
|
289
|
+
is_editorial=is_editorial,
|
290
|
+
)
|
291
|
+
statement_path = (
|
292
|
+
package.get_build_path()
|
293
|
+
/ f'{statement.path.stem}{last_output.get_file_suffix()}'
|
294
|
+
)
|
295
|
+
statement_path.parent.mkdir(parents=True, exist_ok=True)
|
296
|
+
statement_path.write_bytes(last_content)
|
297
|
+
console.console.print(
|
298
|
+
f'Statement built successfully for language '
|
299
|
+
f'[item]{statement.language}[/item] at '
|
300
|
+
f'[item]{statement_path}[/item].'
|
301
|
+
)
|
302
|
+
return statement_path
|
303
|
+
|
304
|
+
|
305
|
+
@app.command('build, b', help='Build statements.')
|
306
|
+
@package.within_problem
|
307
|
+
def build(
|
308
|
+
verification: environment.VerificationParam,
|
309
|
+
languages: Annotated[
|
310
|
+
Optional[List[str]],
|
311
|
+
typer.Option(
|
312
|
+
default_factory=list,
|
313
|
+
help='Languages to build statements for. If not specified, build statements for all available languages.',
|
314
|
+
),
|
315
|
+
],
|
316
|
+
output: Annotated[
|
317
|
+
Optional[StatementType],
|
318
|
+
typer.Option(
|
319
|
+
case_sensitive=False,
|
320
|
+
help='Output type to be generated. If not specified, will infer from the conversion steps specified in the package.',
|
321
|
+
),
|
322
|
+
] = StatementType.PDF,
|
323
|
+
samples: Annotated[
|
324
|
+
bool,
|
325
|
+
typer.Option(help='Whether to build the statement with samples or not.'),
|
326
|
+
] = True,
|
327
|
+
editorial: Annotated[
|
328
|
+
bool, typer.Option(help='Whether to add editorial blocks to the statements.')
|
329
|
+
] = False,
|
330
|
+
):
|
331
|
+
# At most run the validators, only in samples.
|
332
|
+
if samples:
|
333
|
+
builder.build(verification=verification, groups=set(['samples']))
|
334
|
+
|
335
|
+
pkg = package.find_problem_package_or_die()
|
336
|
+
candidate_languages = languages
|
337
|
+
if not candidate_languages:
|
338
|
+
candidate_languages = sorted(set([st.language for st in pkg.statements]))
|
339
|
+
|
340
|
+
for language in candidate_languages:
|
341
|
+
candidates_for_lang = [st for st in pkg.statements if st.language == language]
|
342
|
+
if not candidates_for_lang:
|
343
|
+
console.console.print(
|
344
|
+
f'[error]No statement found for language [item]{language}[/item].[/error]',
|
345
|
+
)
|
346
|
+
raise typer.Exit(1)
|
347
|
+
|
348
|
+
build_statement(
|
349
|
+
candidates_for_lang[0],
|
350
|
+
pkg,
|
351
|
+
output_type=output,
|
352
|
+
use_samples=samples,
|
353
|
+
is_editorial=editorial,
|
354
|
+
)
|
355
|
+
|
356
|
+
|
357
|
+
@app.callback()
|
358
|
+
def callback():
|
359
|
+
pass
|