12
12
from problem import Problem
13
13
14
14
15
- # Replace \problemname{...} by the value of `name:` in problems.yaml in all .tex files.
16
- # This is needed because Kattis is currently still running the legacy version of the problem spec,
17
- # rather than 2023-07-draft.
18
- def fix_problem_name_cmd (problem ):
19
- reverts = []
20
- for f in (problem .path / "problem_statement" ).iterdir ():
21
- if f .is_file () and f .suffix == ".tex" and len (f .suffixes ) >= 2 :
22
- lang = f .suffixes [- 2 ][1 :]
23
- t = f .read_text ()
24
- match = re .search (r"\\problemname\{\s*(\\problemyamlname)?\s*\}" , t )
25
- if match :
26
- if lang in problem .settings .name :
27
- reverts .append ((f , t ))
28
- t = t .replace (match [0 ], r"\problemname{" + problem .settings .name [lang ] + "}" )
29
- f .write_text (t )
30
- else :
31
- util .error (f"{ f } : no name set for language { lang } ." )
32
-
33
- def revert ():
34
- for f , t in reverts :
35
- f .write_text (t )
36
-
37
- return revert
38
-
39
-
40
15
def force_single_language (problems ):
41
16
if config .args .languages and len (config .args .languages ) == 1 :
42
17
statement_language = config .args .languages [0 ]
@@ -115,18 +90,13 @@ def build_samples_zip(problems, output, statement_language):
115
90
116
91
117
92
def build_problem_zip (problem : Problem , output : Path ):
118
- """Make DOMjudge ZIP file for specified problem."""
93
+ """Make DOMjudge/Kattis ZIP file for specified problem."""
119
94
120
95
# Add problem PDF for only one language to the zip file (note that Kattis export does not include PDF)
121
96
statement_language = None if config .args .kattis else force_single_language ([problem ])
122
97
123
- deprecated = [ # may be removed at some point.
124
- "domjudge-problem.ini" ,
125
- ]
126
-
127
- write_file_strs : list [tuple [str , str ]] = []
128
-
129
98
files = [
99
+ ("problem.yaml" , True ),
130
100
("problem_statement/*" , True ),
131
101
("submissions/accepted/**/*" , True ),
132
102
("submissions/*/**/*" , False ),
@@ -156,56 +126,21 @@ def build_problem_zip(problem: Problem, output: Path):
156
126
if config .args .kattis :
157
127
files .append (("input_validators/**/*" , True ))
158
128
159
- print ("Preparing to make ZIP file for problem dir %s" % problem .path , file = sys .stderr )
160
-
161
- # DOMjudge does not support 'type' in problem.yaml nor 'output_validator_args' in testdata.yaml yet.
162
- # TODO: Remove this once it does.
163
- problem_yaml_str = (problem .path / "problem.yaml" ).read_text ()
164
- if not config .args .kattis :
165
- validator_flags = " " .join (
166
- problem .get_testdata_yaml (
167
- problem .path / "data" ,
168
- "output_validator_args" ,
169
- PrintBar ("Getting validator_flags for legacy DOMjudge export" ),
170
- )
171
- )
172
- if validator_flags :
173
- validator_flags = "validator_flags: " + validator_flags + "\n "
174
- write_file_strs .append (
175
- (
176
- "problem.yaml" ,
177
- f"""{ problem_yaml_str } \n validation: {
178
- "custom interactive"
179
- if problem .interactive
180
- else "custom multi-pass"
181
- if problem .multi_pass
182
- else "custom"
183
- if problem .custom_output
184
- else "default"
185
- } \n { validator_flags } """ ,
186
- )
187
- )
188
- else :
189
- write_file_strs .append (("problem.yaml" , problem_yaml_str ))
190
-
191
- # DOMjudge does not support 'limits.time_limit' in problem.yaml yet.
192
- # TODO: Remove this once it does.
193
- if not config .args .kattis :
194
- write_file_strs .append ((".timelimit" , str (problem .limits .time_limit )))
129
+ message ("preparing zip file content" , "Zip" , problem .path , color_type = MessageType .LOG )
195
130
196
- # Warn for all deprecated files but still add them to the files list
197
- for pattern in deprecated :
198
- files .append ((pattern , False ))
199
- # Only include hidden files if the pattern starts with a '.'.
200
- paths = list (util .glob (problem .path , pattern , include_hidden = pattern [0 ] == "." ))
201
- if len (paths ) > 0 :
202
- addition = ""
203
- if len (paths ) > 1 :
204
- addition = f" and { len (paths ) - 1 } more"
205
- util .warn (f'Found deprecated file "{ paths [0 ]} "{ addition } .' )
131
+ # prepare files inside dir
132
+ export_dir = problem .tmpdir / "export"
133
+ if export_dir .exists ():
134
+ shutil .rmtree (export_dir )
135
+ # For Kattis, prepend the problem shortname to all files.
136
+ if config .args .kattis :
137
+ export_dir /= problem .name
138
+ export_dir .mkdir (parents = True , exist_ok = True )
206
139
207
- # Build list of files to store in ZIP file.
208
- copyfiles = set ()
140
+ def add_file (path , source ):
141
+ path = export_dir / path
142
+ path .parent .mkdir (parents = True , exist_ok = True )
143
+ ensure_symlink (path , source )
209
144
210
145
# Include all files beside testcases
211
146
for pattern , required in files :
@@ -214,22 +149,17 @@ def build_problem_zip(problem: Problem, output: Path):
214
149
if required and len (paths ) == 0 :
215
150
util .error (f"No matches for required path { pattern } ." )
216
151
for f in paths :
217
- # NOTE: Directories are skipped because ZIP only supports files.
218
152
if f .is_file ():
219
153
out = f .relative_to (problem .path )
220
154
out = remove_language_suffix (out , statement_language )
221
- # For Kattis, prepend the problem shortname to all files.
222
- if config .args .kattis :
223
- out = problem .name / out
224
- copyfiles .add ((f , out ))
155
+ add_file (out , f )
225
156
226
157
# Include all testcases (specified by a .in file) and copy all related files
227
158
for pattern , required in testcases :
228
159
paths = list (util .glob (problem .path , pattern ))
229
160
if required and len (paths ) == 0 :
230
161
util .error (f"No matches for required path { pattern } ." )
231
162
for f in paths :
232
- # NOTE: Directories are skipped because ZIP only supports files.
233
163
if f .is_file ():
234
164
if not f .with_suffix (".ans" ).is_file ():
235
165
util .warn (f"No answer file found for { f } , skipping." )
@@ -238,31 +168,98 @@ def build_problem_zip(problem: Problem, output: Path):
238
168
f2 = f .with_suffix (ext )
239
169
if f2 .is_file ():
240
170
out = f2 .relative_to (problem .path )
241
- # For Kattis, prepend the problem shortname to all files.
242
- if config .args .kattis :
243
- out = problem .name / out
244
- copyfiles .add ((f2 , out ))
171
+ add_file (out , f2 )
245
172
246
- # Build .ZIP file.
247
- print ("writing ZIP file:" , output , file = sys .stderr )
173
+ # DOMjudge does not support 'type' in problem.yaml nor 'output_validator_args' in testdata.yaml yet.
174
+ # TODO: Remove this once it does.
175
+ if not config .args .kattis :
176
+ yaml_path = export_dir / "problem.yaml"
177
+ yaml_data = [yaml_path .read_text (), "\n validation:" ]
178
+ if problem .custom_output :
179
+ yaml_data .append (" custom" )
180
+ if problem .interactive :
181
+ yaml_data .append (" interactive" )
182
+ if problem .multi_pass :
183
+ yaml_data .append (" multi-pass" )
184
+ else :
185
+ yaml_data .append (" default" )
186
+ yaml_data .append ("\n " )
187
+
188
+ validator_flags = " " .join (
189
+ problem .get_testdata_yaml (
190
+ problem .path / "data" ,
191
+ "output_validator_args" ,
192
+ PrintBar ("Getting validator_flags for legacy DOMjudge export" ),
193
+ )
194
+ )
195
+ if validator_flags :
196
+ yaml_data .append (f"validator_flags: { validator_flags } \n " )
197
+
198
+ yaml_path .unlink ()
199
+ yaml_path .write_text ("" .join (yaml_data ))
200
+
201
+ # DOMjudge does not support 'limits.time_limit' in problem.yaml yet.
202
+ # TODO: Remove this once it does.
203
+ if not config .args .kattis :
204
+ (export_dir / ".timelimit" ).write_text (str (problem .limits .time_limit ))
248
205
249
- revert_problem_name_cmd = fix_problem_name_cmd (problem )
206
+ # Replace \problemname{...} by the value of `name:` in problems.yaml in all .tex files.
207
+ # This is needed because Kattis is currently still running the legacy version of the problem spec,
208
+ # rather than 2023-07-draft.
209
+ for f in (export_dir / "problem_statement" ).iterdir ():
210
+ if f .is_file () and f .suffix == ".tex" and len (f .suffixes ) >= 2 :
211
+ lang = f .suffixes [- 2 ][1 :]
212
+ t = f .read_text ()
213
+ match = re .search (r"\\problemname\{\s*(\\problemyamlname)?\s*\}" , t )
214
+ if match :
215
+ if lang in problem .settings .name :
216
+ t = t .replace (match [0 ], r"\problemname{" + problem .settings .name [lang ] + "}" )
217
+ f .unlink ()
218
+ f .write_text (t )
219
+ else :
220
+ util .error (f"{ f } : no name set for language { lang } ." )
221
+
222
+ # DOMjudge does not support constants.
223
+ # TODO: Remove this if it ever does.
224
+ if problem .settings .constants :
225
+ constants_supported = [
226
+ "data/**/testdata.yaml" ,
227
+ "output_validators/**/*" ,
228
+ "input_validators/**/*" ,
229
+ # "problem_statement/*", uses \constants
230
+ # "submissions/*/**/*", removed support?
231
+ ]
232
+ for pattern in constants_supported :
233
+ for f in export_dir .glob (pattern ):
234
+ if f .is_file () and util .has_substitute (f , config .CONSTANT_SUBSTITUTE_REGEX ):
235
+ text = f .read_text ()
236
+ text = util .substitute (
237
+ text ,
238
+ problem .settings .constants ,
239
+ pattern = config .CONSTANT_SUBSTITUTE_REGEX ,
240
+ bar = util .PrintBar ("Zip" ),
241
+ )
242
+ f .unlink ()
243
+ f .write_text (text )
250
244
245
+ # Build .ZIP file.
246
+ message ("writing zip file" , "Zip" , output , color_type = MessageType .LOG )
251
247
try :
252
248
zf = zipfile .ZipFile (output , mode = "w" , compression = zipfile .ZIP_DEFLATED , allowZip64 = False )
253
249
254
- for source , target in sorted (copyfiles ):
255
- zf .write (source , target , compress_type = zipfile .ZIP_DEFLATED )
256
- for target_file , content in sorted (write_file_strs ):
257
- zf .writestr (target_file , content , compress_type = zipfile .ZIP_DEFLATED )
250
+ export_dir = problem .tmpdir / "export"
251
+ for f in sorted (export_dir .rglob ("*" )):
252
+ # NOTE: Directories are skipped because ZIP only supports files.
253
+ if f .is_file ():
254
+ name = f .relative_to (export_dir )
255
+ zf .write (f , name , compress_type = zipfile .ZIP_DEFLATED )
258
256
259
257
# Done.
260
258
zf .close ()
261
- print ("done" , file = sys . stderr )
259
+ message ("done" , "Zip" , color_type = MessageType . LOG )
262
260
print (file = sys .stderr )
263
-
264
- finally :
265
- revert_problem_name_cmd ()
261
+ except Exception :
262
+ return False
266
263
267
264
return True
268
265
0 commit comments