diff --git a/bin/config.py b/bin/config.py index c37d40a3..40170f8f 100644 --- a/bin/config.py +++ b/bin/config.py @@ -23,6 +23,16 @@ VALIDATION_MODES = ['default', 'custom', 'custom interactive'] +KNOWN_LICENSES = [ + 'cc by-sa', + 'cc by', + 'cc0', + 'public domain', + 'educational', + 'permission', + 'unknown', +] + MAX_PRIORITY = max(PRIORITY.values()) MAX_PRIORITY_VERDICT = [v for v in PRIORITY if PRIORITY[v] == MAX_PRIORITY] @@ -46,13 +56,15 @@ '.jpg', '.svg', '.pdf', - '.gif', + #'.args', + #'.files', # this is actually a folder ] KNOWN_TEXT_DATA_EXTENSIONS = KNOWN_TESTCASE_EXTENSIONS + [ '.interaction', '.hint', '.desc', + #'.args', ] INVALID_CASE_DIRECTORIES = [ diff --git a/bin/export.py b/bin/export.py index 7a470eee..9e341a69 100644 --- a/bin/export.py +++ b/bin/export.py @@ -104,37 +104,32 @@ def build_samples_zip(problems, statement_language): def build_problem_zip(problem, output, statement_language): """Make DOMjudge ZIP file for specified problem.""" + deprecated = [ # may be removed at some point. + 'domjudge-problem.ini', + ] + files = [ - ('domjudge-problem.ini', False), # DEPRECATED, may be removed at some point. ('problem.yaml', True), ('problem_statement/*', True), - ('data/secret/**/*.in', True), - ('data/secret/**/*.ans', True), ('submissions/accepted/**/*', True), ('submissions/*/**/*', False), - ('attachments/**/*', False), + ('attachments/**/*', problem.interactive), ] - for ext in config.KNOWN_DATA_EXTENSIONS: - files += [ - (f'data/sample/**/*{ext}', False), - (f'data/secret/**/*{ext}', False), - ] + testcases = [ + ('data/secret/**/*.in', True), + ('data/sample/**/*.in', not problem.interactive), + ] - if not config.args.kattis: - files += [('.timelimit', True)] - files += [(f'problem.{statement_language}.pdf', True)] + if problem.interactive: + # .interaction files don't need a corresponding .in + # therefore we can handle them like all other files + files += [('data/sample/**/*.interaction', False)] - if not problem.interactive: - # Glob, required? - files += [ - ('data/sample/**/*.in', True), - ('data/sample/**/*.ans', True), - ] - else: + if not config.args.kattis: files += [ - # Either .interaction or .in.statement should be present, but we only care about .interaction here. - ('data/sample/**/*.interaction', False), + ('.timelimit', True), + (f'problem.{statement_language}.pdf', True), ('data/sample/**/*.in.statement', False), ('data/sample/**/*.ans.statement', False), ] @@ -147,6 +142,17 @@ def build_problem_zip(problem, output, statement_language): print("Preparing to make ZIP file for problem dir %s" % problem.path, file=sys.stderr) + # Warn for all deprecated files but still add them to the files list + for pattern in deprecated: + files.append((pattern, False)) + # Only include hidden files if the pattern starts with a '.'. + paths = list(util.glob(problem.path, pattern, include_hidden=pattern[0] == '.')) + if len(paths) > 0: + addition = '' + if len(paths) > 1: + addition = f' and {len(paths) - 1} more' + util.warn(f'Found deprecated file "{paths[0]}"{addition}.') + # Build list of files to store in ZIP file. copyfiles = set() @@ -165,6 +171,25 @@ def build_problem_zip(problem, output, statement_language): out = problem.name / out copyfiles.add((f, out)) + for pattern, required in testcases: + paths = list(util.glob(problem.path, pattern)) + if required and len(paths) == 0: + util.error(f'No matches for required path {pattern}.') + for f in paths: + # NOTE: Directories are skipped because ZIP only supports files. + if f.is_file(): + if not f.with_suffix('.ans').is_file(): + util.warn(f'No answer file found for {f}, skipping.') + else: + for ext in config.KNOWN_DATA_EXTENSIONS: + f2 = f.with_suffix(ext) + if f2.is_file(): + out = f2.relative_to(problem.path) + # For Kattis, prepend the problem shortname to all files. + if config.args.kattis: + out = problem.name / out + copyfiles.add((f2, out)) + # Build .ZIP file. print("writing ZIP file:", output, file=sys.stderr) diff --git a/bin/problem.py b/bin/problem.py index b7754bf2..9c66b182 100644 --- a/bin/problem.py +++ b/bin/problem.py @@ -29,6 +29,7 @@ def __init__(self, path, tmpdir, label=None): # The Path of the problem directory. self.path = path self.tmpdir = tmpdir / self.name + self.tmpdir.mkdir(parents=True, exist_ok=True) # Read problem.yaml and domjudge-problem.ini into self.settings Namespace object. self._read_settings() diff --git a/bin/skel.py b/bin/skel.py index 3553d853..e3baa30f 100644 --- a/bin/skel.py +++ b/bin/skel.py @@ -71,10 +71,6 @@ def _ask_variable_choice(name, choices, default=None): return _ask_variable(name + text, default if default else '') -def _license_choices(): - return ['cc by-sa', 'cc by', 'cc0', 'public domain', 'educational', 'permission', 'unknown'] - - # Returns the alphanumeric version of a string: # This reduces it to a string that follows the regex: # [a-zA-Z0-9][a-zA-Z0-9_.-]*[a-zA-Z0-9] @@ -101,7 +97,7 @@ def new_contest(): testsession = _ask_variable_bool('testsession', False) year = _ask_variable_string('year', str(datetime.datetime.now().year)) source_url = _ask_variable_string('source url', '', True) - license = _ask_variable_choice('license', _license_choices()) + license = _ask_variable_choice('license', config.KNOWN_LICENSES) rights_owner = _ask_variable_string('rights owner', 'author') title = title.replace('_', '-') @@ -153,11 +149,11 @@ def new_problem(): validator_flags = '' if config.args.validation: - assert config.args.validation in ['default', 'custom', 'custom interactive'] + assert config.args.validation in config.VALIDATION_MODES validation = config.args.validation else: validation = _ask_variable_choice( - 'validation', ['default', 'float', 'custom', 'custom interactive'] + 'validation', config.VALIDATION_MODES[:1] + ['float'] + config.VALIDATION_MODES[1:] ) if validation == 'float': validation = 'default' @@ -183,7 +179,7 @@ def new_problem(): 'source url', variables.get('source_url', ''), True ) variables['license'] = _ask_variable_choice( - 'license', _license_choices(), variables.get('license', None) + 'license', config.KNOWN_LICENSES, variables.get('license', None) ) variables['rights_owner'] = _ask_variable_string( 'rights owner', variables.get('rights_owner', 'author')