Skip to content

Commit b721537

Browse files
mzuennimpsijm
andcommitted
Legacy export (#441)
* make legacy export an explicit command * [export] Add all the directories! * [export] Legacy: remove solution/ and problem_slide/ from export dir * [export] Make answer_validators/ not required Apparently, `bt validate` also doesn't require them? * prepend problem name * fix test * handle languages * use ngerman * add german to wsl * keep languages in sync * keep languages in sync * [export] Add comment explaining why name in problems.yaml can also be str * [doc] Improve singular/plural in explanation of `--languages` * [export][latex] Rename --languages flag to --lang * [test][export] Add assertions for which PDFs should be in the ZIPs * [test] TestContest.test_zip: also remove constituent zip files after test completes --------- Co-authored-by: Maarten Sijm <[email protected]>
1 parent 825c8cd commit b721537

File tree

16 files changed

+413
-252
lines changed

16 files changed

+413
-252
lines changed

.github/workflows/ci.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,5 +50,6 @@ jobs:
5050
lmodern
5151
texlive-science
5252
latexmk
53+
texlive-lang-german
5354
- shell: wsl-bash {0}
5455
run: pytest

bin/config.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,6 @@
109109
"jobs": (os.cpu_count() or 1) // 2,
110110
"time": 600, # Used for `bt fuzz`
111111
"verbose": 0,
112-
"languages": None,
113112
}
114113

115114

@@ -120,7 +119,7 @@
120119
grep -Ev '^(h|jobs|time|verbose)$' | sed 's/^/"/;s/$/",/' | tr '\n' ' ' | sed 's/^/ARGS_LIST: Final[Sequence[str]] = [/;s/, $/]\n/'
121120
"""
122121
# fmt: off
123-
ARGS_LIST: Final[Sequence[str]] = ["1", "add", "all", "answer", "api", "author", "check_deterministic", "clean", "colors", "contest", "contest_id", "contestname", "cp", "defaults", "default_solution", "depth", "directory", "error", "force", "force_build", "generic", "input", "interaction", "interactive", "invalid", "kattis", "language", "latest_bt", "memory", "more", "move_to", "no_bar", "no_generate", "no_solution", "no_solutions", "no_testcase_sanity_checks", "no_time_limit", "no_validators", "no_visualizer", "open", "order", "order_from_ccs", "overview", "password", "post_freeze", "problem", "problemname", "remove", "reorder", "samples", "sanitizer", "skel", "skip", "sort", "submissions", "table", "testcases", "time_limit", "timeout", "token", "tree", "type", "username", "valid_output", "watch", "web", "write"]
122+
ARGS_LIST: Final[Sequence[str]] = ["1", "add", "all", "answer", "api", "author", "check_deterministic", "clean", "colors", "contest", "contest_id", "contestname", "cp", "defaults", "default_solution", "depth", "directory", "error", "force", "force_build", "generic", "input", "interaction", "interactive", "invalid", "kattis", "lang", "latest_bt", "legacy", "memory", "more", "move_to", "no_bar", "no_generate", "no_solution", "no_solutions", "no_testcase_sanity_checks", "no_time_limit", "no_validators", "no_visualizer", "open", "order", "order_from_ccs", "overview", "password", "post_freeze", "problem", "problemname", "remove", "reorder", "samples", "sanitizer", "skel", "skip", "sort", "submissions", "table", "testcases", "time_limit", "timeout", "token", "tree", "type", "username", "valid_output", "watch", "web", "write"]
124123
# fmt: on
125124

126125

bin/export.py

Lines changed: 217 additions & 171 deletions
Large diffs are not rendered by default.

bin/latex.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -396,21 +396,21 @@ def build_problem_pdf(problem: "Problem", language: str, build_type=PdfType.PROB
396396

397397
def build_problem_pdfs(problem: "Problem", build_type=PdfType.PROBLEM, web=False):
398398
"""Build PDFs for various languages. If list of languages is specified,
399-
(either via config files or --language arguments), build those. Otherwise
399+
(either via config files or --lang arguments), build those. Otherwise
400400
build all languages for which there is a statement latex source.
401401
"""
402-
if config.args.languages is not None:
403-
for lang in config.args.languages:
402+
if config.args.lang is not None:
403+
for lang in config.args.lang:
404404
if lang not in problem.statement_languages:
405405
message(
406406
f"No statement source for language {lang}",
407407
problem.name,
408408
color_type=MessageType.FATAL,
409409
)
410-
languages = config.args.languages
410+
languages = config.args.lang
411411
else:
412412
languages = problem.statement_languages
413-
# For solutions or problem slides, filter for `<build_type>.<language>.tex` files that exist.
413+
# For solutions or problem slides, filter for `<build_type>.<lang>.tex` files that exist.
414414
if build_type != PdfType.PROBLEM:
415415
filtered_languages = []
416416
for lang in languages:
@@ -424,7 +424,7 @@ def build_problem_pdfs(problem: "Problem", build_type=PdfType.PROBLEM, web=False
424424
)
425425
languages = filtered_languages
426426
if config.args.watch and len(languages) > 1:
427-
fatal("--watch does not work with multiple languages. Please use --language")
427+
fatal("--watch does not work with multiple languages. Please use --lang")
428428
return all([build_problem_pdf(problem, lang, build_type, web) for lang in languages])
429429

430430

@@ -551,8 +551,8 @@ def build_contest_pdfs(contest, problems, tmpdir, lang=None, build_type=PdfType.
551551
message(
552552
"No statement language present in every problem.", contest, color_type=MessageType.FATAL
553553
)
554-
if config.args.languages is not None:
555-
languages = config.args.languages
554+
if config.args.lang is not None:
555+
languages = config.args.lang
556556
for lang in set(languages) - statement_languages:
557557
message(
558558
f"Unable to build all statements for language {lang}",
@@ -563,7 +563,7 @@ def build_contest_pdfs(contest, problems, tmpdir, lang=None, build_type=PdfType.
563563
languages = statement_languages
564564
if config.args.watch and len(languages) > 1:
565565
message(
566-
"--watch does not work with multiple languages. Please use --language",
566+
"--watch does not work with multiple languages. Please use --lang",
567567
contest,
568568
color_type=MessageType.FATAL,
569569
)

bin/skel.py

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
# Local imports
66
import config
77
import latex
8-
from export import force_single_language
98
from problem import Problem
109
from util import *
1110
import contest
@@ -144,7 +143,7 @@ def new_problem():
144143
if config.args.problem:
145144
fatal("--problem does not work for new_problem.")
146145

147-
statement_languages = config.args.languages if config.args.languages else ["en"]
146+
statement_languages = config.args.lang if config.args.lang else ["en"]
148147
main_language = "en" if "en" in statement_languages else statement_languages[0]
149148

150149
problemname = {
@@ -292,11 +291,6 @@ def rename_problem(problem):
292291
data["name"] = newname
293292
write_yaml(data, problem_yaml)
294293

295-
# DOMjudge does not yet support multilingual problems.yaml files.
296-
statement_language = force_single_language([problem])
297-
if isinstance(newname, dict):
298-
newname = newname[statement_language]
299-
300294
problems_yaml = Path("problems.yaml")
301295
if problems_yaml.is_file():
302296
data = read_yaml(problems_yaml) or []

bin/tools.py

Lines changed: 72 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -343,9 +343,7 @@ def build_parser():
343343
action="store_true",
344344
help="Copy the output pdf instead of symlinking it.",
345345
)
346-
global_parser.add_argument(
347-
"--language", dest="languages", action="append", help="Set language."
348-
)
346+
global_parser.add_argument("--lang", nargs="+", help="Languages to include.")
349347

350348
subparsers = parser.add_subparsers(
351349
title="actions", dest="action", parser_class=SuppressingParser
@@ -814,12 +812,22 @@ def build_parser():
814812
action="store_true",
815813
help="Make a zip more following the kattis problemarchive.com format.",
816814
)
815+
zipparser.add_argument(
816+
"--legacy",
817+
action="store_true",
818+
help="Make a zip more following the legacy format.",
819+
)
817820
zipparser.add_argument("--no-solutions", action="store_true", help="Do not compile solutions")
818821

819822
# Build a zip with all samples.
820-
subparsers.add_parser(
823+
samplezipparser = subparsers.add_parser(
821824
"samplezip", parents=[global_parser], help="Create zip file of all samples."
822825
)
826+
samplezipparser.add_argument(
827+
"--legacy",
828+
action="store_true",
829+
help="Make a zip more following the legacy format.",
830+
)
823831

824832
gitlab_parser = subparsers.add_parser(
825833
"gitlabci", parents=[global_parser], help="Print a list of jobs for the given contest."
@@ -856,6 +864,11 @@ def build_parser():
856864
action="store",
857865
help="Contest ID to use when writing to the API. Defaults to value of contest_id in contest.yaml.",
858866
)
867+
exportparser.add_argument(
868+
"--legacy",
869+
action="store_true",
870+
help="Make export more following the legacy format.",
871+
)
859872

860873
updateproblemsyamlparser = subparsers.add_parser(
861874
"update_problems_yaml",
@@ -871,6 +884,11 @@ def build_parser():
871884
action="store_true",
872885
help="Sort the problems by id.",
873886
)
887+
updateproblemsyamlparser.add_argument(
888+
"--legacy",
889+
action="store_true",
890+
help="Make problems.yaml more following the legacy format.",
891+
)
874892

875893
# Print the corresponding temporary directory.
876894
tmpparser = subparsers.add_parser(
@@ -1034,8 +1052,8 @@ def run_parsed_arguments(args):
10341052
sampleout = Path("samples.zip")
10351053
if level == "problem":
10361054
sampleout = problems[0].path / sampleout
1037-
statement_language = export.force_single_language(problems)
1038-
export.build_samples_zip(problems, sampleout, statement_language)
1055+
languages = export.select_languages(problems)
1056+
export.build_samples_zip(problems, sampleout, languages)
10391057
return
10401058

10411059
if action == "rename_problem":
@@ -1175,10 +1193,16 @@ def run_parsed_arguments(args):
11751193
config.args = old_args
11761194

11771195
if not config.args.kattis:
1178-
# Make sure that all problems use the same language for the PDFs
1179-
export.force_single_language(problems)
1180-
11811196
success &= latex.build_problem_pdfs(problem)
1197+
if not config.args.no_solutions:
1198+
success &= latex.build_problem_pdfs(
1199+
problem, build_type=latex.PdfType.SOLUTION
1200+
)
1201+
1202+
if problem.path.glob(str(latex.PdfType.PROBLEM_SLIDE.path("*"))):
1203+
success &= latex.build_problem_pdfs(
1204+
problem, build_type=latex.PdfType.PROBLEM_SLIDE
1205+
)
11821206

11831207
if not config.args.force:
11841208
success &= problem.validate_data(validate.Mode.INPUT, constraints={})
@@ -1192,10 +1216,8 @@ def run_parsed_arguments(args):
11921216
print(file=sys.stderr)
11931217

11941218
if action in ["export"]:
1195-
# Add contest PDF for only one language to DOMjudge
1196-
statement_language = export.force_single_language(problems)
1197-
1198-
export.export_contest_and_problems(problems, statement_language)
1219+
languages = export.select_languages(problems)
1220+
export.export_contest_and_problems(problems, languages)
11991221

12001222
if level == "problemset":
12011223
print(f"{Style.BRIGHT}CONTEST {contest}{Style.RESET_ALL}", file=sys.stderr)
@@ -1223,48 +1245,53 @@ def run_parsed_arguments(args):
12231245
)
12241246

12251247
if action in ["zip"]:
1226-
statement_language = None
1248+
languages = []
12271249
if not config.args.kattis:
1228-
# Add contest/solutions PDF for only one language to the zip file
1229-
statement_language = export.force_single_language(problems)
1250+
languages = export.select_languages(problems)
12301251

1231-
success &= latex.build_contest_pdfs(contest, problems, tmpdir, statement_language)
1232-
success &= latex.build_contest_pdfs(
1233-
contest, problems, tmpdir, statement_language, web=True
1234-
)
1235-
if not config.args.no_solutions:
1236-
success &= latex.build_contest_pdf(
1237-
contest,
1238-
problems,
1239-
tmpdir,
1240-
statement_language,
1241-
build_type=latex.PdfType.SOLUTION,
1242-
)
1243-
success &= latex.build_contest_pdf(
1244-
contest,
1245-
problems,
1246-
tmpdir,
1247-
statement_language,
1248-
build_type=latex.PdfType.SOLUTION,
1249-
web=True,
1250-
)
12511252
# Only build the problem slides if at least one problem has the TeX for it
12521253
slideglob = latex.PdfType.PROBLEM_SLIDE.path("*")
1253-
if any(problem.path.glob(str(slideglob)) for problem in problems):
1254-
success &= latex.build_contest_pdf(
1255-
contest,
1256-
problems,
1257-
tmpdir,
1258-
statement_language,
1259-
build_type=latex.PdfType.PROBLEM_SLIDE,
1254+
build_problem_slides = any(
1255+
problem.path.glob(str(slideglob)) for problem in problems
1256+
)
1257+
1258+
for language in languages:
1259+
success &= latex.build_contest_pdfs(contest, problems, tmpdir, language)
1260+
success &= latex.build_contest_pdfs(
1261+
contest, problems, tmpdir, language, web=True
12601262
)
1261-
else:
1263+
if not config.args.no_solutions:
1264+
success &= latex.build_contest_pdf(
1265+
contest,
1266+
problems,
1267+
tmpdir,
1268+
language,
1269+
build_type=latex.PdfType.SOLUTION,
1270+
)
1271+
success &= latex.build_contest_pdf(
1272+
contest,
1273+
problems,
1274+
tmpdir,
1275+
language,
1276+
build_type=latex.PdfType.SOLUTION,
1277+
web=True,
1278+
)
1279+
if build_problem_slides:
1280+
success &= latex.build_contest_pdf(
1281+
contest,
1282+
problems,
1283+
tmpdir,
1284+
language,
1285+
build_type=latex.PdfType.PROBLEM_SLIDE,
1286+
)
1287+
1288+
if not build_problem_slides:
12621289
log(f"No problem has {slideglob.name}, skipping problem slides")
12631290

12641291
outfile = contest + ".zip"
12651292
if config.args.kattis:
12661293
outfile = contest + "-kattis.zip"
1267-
export.build_contest_zip(problems, problem_zips, outfile, statement_language)
1294+
export.build_contest_zip(problems, problem_zips, outfile, languages)
12681295

12691296
if action in ["update_problems_yaml"]:
12701297
export.update_problems_yaml(

bin/upgrade.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -388,7 +388,6 @@ def _upgrade(problem_path: Path, bar: ProgressBar) -> None:
388388
upgrade_statement(problem_path, bar)
389389
upgrade_format_validators(problem_path, bar)
390390
upgrade_output_validators(problem_path, bar)
391-
# update .in.statement?
392391
upgrade_problem_yaml(problem_path, bar)
393392

394393
bar.done()

doc/commands.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ The flags below work for any subcommand:
5757
- `--no-bar`: Disable showing progress bars. This is useful when running in non-interactive contexts (such as CI jobs) or on platforms/terminals that don't handle the progress bars well.
5858
- `--error`/`-e`: show full output of failing commands using `--error`. The default is to show a short snippet only.
5959
- `--force-build`: Force rebuilding binaries instead of reusing cached version.
60-
- `--language <LANG>`: select a single language to use. `<LANG>` should be a language code like `en` or `nl`.
60+
- `--lang`: select languages to use for LaTeX commands. The languages should be specified by language codes like `en` or `nl`.
6161

6262
# Problem development
6363

doc/multiple_languages.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,18 @@ Here, `LANG` is a two-letter language code, see
1515

1616
It is expected that the languages keys in the metadata and statement files agree.
1717

18-
The default language for BAPCtools is English, but multiple languages can be specified at various points of the tool, typically using the `--language` flag or configuration files.
18+
The default language for BAPCtools is English, but multiple languages can be specified at various points of the tool, typically using the `--lang` flag or configuration files.
1919

2020
## Creating a contest
2121

2222
In short,
2323

24-
1. configure `languages` in `.bapctools.yaml`.
24+
1. configure `lang` in `.bapctools.yaml`.
2525
2. add a skeleton for `problem.LANG.tex` in `skel/problem/statement`.
2626

27-
### Configure `language`
27+
### Configure `lang`
2828

29-
To create a contest supporting French, Dutch, and Luxembourgish, set the configurartion key `languages` to the list `['nl', 'fr', 'lt']`.
29+
To create a contest supporting French, Dutch, and Luxembourgish, set the configurartion key `lang` to the list `['nl', 'fr', 'lt']`.
3030
Configuration keys can be set in many ways, see **Personal configuration file** in the BAPCtools documentation, but an easy way is to create a new contest:
3131

3232
```sh
@@ -36,7 +36,7 @@ bt new_contest
3636
and then create or extend the file `<contestdirectory>/.bapctools.yaml` with
3737

3838
```yaml
39-
languages:
39+
lang:
4040
- nl
4141
- fr
4242
- lt
@@ -82,13 +82,13 @@ To create a problem,
8282
bt new_problem
8383
```
8484

85-
will look for the `languages` configuration (for instance, at contest level) and use that by default.
85+
will look for the `lang` configuration (for instance, at contest level) and use that by default.
8686
Thus, if the contest is set up as above, you need to do nothing extra.
8787

8888
With arguments, or outside of a contest directory,
8989

9090
```sh
91-
bt new_problem --language en --language fr
91+
bt new_problem --lang en fr
9292
```
9393

9494
creates a problem with two languages, English and French.
@@ -108,7 +108,7 @@ creates PDFs for every problem language statement `problem.xy.tex`.
108108
With arguments,
109109

110110
```sh
111-
bt pdf --language en --language fr
111+
bt pdf --lang en fr
112112
```
113113

114114
produces PDFs for English and French.
@@ -117,7 +117,7 @@ The resulting PDFs are named `<problemdirectory>/problem.xy.pdf`.
117117

118118
## Solution PDF
119119

120-
Similarly, `bt solutions [--language en --language fr]` creates
120+
Similarly, `bt solutions [--lang en fr]` creates
121121
`<problemdirectory>/solution.xy.pdf` for the given languages, defaulting to
122122
all available `solution.xy.tex` files.
123123

latex/lang/de.tex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
\newcommand{\langbabel}{german}
1+
\newcommand{\langbabel}{ngerman}
22

33
% bapc.cls
44
\newcommand{\langblank}{Diese Seite wurde absichtlich leer gelassen.}

0 commit comments

Comments
 (0)