From 709a28c3ffb846d2bda1a496a973795c2ef95d69 Mon Sep 17 00:00:00 2001 From: Andrea Ciprietti Date: Sun, 15 Jan 2023 22:51:05 +0100 Subject: [PATCH] Added fetch-from-contest feature --- p2d/p2d.py | 19 +++++++++++++------ p2d/p2d_utils.py | 29 +++++++++++++++++++++++++++++ p2d/polygon_api.py | 5 ++++- 3 files changed, 46 insertions(+), 7 deletions(-) diff --git a/p2d/p2d.py b/p2d/p2d.py index 8a3eae6..145e380 100644 --- a/p2d/p2d.py +++ b/p2d/p2d.py @@ -23,6 +23,7 @@ def prepare_argument_parser(): parser.add_argument('--polygon', '--import', '--get', '--download', action='store_true', help='Whether the problem packages should be downloaded from Polygon. Otherwise only the packages already present in the system will be considered.') parser.add_argument('--convert', action='store_true', help='Whether the polygon packages should be converted to DOMjudge packages. Otherwise only the DOMjudge packages already present in the system will be considered.') parser.add_argument('--domjudge', '--export', '--send', '--upload', action='store_true', help='Whether the DOMjudge packages shall be uploaded to the DOMjudge instance specified in config.yaml.') + parser.add_argument('--from-contest', type=int, help='Update config.yaml with the problems of the specified Polygon contest.') parser.add_argument('--pdf-contest', action='store_true', help='Whether the pdf of the whole problemset and the pdf with all the solutions should be generated. If set, the files are created in \'contest_dir/tex/problemset.pdf\' and \'contest_dir/tex/solutions.pdf\'.') parser.add_argument('--verbosity', choices=['debug', 'info', 'warning'], default='info', help='Verbosity of the logs.') @@ -50,9 +51,10 @@ def p2d(args): p2d_utils.validate_config_yaml(config) if not args.polygon and not args.convert and not args.domjudge \ + and args.from_contest is None \ and not args.pdf_contest \ and not args.clear_dir and not args.clear_domjudge_ids: - logging.error('At least one of the flags --polygon, --convert, --domjudge, --contestpdf, --clear-dir, --clear-domjudge-ids is necessary.') + logging.error('At least one of the flags --polygon, --convert, --domjudge, --from-contest, --contestpdf, --clear-dir, --clear-domjudge-ids is necessary.') exit(1) if args.clear_dir: @@ -75,11 +77,12 @@ def p2d(args): p2d_utils.save_config_yaml(config, contest_dir) logging.info('Deleted the DOMjudge IDs from config.yaml.') - if args.polygon and ('polygon' not in config - or 'key' not in config['polygon'] - or 'secret' not in config['polygon']): + if (args.polygon or args.from_contest) \ + and ('polygon' not in config + or 'key' not in config['polygon'] + or 'secret' not in config['polygon']): logging.error('The entries polygon:key and polygon:secret must be ' - 'present in config.yaml to download problems from polygon.') + 'present in config.yaml to access Polygon problems.') exit(1) if args.domjudge and ('domjudge' not in config @@ -89,13 +92,17 @@ def p2d(args): or 'password' not in config['domjudge']): logging.error('The entries domjudge:contest_id, domjudge:server, ' 'domjudge:username, domjudge:password must be present ' - 'in config.yaml to download problems from polygon.') + 'in config.yaml to upload problems on DOMjudge.') exit(1) pathlib.Path(os.path.join(contest_dir, 'polygon')).mkdir(exist_ok=True) pathlib.Path(os.path.join(contest_dir, 'domjudge')).mkdir(exist_ok=True) pathlib.Path(os.path.join(contest_dir, 'tex')).mkdir(exist_ok=True) + if args.from_contest is not None: + p2d_utils.fill_config_from_contest(config, args.from_contest) + p2d_utils.save_config_yaml(config, contest_dir) + # Process the problems, one at a time. # For each problem some of the following operations are performed (depending # on the command line flags used to run the command): diff --git a/p2d/p2d_utils.py b/p2d/p2d_utils.py index 6ac720f..821c541 100644 --- a/p2d/p2d_utils.py +++ b/p2d/p2d_utils.py @@ -204,6 +204,35 @@ def manage_domjudge(config, domjudge_dir, problem): logging.info('Updated the DOMjudge package on the server \'%s\', with id = \'%s\'.' % (config['domjudge']['server'], problem['domjudge_id'])) +# Updates config with the data of the problems in the specified contest. +def fill_config_from_contest(config, contest_id): + contest_problems = polygon_api.get_contest_problems( + config['polygon']['key'], config['polygon']['secret'], + contest_id + ) + logging.info('Fetched problems from contest {}.'.format(contest_id)) + + new_problems = [] + + for label in contest_problems: + problem = contest_problems[label] + if problem['deleted']: + continue + if problem['id'] not in [p['polygon_id'] for p in config['problems']]: + config['problems'].append({ + 'name': problem['name'], + 'polygon_id': problem['id'] + }) + new_problems.append(problem['name']) + config_problem = [p for p in config['problems'] if p['polygon_id'] == problem['id']][0] + if 'label' not in config_problem: + config_problem['label'] = label + + if len(new_problems) > 0: + logging.info('Found new problems: {}.'.format(', '.join(new_problems))) + else: + logging.info('No new problems were found in the contest.') + # Generates contest_dir/tex/problemset.pdf and contest_dir/tex/solutions.pdf. def generate_problemset_solutions(config, contest_dir): pdf_generation_params = { diff --git a/p2d/polygon_api.py b/p2d/polygon_api.py index 3b9acfc..f4fa5ba 100644 --- a/p2d/polygon_api.py +++ b/p2d/polygon_api.py @@ -73,4 +73,7 @@ def download_package(key, secret, problem_id, package_id, polygon_zip): 'type': 'linux'}) with open(polygon_zip, "wb") as f: f.write(io.BytesIO(package.content).getbuffer()) - + +# Fetches the list of problems of the specified contest. +def get_contest_problems(key , secret, contest_id): + return call_polygon_api(key, secret, 'contest.problems', {'contestId': contest_id}).json()['result']