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,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
|
+
)
|