diff --git a/cms/db/contest.py b/cms/db/contest.py index c8aba842cc..05b59a1b03 100644 --- a/cms/db/contest.py +++ b/cms/db/contest.py @@ -219,6 +219,9 @@ class Contest(Base): CheckConstraint("min_user_test_interval > '0 seconds'"), nullable=True) + # Time after which the minimum interval restriction for submissions is lifted + restricted_time = Column(Interval, nullable=True) + # The scores for this contest will be rounded to this number of # decimal places. score_precision = Column( diff --git a/cms/server/admin/handlers/contest.py b/cms/server/admin/handlers/contest.py index f2da7631be..0d823d6eb5 100644 --- a/cms/server/admin/handlers/contest.py +++ b/cms/server/admin/handlers/contest.py @@ -121,6 +121,7 @@ def post(self, contest_id): self.get_int(attrs, "max_user_test_number") self.get_timedelta_sec(attrs, "min_submission_interval") self.get_timedelta_sec(attrs, "min_user_test_interval") + self.get_timedelta_sec(attrs, "restricted_time") self.get_string(attrs, "timezone", empty=None) self.get_int(attrs, "score_precision") diff --git a/cms/server/admin/templates/contest.html b/cms/server/admin/templates/contest.html index d78d3a9106..2383bcbf33 100644 --- a/cms/server/admin/templates/contest.html +++ b/cms/server/admin/templates/contest.html @@ -251,6 +251,15 @@

Contest configuration

+ + + + Lift minimum submission interval restriction after + + + timedelta(): + lift_time = min(actual_stop, actual_start + restricted_time) + else: + lift_time = max(actual_start, actual_stop + restricted_time) + actual_phase = 0 if timestamp <= lift_time else 0.5 + current_phase_begin = actual_start if actual_phase == 0 else lift_time + current_phase_end = actual_stop if actual_phase == .5 or restricted_time is None else lift_time elif contest_start <= timestamp < actual_start: # This also includes a funny corner case: the user's start # is known but is in the future (the admin either set it diff --git a/cms/server/contest/submission/workflow.py b/cms/server/contest/submission/workflow.py index e3641a2bc5..7de4ddce2d 100644 --- a/cms/server/contest/submission/workflow.py +++ b/cms/server/contest/submission/workflow.py @@ -35,9 +35,12 @@ from .check import check_max_number, check_min_interval from .file_matching import InvalidFilesOrLanguage, match_files_and_language from .file_retrieval import InvalidArchive, extract_files_from_tornado -from .utils import fetch_file_digests_from_previous_submission, StorageFailed, \ - store_local_copy - +from .utils import ( + fetch_file_digests_from_previous_submission, + StorageFailed, + store_local_copy, +) +from ..phase_management import compute_actual_phase logger = logging.getLogger(__name__) @@ -62,7 +65,7 @@ def formatted_text(self): def accept_submission(sql_session, file_cacher, participation, task, timestamp, - tornado_files, language_name, official): + tornado_files, language_name, official, check_interval_restriction): """Process a contestant's request to submit a submission. Parse and validate the data that a contestant sent for a submission @@ -106,20 +109,36 @@ def accept_submission(sql_session, file_cacher, participation, task, timestamp, participation, task=task): raise UnacceptableSubmission( N_("Too many submissions!"), - N_("You have reached the maximum limit of " - "at most %d submissions on this task."), - task.max_submission_number) - - if not check_min_interval(sql_session, contest.min_submission_interval, - timestamp, participation, contest=contest): + N_( + "You have reached the maximum limit of " + "at most %d submissions on this task." + ), + task.max_submission_number, + ) + + if check_interval_restriction and not check_min_interval( + sql_session, + contest.min_submission_interval, + timestamp, + participation, + contest=contest, + ): raise UnacceptableSubmission( N_("Submissions too frequent!"), - N_("Among all tasks, you can submit again " - "after %d seconds from last submission."), - contest.min_submission_interval.total_seconds()) - - if not check_min_interval(sql_session, task.min_submission_interval, - timestamp, participation, task=task): + N_( + "Among all tasks, you can submit again " + "after %d seconds from last submission." + ), + contest.min_submission_interval.total_seconds(), + ) + + if check_interval_restriction and not check_min_interval( + sql_session, + task.min_submission_interval, + timestamp, + participation, + task=task, + ): raise UnacceptableSubmission( N_("Submissions too frequent!"), N_("For this task, you can submit again " diff --git a/cms/server/contest/templates/contest.html b/cms/server/contest/templates/contest.html index 5a25e0a835..ee31f41723 100644 --- a/cms/server/contest/templates/contest.html +++ b/cms/server/contest/templates/contest.html @@ -184,7 +184,7 @@

- {% if actual_phase == 0 or actual_phase == 3 or participation.unrestricted %} + {% if actual_phase == 0 or actual_phase == 0.5 or actual_phase == 3 or participation.unrestricted %} {% for t_iter in contest.tasks %} - {% if actual_phase == 0 or participation.unrestricted %}{# FIXME maybe >= 0? #} + {% if actual_phase == 0 or actual_phase == 0.5 or participation.unrestricted %}{# FIXME maybe >= 0? #} {% if testing_enabled %} {% trans %}Testing{% endtrans %} diff --git a/cms/server/contest/templates/macro/submission.html b/cms/server/contest/templates/macro/submission.html index 0059b03e6e..d1d3147c1c 100644 --- a/cms/server/contest/templates/macro/submission.html +++ b/cms/server/contest/templates/macro/submission.html @@ -53,7 +53,7 @@ {% set num_cols = num_cols + 1 %} {% endif %} -{% if can_use_tokens and actual_phase == 0 %} +{% if can_use_tokens and actual_phase == 0 or actual_phase == 0.5%} {% set num_cols = num_cols + 1 %} {% endif %} @@ -78,7 +78,7 @@ {% if submissions_download_allowed %} {% trans %}Files{% endtrans %} {% endif %} -{% if can_use_tokens and actual_phase == 0 %} +{% if can_use_tokens and (actual_phase == 0 or actual_phase == 0.5) %} {% trans %}Token{% endtrans %} {% endif %} @@ -255,7 +255,7 @@ {% endif %} {% endif %} -{% if can_use_tokens and actual_phase == 0 %} +{% if can_use_tokens and (actual_phase == 0 or actual_phase == 0.5) %} {% if s.token is not none %} {% trans %}Played{% endtrans %} diff --git a/cms/server/contest/templates/overview.html b/cms/server/contest/templates/overview.html index cf7cfc4163..86574f628e 100644 --- a/cms/server/contest/templates/overview.html +++ b/cms/server/contest/templates/overview.html @@ -146,7 +146,7 @@

{% trans %}General information{% endtrans %}

{% elif actual_phase == -1 %} {% trans %}By clicking on the button below you can start your time frame.{% endtrans %} {%+ trans %}Once you start, you can submit solutions until the end of the time frame or until the end of the contest, whatever comes first.{% endtrans %} - {% elif actual_phase == 0 %} + {% elif actual_phase == 0 or actual_phase == 0.5 %} {% trans start_time=participation.starting_time|format_datetime_smart %}You started your time frame at {{ start_time }}.{% endtrans %} {%+ trans %}You can submit solutions until the end of the time frame or until the end of the contest, whatever comes first.{% endtrans %} {% elif actual_phase == +1 %} @@ -179,7 +179,7 @@

{% trans %}General information{% endtrans %}

-{% if actual_phase == 0 or actual_phase == 3%} +{% if actual_phase == 0 or actual_phase == 0.5 or actual_phase == 3%}

{% trans %}Task overview{% endtrans %}

diff --git a/cms/server/contest/templates/task_submissions.html b/cms/server/contest/templates/task_submissions.html index acf77d139e..7e3c967b7f 100644 --- a/cms/server/contest/templates/task_submissions.html +++ b/cms/server/contest/templates/task_submissions.html @@ -17,7 +17,7 @@ {# Whether the user has a token to play (maybe after waiting some time). #} {% set can_play_token = can_use_tokens - and actual_phase == 0 + and (actual_phase == 0 or actual_phase == 0.5) and (tokens_info[0] > 0 or tokens_info[0] == -1) %} {# Whether the user can play a token right now (only meaningful when can_play_token = true). #} @@ -316,7 +316,7 @@

{% trans %}Submit a solution{% endtrans %}

{% trans %}Previous submissions{% endtrans %}

-{% if can_use_tokens_in_contest and actual_phase == 0 %} +{% if can_use_tokens_in_contest and (actual_phase == 0 or actual_phase == 0.5) %}
{% if score_type.feedback() in ["full", "partial", "no"] %} {% elif not can_use_tokens %} @@ -343,7 +343,7 @@

{% trans %}Previous submissions{% endtrans %}0 restrictions are active until start + restricted_time + """ + self.restricted_time = restricted_time + def short_path(self, f): """ Return a (possibly) shorter name for a file (which can be relative @@ -566,7 +581,8 @@ def _makecontest(self): cdb.min_submission_interval = self.min_submission_interval cdb.max_user_test_number = self.max_user_test_number cdb.min_user_test_interval = self.min_user_test_interval - + cdb.restricted_time = self.restricted_time + self.usersdb = {} self.participationsdb = {} diff --git a/cmscontrib/loaders/italy_yaml.py b/cmscontrib/loaders/italy_yaml.py index 9243d951b9..b0372d1786 100644 --- a/cmscontrib/loaders/italy_yaml.py +++ b/cmscontrib/loaders/italy_yaml.py @@ -216,6 +216,7 @@ def get_contest(self): load(conf, args, "max_user_test_number") load(conf, args, "min_submission_interval", conv=make_timedelta) load(conf, args, "min_user_test_interval", conv=make_timedelta) + load(conf, args, "restricted_time", conv=make__timedelta) tasks = load(conf, None, ["tasks", "problemi"]) participations = load(conf, None, ["users", "utenti"]) diff --git a/cmscontrib/loaders/tps.py b/cmscontrib/loaders/tps.py index 1eb2e68a87..cec588012a 100644 --- a/cmscontrib/loaders/tps.py +++ b/cmscontrib/loaders/tps.py @@ -205,6 +205,7 @@ def get_task(self, get_statement=True): args['min_submission_interval'] = make_timedelta(60) args['min_user_test_interval'] = make_timedelta(60) + args['restricted_time'] = make_timedelta(-60*15) task = Task(**args) diff --git a/cmscontrib/updaters/update_45.sql b/cmscontrib/updaters/update_45.sql new file mode 100644 index 0000000000..5257063876 --- /dev/null +++ b/cmscontrib/updaters/update_45.sql @@ -0,0 +1,5 @@ +begin; + +alter table contests add restricted_time varchar; + +rollback; -- change this to: commit; diff --git a/cmstestsuite/unit_tests/server/contest/phase_management_test.py b/cmstestsuite/unit_tests/server/contest/phase_management_test.py index 34e642e0f6..6e08c6672c 100755 --- a/cmstestsuite/unit_tests/server/contest/phase_management_test.py +++ b/cmstestsuite/unit_tests/server/contest/phase_management_test.py @@ -59,7 +59,7 @@ def parse_timedelta(value): def test(contest_start, contest_stop, analysis_start, analysis_end, - per_user_time, starting_time, delay_time, extra_time, intervals): + per_user_time, starting_time, restricted_time, delay_time, extra_time, intervals): """Helper to test compute_actual_phase. It takes all the parameters accepted by compute_actual_phase (with @@ -87,6 +87,7 @@ def test(contest_start, contest_stop, analysis_start, analysis_end, user; contest is USACO-like if given and traditional if not. starting_time (string|None): when the user started their time frame. + restricted_time (string|None): the time the submission interval-restriction is active delay_time (string): how much the user's start is delayed. extra_time (string): how much extra time is given to the user at the end. @@ -102,6 +103,7 @@ def test(contest_start, contest_stop, analysis_start, analysis_end, analysis_end = parse_datetime(analysis_end) per_user_time = parse_timedelta(per_user_time) starting_time = parse_datetime(starting_time) + restricted_time = parse_timedelta(restricted_time) delay_time = parse_timedelta(delay_time) extra_time = parse_timedelta(extra_time) @@ -127,7 +129,7 @@ def test(contest_start, contest_stop, analysis_start, analysis_end, res = compute_actual_phase( end - step, contest_start, contest_stop, analysis_start, analysis_end, - per_user_time, starting_time, delay_time, extra_time) + per_user_time, starting_time, restricted_time, delay_time, extra_time) assert res == (status, begin, end, valid_begin, valid_end), \ "Check on %s returned %s instead of %s" % ( end - step, res, (status, begin, end, @@ -137,7 +139,7 @@ def test(contest_start, contest_stop, analysis_start, analysis_end, res = compute_actual_phase( begin + step, contest_start, contest_stop, analysis_start, analysis_end, - per_user_time, starting_time, delay_time, extra_time) + per_user_time, starting_time, restricted_time, delay_time, extra_time) assert res == (status, begin, end, valid_begin, valid_end), \ "Check on %s returned %s instead of %s" % ( begin + step, res, (status, begin, end, @@ -149,7 +151,7 @@ def test(contest_start, contest_stop, analysis_start, analysis_end, res = compute_actual_phase( begin + step, contest_start, contest_stop, analysis_start, analysis_end, - per_user_time, starting_time, delay_time, extra_time) + per_user_time, starting_time, restricted_time, delay_time, extra_time) assert res == (status, begin, end, valid_begin, valid_end), \ "Check on %s returned %s instead of %s" % ( begin + step, res, (status, begin, end, @@ -157,7 +159,7 @@ def test(contest_start, contest_stop, analysis_start, analysis_end, res = compute_actual_phase( end - step, contest_start, contest_stop, analysis_start, analysis_end, - per_user_time, starting_time, delay_time, extra_time) + per_user_time, starting_time, restricted_time, delay_time, extra_time) assert res == (status, begin, end, valid_begin, valid_end), \ "Check on %s returned %s instead of %s" % ( end - step, res, (status, begin, end, @@ -170,66 +172,88 @@ class TestComputeActualPhase(unittest.TestCase): def test_traditional(): # Test "traditional" contests. There's not much variability, so # we just test different delay_time/extra_time combinations. - test("4", "12", None, None, None, None, "0", "0", + test("4", "12", None, None, None, None, None, "0", "0", ("4", 0, "12")) - test("4", "12", None, None, None, None, "0", "2", + test("4", "12", None, None, None, None, None, "0", "2", ("4", 0, "14")) - test("4", "12", None, None, None, None, "2", "0", + test("4", "12", None, None, None, None, None, "2", "0", ("4", -1, "6", 0, "14")) - test("4", "12", None, None, None, None, "2", "2", + test("4", "12", None, None, None, None, None, "2", "2", ("4", -1, "6", 0, "16")) - test("4", "8", None, None, None, None, "5", "0", + test("4", "8", None, None, None, None, None, "5", "0", ("4", -1, "9", 0, "13")) # Almost identical, with starting_time set to make sure it # doesn't affect anything. - test("4", "12", None, None, None, "7", "0", "0", + test("4", "12", None, None, None, "7", None, "0", "0", ("4", 0, "12")) - test("4", "12", None, None, None, "7", "0", "2", + test("4", "12", None, None, None, "7", None, "0", "2", ("4", 0, "14")) - test("4", "12", None, None, None, "7", "2", "0", + test("4", "12", None, None, None, "7", None, "2", "0", ("4", -1, "6", 0, "14")) - test("4", "12", None, None, None, "7", "2", "2", + test("4", "12", None, None, None, "7", None, "2", "2", ("4", -1, "6", 0, "16")) - test("4", "8", None, None, None, "7", "5", "0", + test("4", "8", None, None, None, "7", None, "5", "0", ("4", -1, "9", 0, "13")) + # Almost identical, with restricted_time to test it + test("4", "12", None, None, None, "7", "6", "0", "0", + ("4", 0, "10", .5, "12")) + test("4", "12", None, None, None, "7", "6", "0", "2", + ("4", 0, "10", .5, "14")) + test("4", "12", None, None, None, "7", "6", "2", "0", + ("4", -1, "6", 0 ,"12", .5, "14")) + test("4", "12", None, None, None, "7", "6", "2", "2", + ("4", -1, "6", 0, "12", .5, "16")) + test("4", "8", None, None, None, "7", "6", "5", "0", + ("4", -1, "9", 0, "15", .5, "13")) + test("4", "12", None, None, None, "7", "-2", "0", "0", + ("4", 0, "10", .5, "12")) + test("4", "12", None, None, None, "7", "-4", "0", "2", + ("4", 0, "10", .5, "14")) + test("4", "12", None, None, None, "7", "-2", "2", "0", + ("4", -1, "6", 0 ,"12", .5, "14")) + test("4", "12", None, None, None, "7", "-4", "2", "2", + ("4", -1, "6", 0, "12", .5, "16")) + test("4", "8", None, None, None, "7", "-2", "5", "0", + ("4", -1, "9", 0, "15", .5, "13")) + # Test analysis mode. Almost identical to above - test("4", "12", "17", "20", None, None, "0", "0", + test("4", "12", "17", "20", None, None, None, "0", "0", ("4", 0, "12", 2, "17", 3, "20")) - test("4", "12", "17", "20", None, None, "0", "2", + test("4", "12", "17", "20", None, None, None, "0", "2", ("4", 0, "14", 2, "17", 3, "20")) - test("4", "12", "17", "20", None, None, "2", "0", + test("4", "12", "17", "20", None, None, None, "2", "0", ("4", -1, "6", 0, "14", 2, "17", 3, "20")) - test("4", "12", "17", "20", None, None, "2", "2", + test("4", "12", "17", "20", None, None, None, "2", "2", ("4", -1, "6", 0, "16", 2, "17", 3, "20")) - test("4", "8", "17", "20", None, None, "5", "0", + test("4", "8", "17", "20", None, None, None, "5", "0", ("4", -1, "9", 0, "13", 2, "17", 3, "20")) - test("4", "12", "17", "20", None, "7", "0", "0", + test("4", "12", "17", "20", None, "7", None, "0", "0", ("4", 0, "12", 2, "17", 3, "20")) - test("4", "12", "17", "20", None, "7", "0", "2", + test("4", "12", "17", "20", None, "7", None, "0", "2", ("4", 0, "14", 2, "17", 3, "20")) - test("4", "12", "17", "20", None, "7", "2", "0", + test("4", "12", "17", "20", None, "7", None, "2", "0", ("4", -1, "6", 0, "14", 2, "17", 3, "20")) - test("4", "12", "17", "20", None, "7", "2", "2", + test("4", "12", "17", "20", None, "7", None, "2", "2", ("4", -1, "6", 0, "16", 2, "17", 3, "20")) - test("4", "8", "17", "20", None, "7", "5", "0", + test("4", "8", "17", "20", None, "7", None, "5", "0", ("4", -1, "9", 0, "13", 2, "17", 3, "20")) # Test for overlapping of contest and analysis for this user - test("4", "12", "12", "20", None, None, "2", "0", + test("4", "12", "12", "20", None, None, None, "2", "0", ("4", -1, "6", 0, "14", 3, "20")) - test("4", "12", "12", "20", None, None, "0", "2", + test("4", "12", "12", "20", None, None, None, "0", "2", ("4", 0, "14", 3, "20")) - test("4", "12", "12", "20", None, None, "1", "1", + test("4", "12", "12", "20", None, None, None, "1", "1", ("4", -1, "5", 0, "14", 3, "20")) - test("4", "8", "8", "12", None, None, "0", "5", + test("4", "8", "8", "12", None, None, None, "0", "5", ("4", 0, "13")) - test("4", "8", "8", "12", None, None, "5", "0", + test("4", "8", "8", "12", None, None, None, "5", "0", ("4", -1, "9", 0, "13")) - test("4", "8", "8", "12", None, None, "9", "0", + test("4", "8", "8", "12", None, None, None, "9", "0", ("4", -1, "13", 0, "17")) - test("4", "8", "8", "16", None, None, "5", "1", + test("4", "8", "8", "16", None, None, None, "5", "1", ("4", -1, "9", 0, "14", 3, "16")) @staticmethod @@ -239,109 +263,109 @@ def test_usaco_like(): # contest time as well as cases where it oversteps the end and # (absurdly) the start of the contest. Also try different # delay_time/extra_time combinations. - test("6", "18", None, None, "6", "9", "0", "0", + test("6", "18", None, None, "6", "9", None, "0", "0", ("6", -1, "9", 0, "15", +1, "18")) - test("6", "18", None, None, "6", "3", "0", "0", + test("6", "18", None, None, "6", "3", None, "0", "0", ("6", 0, "9", +1, "18")) - test("6", "18", None, None, "6", "15", "0", "0", + test("6", "18", None, None, "6", "15", None, "0", "0", ("6", -1, "15", 0, "18")) - test("6", "18", None, None, "6", "9", "0", "1", + test("6", "18", None, None, "6", "9", None, "0", "1", ("6", -1, "9", 0, "16", +1, "18")) - test("6", "18", None, None, "6", "3", "0", "1", + test("6", "18", None, None, "6", "3", None, "0", "1", ("6", 0, "10", +1, "18")) - test("6", "18", None, None, "6", "15", "0", "1", + test("6", "18", None, None, "6", "15", None, "0", "1", ("6", -1, "15", 0, "19")) - test("6", "18", None, None, "6", "9", "1", "0", + test("6", "18", None, None, "6", "9", None, "1", "0", ("6", -1, "10", 0, "16", +1, "18")) - test("6", "18", None, None, "6", "3", "1", "0", + test("6", "18", None, None, "6", "3", None, "1", "0", ("6", -1, "7", 0, "10", +1, "18")) - test("6", "18", None, None, "6", "15", "1", "0", + test("6", "18", None, None, "6", "15", None, "1", "0", ("6", -1, "16", 0, "19")) - test("6", "18", None, None, "6", "9", "1", "1", + test("6", "18", None, None, "6", "9", None, "1", "1", ("6", -1, "10", 0, "17", +1, "18")) - test("6", "18", None, None, "6", "3", "1", "1", + test("6", "18", None, None, "6", "3", None, "1", "1", ("6", -1, "7", 0, "11", +1, "18")) - test("6", "18", None, None, "6", "15", "1", "1", + test("6", "18", None, None, "6", "15", None, "1", "1", ("6", -1, "16", 0, "20")) # Test "USACO-like" contests, with unknown starting_time. Just # make sure delay_time/extra_time don't affect anything. - test("6", "18", None, None, "6", None, "0", "0", + test("6", "18", None, None, "6", None, None, "0", "0", ("6", -1, "18")) - test("6", "18", None, None, "6", None, "0", "1", + test("6", "18", None, None, "6", None, None, "0", "1", ("6", -1, "18")) - test("6", "18", None, None, "6", None, "1", "0", + test("6", "18", None, None, "6", None, None, "1", "0", ("6", -1, "18")) - test("6", "18", None, None, "6", None, "1", "1", + test("6", "18", None, None, "6", None, None, "1", "1", ("6", -1, "18")) # Test ridiculous corner cases. - test("6", "18", None, None, "3", "2", "0", "0", + test("6", "18", None, None, "3", "2", None, "0", "0", ("6", 0, "6", +1, "18")) - test("6", "18", None, None, "3", "2", "0", "1", + test("6", "18", None, None, "3", "2", None, "0", "1", ("6", 0, "7", +1, "18")) - test("6", "18", None, None, "3", "2", "1", "0", + test("6", "18", None, None, "3", "2", None, "1", "0", ("6", -1, "7", 0, "7", +1, "18")) - test("6", "18", None, None, "3", "2", "1", "1", + test("6", "18", None, None, "3", "2", None, "1", "1", ("6", -1, "7", 0, "8", +1, "18")) - test("6", "18", None, None, "3", "19", "0", "0", + test("6", "18", None, None, "3", "19", None, "0", "0", ("6", -1, "18", 0, "18")) - test("6", "18", None, None, "3", "19", "0", "1", + test("6", "18", None, None, "3", "19", None, "0", "1", ("6", -1, "18", 0, "19")) # These are plainly absurd. - test("6", "18", None, None, "3", "19", "1", "0", + test("6", "18", None, None, "3", "19", None, "1", "0", ("6", -1, "19", 0, "19")) - test("6", "18", None, None, "3", "19", "1", "1", + test("6", "18", None, None, "3", "19", None, "1", "1", ("6", -1, "19", 0, "20")) # Identical to above. just to check analysis mode. - test("6", "18", "21", "23", "6", "9", "0", "0", + test("6", "18", "21", "23", "6", "9", None, "0", "0", ("6", -1, "9", 0, "15", +1, "18", 2, "21", 3, "23")) - test("6", "18", "21", "23", "6", "3", "0", "0", + test("6", "18", "21", "23", "6", "3", None, "0", "0", ("6", 0, "9", +1, "18", 2, "21", 3, "23")) - test("6", "18", "21", "23", "6", "15", "0", "0", + test("6", "18", "21", "23", "6", "15", None, "0", "0", ("6", -1, "15", 0, "18", 2, "21", 3, "23")) - test("6", "18", "21", "23", "6", "9", "0", "1", + test("6", "18", "21", "23", "6", "9", None, "0", "1", ("6", -1, "9", 0, "16", +1, "18", 2, "21", 3, "23")) - test("6", "18", "21", "23", "6", "3", "0", "1", + test("6", "18", "21", "23", "6", "3", None, "0", "1", ("6", 0, "10", +1, "18", 2, "21", 3, "23")) - test("6", "18", "21", "23", "6", "15", "0", "1", + test("6", "18", "21", "23", "6", "15", None, "0", "1", ("6", -1, "15", 0, "19", 2, "21", 3, "23")) - test("6", "18", "21", "23", "6", "9", "1", "0", + test("6", "18", "21", "23", "6", "9", None, "1", "0", ("6", -1, "10", 0, "16", +1, "18", 2, "21", 3, "23")) - test("6", "18", "21", "23", "6", "3", "1", "0", + test("6", "18", "21", "23", "6", "3", None, "1", "0", ("6", -1, "7", 0, "10", +1, "18", 2, "21", 3, "23")) - test("6", "18", "21", "23", "6", "15", "1", "0", + test("6", "18", "21", "23", "6", "15", None, "1", "0", ("6", -1, "16", 0, "19", 2, "21", 3, "23")) - test("6", "18", "21", "23", "6", "9", "1", "1", + test("6", "18", "21", "23", "6", "9", None, "1", "1", ("6", -1, "10", 0, "17", +1, "18", 2, "21", 3, "23")) - test("6", "18", "21", "23", "6", "3", "1", "1", + test("6", "18", "21", "23", "6", "3", None, "1", "1", ("6", -1, "7", 0, "11", +1, "18", 2, "21", 3, "23")) - test("6", "18", "21", "23", "6", "15", "1", "1", + test("6", "18", "21", "23", "6", "15", None, "1", "1", ("6", -1, "16", 0, "20", 2, "21", 3, "23")) - test("6", "18", "21", "23", "6", None, "0", "0", + test("6", "18", "21", "23", "6", None, None, "0", "0", ("6", -1, "18", 2, "21", 3, "23")) - test("6", "18", "21", "23", "6", None, "0", "1", + test("6", "18", "21", "23", "6", None, None, "0", "1", ("6", -1, "18", 2, "21", 3, "23")) - test("6", "18", "21", "23", "6", None, "1", "0", + test("6", "18", "21", "23", "6", None, None, "1", "0", ("6", -1, "18", 2, "21", 3, "23")) - test("6", "18", "21", "23", "6", None, "1", "1", + test("6", "18", "21", "23", "6", None, None, "1", "1", ("6", -1, "18", 2, "21", 3, "23")) - test("6", "18", "21", "23", "3", "2", "0", "0", + test("6", "18", "21", "23", "3", "2", None, "0", "0", ("6", 0, "6", +1, "18", 2, "21", 3, "23")) - test("6", "18", "21", "23", "3", "2", "0", "1", + test("6", "18", "21", "23", "3", "2", None, "0", "1", ("6", 0, "7", +1, "18", 2, "21", 3, "23")) - test("6", "18", "21", "23", "3", "2", "1", "0", + test("6", "18", "21", "23", "3", "2", None, "1", "0", ("6", -1, "7", 0, "7", +1, "18", 2, "21", 3, "23")) - test("6", "18", "21", "23", "3", "2", "1", "1", + test("6", "18", "21", "23", "3", "2", None, "1", "1", ("6", -1, "7", 0, "8", +1, "18", 2, "21", 3, "23")) - test("6", "18", "21", "23", "3", "19", "0", "0", + test("6", "18", "21", "23", "3", "19", None, "0", "0", ("6", -1, "18", 0, "18", 2, "21", 3, "23")) - test("6", "18", "21", "23", "3", "19", "0", "1", + test("6", "18", "21", "23", "3", "19", None, "0", "1", ("6", -1, "18", 0, "19", 2, "21", 3, "23")) - test("6", "18", "21", "23", "3", "19", "1", "0", + test("6", "18", "21", "23", "3", "19", None, "1", "0", ("6", -1, "19", 0, "19", 2, "21", 3, "23")) - test("6", "18", "21", "23", "3", "19", "1", "1", + test("6", "18", "21", "23", "3", "19", None, "1", "1", ("6", -1, "19", 0, "20", 2, "21", 3, "23")) diff --git a/docs/Configuring a contest.rst b/docs/Configuring a contest.rst index 6858896992..03f25d39bc 100644 --- a/docs/Configuring a contest.rst +++ b/docs/Configuring a contest.rst @@ -25,6 +25,7 @@ The limits can be set both for individual tasks and for the whole contest. A sub Each of these fields can be left unset to prevent the corresponding limitation from being enforced. +You can set ``restricted_time`` to have the minimum interval restriction on submissions lifted after a certain time. If the field is left unset the restriction is never lifted, if it has a value of zero it is never active. If it is positive the restriction is lifted this much time in seconds after the contest starts, and if it is negative it is lifted this much time in seconds (as an absolute value) before the contest ends.` Feedback to contestants =======================