From 1e08c7392eac7c3c9f632c7b82e8a6e2fb96b8c0 Mon Sep 17 00:00:00 2001 From: Lorenzo Conti Date: Sat, 4 Feb 2023 15:33:22 +0100 Subject: [PATCH 1/8] Add a phase that removes the interval at the end of a contest --- cms/db/contest.py | 3 + cms/server/contest/handlers/contest.py | 1 + cms/server/contest/phase_management.py | 17 +- cms/server/contest/submission/workflow.py | 12 +- cmscontrib/gerpythonformat/CommonConfig.py | 4 + cmscontrib/gerpythonformat/ContestConfig.py | 6 +- cmscontrib/updaters/update_45.sql | 5 + .../server/contest/phase_management_test.py | 160 +++++++++--------- 8 files changed, 121 insertions(+), 87 deletions(-) create mode 100644 cmscontrib/updaters/update_45.sql diff --git a/cms/db/contest.py b/cms/db/contest.py index c8aba842cc..38d7db0102 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 interval restriction 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/contest/handlers/contest.py b/cms/server/contest/handlers/contest.py index b1211de0dc..64e74041eb 100644 --- a/cms/server/contest/handlers/contest.py +++ b/cms/server/contest/handlers/contest.py @@ -195,6 +195,7 @@ def render_params(self): group.analysis_stop if group.analysis_enabled else None, group.per_user_time, participation.starting_time, + self.contest.restricted_time, participation.delay_time, participation.extra_time) ret["actual_phase"], ret["current_phase_begin"], \ diff --git a/cms/server/contest/phase_management.py b/cms/server/contest/phase_management.py index b7f19f1414..8f068eca2f 100644 --- a/cms/server/contest/phase_management.py +++ b/cms/server/contest/phase_management.py @@ -28,7 +28,7 @@ def compute_actual_phase(timestamp, contest_start, contest_stop, analysis_start, analysis_stop, per_user_time, - starting_time, delay_time, extra_time): + starting_time, restricted_time, delay_time, extra_time): """Determine the current phase and when the active phase is. The "actual phase" of the contest for a certain user is the status @@ -43,7 +43,9 @@ def compute_actual_phase(timestamp, contest_start, contest_stop, already started, its per-user time frame hasn't yet (this usually means the user still has to click on the "start!" button in USACO-like contests); - * 0: the user can compete; + * 0: the user can compete; + * 0.5:the user can still compete and the interval restriction between + submissions is lifted * +1: the user cannot compete because, even if the contest hasn't stopped yet, its per-user time frame already has (again, this should normally happen only in USACO-like contests); @@ -133,9 +135,14 @@ def compute_actual_phase(timestamp, contest_start, contest_stop, assert contest_start <= actual_start <= actual_stop if actual_start <= timestamp <= actual_stop: - actual_phase = 0 - current_phase_begin = actual_start - current_phase_end = actual_stop + if restricted_time is None: + actual_phase = 0 + elif restricted_time > 0: + actual_phase = 0 if actual_start + restricted_time<= timestamp else .5 + else: + actual_phase = 0 if actual_start + restricted_time <= timestamp else .5 + current_phase_begin = actual_start if actual_phase is 0 else actual_start + restricted_time + current_phase_end = actual_stop if actual_phase is .5 or restricted_time is None else actual_start + restricted_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..79c289939b 100644 --- a/cms/server/contest/submission/workflow.py +++ b/cms/server/contest/submission/workflow.py @@ -110,7 +110,17 @@ def accept_submission(sql_session, file_cacher, participation, task, timestamp, "at most %d submissions on this task."), task.max_submission_number) - if not check_min_interval(sql_session, contest.min_submission_interval, + + if compute_actual_phase( + contest.timestamp, group.start, group.stop, + group.analysis_start if group.analysis_enabled + else None, + group.analysis_stop if group.analysis_enabled + else None, + group.per_user_time, participation.starting_time, + contest.restricted_time, + participation.delay_time, participation.extra_time) is not .5 \ + and not check_min_interval(sql_session, contest.min_submission_interval, timestamp, participation, contest=contest): raise UnacceptableSubmission( N_("Submissions too frequent!"), diff --git a/cmscontrib/gerpythonformat/CommonConfig.py b/cmscontrib/gerpythonformat/CommonConfig.py index 76d62629bf..09532b10d4 100644 --- a/cmscontrib/gerpythonformat/CommonConfig.py +++ b/cmscontrib/gerpythonformat/CommonConfig.py @@ -562,6 +562,10 @@ def submission_limits(self, max_number, min_interval): self.max_submission_number = max_number self.min_submission_interval = min_interval + @exported_function + def lift_restrictions_after(self, restricted_time): + self.restricted_time = restricted_time + @exported_function def user_test_limits(self, max_number, min_interval): """ diff --git a/cmscontrib/gerpythonformat/ContestConfig.py b/cmscontrib/gerpythonformat/ContestConfig.py index 331bf470cc..457daf8d00 100644 --- a/cmscontrib/gerpythonformat/ContestConfig.py +++ b/cmscontrib/gerpythonformat/ContestConfig.py @@ -162,7 +162,8 @@ def __init__(self, rules, name, ignore_latex=False, verbose_latex=False, # Default submission limits self.submission_limits(None, None) self.user_test_limits(None, None) - + self.lift_restrictions_after(None) + # a standard tokenwise comparator (specified here so that it has to be # compiled at most once per contest) shutil.copy(os.path.join(self._get_ready_dir(), "tokens.cpp"), @@ -566,7 +567,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/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..f35212fabe 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, @@ -170,66 +172,66 @@ 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")) # 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 +241,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")) From 465de1bb6fa73ddb3b6751f773606e43ae585a07 Mon Sep 17 00:00:00 2001 From: Lorenzo Conti Date: Sat, 4 Feb 2023 16:00:56 +0100 Subject: [PATCH 2/8] Add documentation --- cms/server/contest/submission/workflow.py | 12 ++++++++++-- cmscontrib/gerpythonformat/CommonConfig.py | 10 ++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/cms/server/contest/submission/workflow.py b/cms/server/contest/submission/workflow.py index 79c289939b..0eefaa9301 100644 --- a/cms/server/contest/submission/workflow.py +++ b/cms/server/contest/submission/workflow.py @@ -110,7 +110,6 @@ def accept_submission(sql_session, file_cacher, participation, task, timestamp, "at most %d submissions on this task."), task.max_submission_number) - if compute_actual_phase( contest.timestamp, group.start, group.stop, group.analysis_start if group.analysis_enabled @@ -128,7 +127,16 @@ def accept_submission(sql_session, file_cacher, participation, task, timestamp, "after %d seconds from last submission."), contest.min_submission_interval.total_seconds()) - if not check_min_interval(sql_session, task.min_submission_interval, + if compute_actual_phase( + contest.timestamp, group.start, group.stop, + group.analysis_start if group.analysis_enabled + else None, + group.analysis_stop if group.analysis_enabled + else None, + group.per_user_time, participation.starting_time, + contest.restricted_time, + participation.delay_time, participation.extra_time) is not .5 \ + and not check_min_interval(sql_session, task.min_submission_interval, timestamp, participation, task=task): raise UnacceptableSubmission( N_("Submissions too frequent!"), diff --git a/cmscontrib/gerpythonformat/CommonConfig.py b/cmscontrib/gerpythonformat/CommonConfig.py index 09532b10d4..2496008c15 100644 --- a/cmscontrib/gerpythonformat/CommonConfig.py +++ b/cmscontrib/gerpythonformat/CommonConfig.py @@ -564,6 +564,16 @@ def submission_limits(self, max_number, min_interval): @exported_function def lift_restrictions_after(self, restricted_time): + """ + Set the time before the interval restriction on submissions is lifted + This refers to both restrictions set by contest and task. After lifting of restrictions + a participation is moved to phase 0.5 + + restricted_time (timedelta): if None restrictions are always active + if 0 restrictions are never active + if <0 restrictions are active until end + restricted_time + if >0 restrictions are active until start + restricted_time + """ self.restricted_time = restricted_time @exported_function From 0ec641621c74bdd0ee6cd3f227500cd2c5ca40ec Mon Sep 17 00:00:00 2001 From: Lorenzo Conti Date: Sat, 4 Feb 2023 21:12:34 +0100 Subject: [PATCH 3/8] Fix errors with phase 0.5 --- cms/server/contest/handlers/contest.py | 4 ++-- cms/server/contest/handlers/main.py | 4 ++-- cms/server/contest/handlers/task.py | 6 +++--- cms/server/contest/handlers/tasksubmission.py | 12 ++++++------ cms/server/contest/handlers/taskusertest.py | 12 ++++++------ cms/server/contest/phase_management.py | 17 +++++++++-------- cms/server/contest/templates/contest.html | 4 ++-- .../contest/templates/macro/submission.html | 6 +++--- cms/server/contest/templates/overview.html | 4 ++-- .../contest/templates/task_submissions.html | 6 +++--- 10 files changed, 38 insertions(+), 37 deletions(-) diff --git a/cms/server/contest/handlers/contest.py b/cms/server/contest/handlers/contest.py index 64e74041eb..c77b22e5cc 100644 --- a/cms/server/contest/handlers/contest.py +++ b/cms/server/contest/handlers/contest.py @@ -202,8 +202,8 @@ def render_params(self): ret["current_phase_end"], ret["valid_phase_begin"], \ ret["valid_phase_end"] = res - if ret["actual_phase"] == 0: - ret["phase"] = 0 + if ret["actual_phase"] == 0 or ret["actual_phase"] == .5: + ret["phase"] = ret["actual_phase"] # set the timezone used to format timestamps ret["timezone"] = get_timezone(participation.user, self.contest) diff --git a/cms/server/contest/handlers/main.py b/cms/server/contest/handlers/main.py index 9bd0e87c05..3b335dbf04 100644 --- a/cms/server/contest/handlers/main.py +++ b/cms/server/contest/handlers/main.py @@ -382,7 +382,7 @@ class PrintingHandler(ContestHandler): """ @tornado_web.authenticated - @actual_phase_required(0) + @actual_phase_required(0, 0.5) @multi_contest def get(self): participation = self.current_user @@ -404,7 +404,7 @@ def get(self): **self.r_params) @tornado_web.authenticated - @actual_phase_required(0) + @actual_phase_required(0, 0.5) @multi_contest def post(self): try: diff --git a/cms/server/contest/handlers/task.py b/cms/server/contest/handlers/task.py index e29d307b76..0d1962d207 100644 --- a/cms/server/contest/handlers/task.py +++ b/cms/server/contest/handlers/task.py @@ -49,7 +49,7 @@ class TaskDescriptionHandler(ContestHandler): """ @tornado_web.authenticated - @actual_phase_required(0, 3) + @actual_phase_required(0, 0.5, 3) @multi_contest def get(self, task_name): task = self.get_task(task_name) @@ -64,7 +64,7 @@ class TaskStatementViewHandler(FileHandler): """ @tornado_web.authenticated - @actual_phase_required(0, 3) + @actual_phase_required(0, 0.5, 3) @multi_contest def get(self, task_name, lang_code): task = self.get_task(task_name) @@ -90,7 +90,7 @@ class TaskAttachmentViewHandler(FileHandler): """ @tornado_web.authenticated - @actual_phase_required(0, 3) + @actual_phase_required(0, 0.5, 3) @multi_contest def get(self, task_name, filename): task = self.get_task(task_name) diff --git a/cms/server/contest/handlers/tasksubmission.py b/cms/server/contest/handlers/tasksubmission.py index f5d3586254..bbb15e28e3 100644 --- a/cms/server/contest/handlers/tasksubmission.py +++ b/cms/server/contest/handlers/tasksubmission.py @@ -69,7 +69,7 @@ class SubmitHandler(ContestHandler): """ @tornado_web.authenticated - @actual_phase_required(0, 3) + @actual_phase_required(0, 0.5, 3) @multi_contest def post(self, task_name): task = self.get_task(task_name) @@ -112,7 +112,7 @@ class TaskSubmissionsHandler(ContestHandler): """ @tornado_web.authenticated - @actual_phase_required(0, 3) + @actual_phase_required(0, 0.5, 3) @multi_contest def get(self, task_name): participation = self.current_user @@ -246,7 +246,7 @@ def add_task_score(self, participation, task, data): task.score_precision, translation=self.translation) @tornado_web.authenticated - @actual_phase_required(0, 3) + @actual_phase_required(0, 0.5, 3) @multi_contest def get(self, task_name, submission_num): task = self.get_task(task_name) @@ -311,7 +311,7 @@ class SubmissionDetailsHandler(ContestHandler): refresh_cookie = False @tornado_web.authenticated - @actual_phase_required(0, 3) + @actual_phase_required(0, 0.5, 3) @multi_contest def get(self, task_name, submission_num): task = self.get_task(task_name) @@ -355,7 +355,7 @@ class SubmissionFileHandler(FileHandler): """ @tornado_web.authenticated - @actual_phase_required(0, 3) + @actual_phase_required(0, 0.5, 3) @multi_contest def get(self, task_name, submission_num, filename): if not self.contest.submissions_download_allowed: @@ -401,7 +401,7 @@ class UseTokenHandler(ContestHandler): """ @tornado_web.authenticated - @actual_phase_required(0) + @actual_phase_required(0, 0.5) @multi_contest def post(self, task_name, submission_num): task = self.get_task(task_name) diff --git a/cms/server/contest/handlers/taskusertest.py b/cms/server/contest/handlers/taskusertest.py index c78c87c1c8..a6989ebfc1 100644 --- a/cms/server/contest/handlers/taskusertest.py +++ b/cms/server/contest/handlers/taskusertest.py @@ -61,7 +61,7 @@ class UserTestInterfaceHandler(ContestHandler): """ @tornado_web.authenticated - @actual_phase_required(0) + @actual_phase_required(0, 0.5) @multi_contest def get(self): participation = self.current_user @@ -117,7 +117,7 @@ class UserTestHandler(ContestHandler): refresh_cookie = False @tornado_web.authenticated - @actual_phase_required(0) + @actual_phase_required(0, 0.5) @multi_contest def post(self, task_name): if not self.r_params["testing_enabled"]: @@ -163,7 +163,7 @@ class UserTestStatusHandler(ContestHandler): refresh_cookie = False @tornado_web.authenticated - @actual_phase_required(0) + @actual_phase_required(0, 0.5) @multi_contest def get(self, task_name, user_test_num): if not self.r_params["testing_enabled"]: @@ -218,7 +218,7 @@ class UserTestDetailsHandler(ContestHandler): refresh_cookie = False @tornado_web.authenticated - @actual_phase_required(0) + @actual_phase_required(0, 0.5) @multi_contest def get(self, task_name, user_test_num): if not self.r_params["testing_enabled"]: @@ -243,7 +243,7 @@ class UserTestIOHandler(FileHandler): """ @tornado_web.authenticated - @actual_phase_required(0) + @actual_phase_required(0, 0.5) @multi_contest def get(self, task_name, user_test_num, io): if not self.r_params["testing_enabled"]: @@ -277,7 +277,7 @@ class UserTestFileHandler(FileHandler): """ @tornado_web.authenticated - @actual_phase_required(0) + @actual_phase_required(0, 0.5) @multi_contest def get(self, task_name, user_test_num, filename): if not self.r_params["testing_enabled"]: diff --git a/cms/server/contest/phase_management.py b/cms/server/contest/phase_management.py index 8f068eca2f..093a148c8a 100644 --- a/cms/server/contest/phase_management.py +++ b/cms/server/contest/phase_management.py @@ -135,14 +135,15 @@ def compute_actual_phase(timestamp, contest_start, contest_stop, assert contest_start <= actual_start <= actual_stop if actual_start <= timestamp <= actual_stop: - if restricted_time is None: - actual_phase = 0 - elif restricted_time > 0: - actual_phase = 0 if actual_start + restricted_time<= timestamp else .5 - else: - actual_phase = 0 if actual_start + restricted_time <= timestamp else .5 - current_phase_begin = actual_start if actual_phase is 0 else actual_start + restricted_time - current_phase_end = actual_stop if actual_phase is .5 or restricted_time is None else actual_start + restricted_time + actual_phase = 0 + if restricted_time is not None: + if restricted_time > 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 is 0 else lift_time + current_phase_end = actual_stop if actual_phase is .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/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 %} Date: Sun, 5 Feb 2023 10:37:05 +0100 Subject: [PATCH 4/8] Add field in admin interface to set restricted_time --- cms/server/admin/handlers/contest.py | 1 + cms/server/admin/templates/contest.html | 7 ++ cms/server/contest/handlers/tasksubmission.py | 4 +- cms/server/contest/submission/workflow.py | 69 ++++++++++--------- cmscontrib/gerpythonformat/CommonConfig.py | 14 ---- cmscontrib/gerpythonformat/ContestConfig.py | 14 ++++ cmscontrib/loaders/italy_yaml.py | 1 + cmscontrib/loaders/tps.py | 1 + docs/Configuring a contest.rst | 1 + 9 files changed, 62 insertions(+), 50 deletions(-) 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..b70c863784 100644 --- a/cms/server/admin/templates/contest.html +++ b/cms/server/admin/templates/contest.html @@ -251,6 +251,13 @@

Contest configuration

+ + + +
+ + Restricted lifted after +
0 restrictions are active until start + restricted_time - """ - self.restricted_time = restricted_time - @exported_function def user_test_limits(self, max_number, min_interval): """ diff --git a/cmscontrib/gerpythonformat/ContestConfig.py b/cmscontrib/gerpythonformat/ContestConfig.py index 457daf8d00..820730b431 100644 --- a/cmscontrib/gerpythonformat/ContestConfig.py +++ b/cmscontrib/gerpythonformat/ContestConfig.py @@ -535,6 +535,20 @@ def test_user(self, u): """ self._mytestuser = u + @exported_function + def lift_restrictions_after(self, restricted_time): + """ + Set the time before the interval restriction on submissions is lifted + This refers to both restrictions set by contest and task. After lifting of restrictions + a participation is moved to phase 0.5 + + restricted_time (timedelta): if None restrictions are always active + if 0 restrictions are never active + if <0 restrictions are active until end + restricted_time + if >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 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..0a69a750c4 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/docs/Configuring a contest.rst b/docs/Configuring a contest.rst index 6858896992..751ed9997f 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. +Instead of unsetting them manually, you can also set ``restricted_time`` this lifts the min interval restriction. If the field is empty the restrictions are never lifted, if it is zero they are never active. If the field is positive the restrictions are lifted, that much time after the constests starts and if it is negative they are lifted that much time before/after the contests ends.` Feedback to contestants ======================= From 9c0fef439486a77c74ab3e40805e3ca52e5da9fd Mon Sep 17 00:00:00 2001 From: Lorenzo Conti Date: Sun, 5 Feb 2023 13:49:28 +0100 Subject: [PATCH 5/8] Added tests --- cms/server/contest/phase_management.py | 4 ++-- .../server/contest/phase_management_test.py | 22 +++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/cms/server/contest/phase_management.py b/cms/server/contest/phase_management.py index 093a148c8a..36667e4b2b 100644 --- a/cms/server/contest/phase_management.py +++ b/cms/server/contest/phase_management.py @@ -142,8 +142,8 @@ def compute_actual_phase(timestamp, contest_start, contest_stop, 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 is 0 else lift_time - current_phase_end = actual_stop if actual_phase is .5 or restricted_time is None else lift_time + 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/cmstestsuite/unit_tests/server/contest/phase_management_test.py b/cmstestsuite/unit_tests/server/contest/phase_management_test.py index f35212fabe..7b8ab18a0a 100755 --- a/cmstestsuite/unit_tests/server/contest/phase_management_test.py +++ b/cmstestsuite/unit_tests/server/contest/phase_management_test.py @@ -196,6 +196,28 @@ def test_traditional(): 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, None, "0", "0", ("4", 0, "12", 2, "17", 3, "20")) From a36e3205bb8524d80d97318e97983c12e541ca0c Mon Sep 17 00:00:00 2001 From: Lorax66 <53429318+Lorax66@users.noreply.github.com> Date: Sun, 5 Feb 2023 13:59:23 +0100 Subject: [PATCH 6/8] Apply suggestions from code review Co-authored-by: Manuel Gundlach --- cms/db/contest.py | 2 +- cms/server/admin/templates/contest.html | 6 ++++-- cmscontrib/loaders/tps.py | 2 +- docs/Configuring a contest.rst | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/cms/db/contest.py b/cms/db/contest.py index 38d7db0102..05b59a1b03 100644 --- a/cms/db/contest.py +++ b/cms/db/contest.py @@ -219,7 +219,7 @@ class Contest(Base): CheckConstraint("min_user_test_interval > '0 seconds'"), nullable=True) - # Time after which the interval restriction is lifted + # 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 diff --git a/cms/server/admin/templates/contest.html b/cms/server/admin/templates/contest.html index b70c863784..2383bcbf33 100644 --- a/cms/server/admin/templates/contest.html +++ b/cms/server/admin/templates/contest.html @@ -253,8 +253,10 @@

Contest configuration

- - Restricted lifted after + + Lift minimum submission interval restriction after diff --git a/cmscontrib/loaders/tps.py b/cmscontrib/loaders/tps.py index 0a69a750c4..cec588012a 100644 --- a/cmscontrib/loaders/tps.py +++ b/cmscontrib/loaders/tps.py @@ -205,7 +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) + args['restricted_time'] = make_timedelta(-60*15) task = Task(**args) diff --git a/docs/Configuring a contest.rst b/docs/Configuring a contest.rst index 751ed9997f..03f25d39bc 100644 --- a/docs/Configuring a contest.rst +++ b/docs/Configuring a contest.rst @@ -25,7 +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. -Instead of unsetting them manually, you can also set ``restricted_time`` this lifts the min interval restriction. If the field is empty the restrictions are never lifted, if it is zero they are never active. If the field is positive the restrictions are lifted, that much time after the constests starts and if it is negative they are lifted that much time before/after the contests ends.` +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 ======================= From a52688ad6e4c4d02acb58672b4737f8af0627fc9 Mon Sep 17 00:00:00 2001 From: Lorenzo Conti Date: Sun, 5 Feb 2023 14:14:24 +0100 Subject: [PATCH 7/8] Add missing , in test --- .../unit_tests/server/contest/phase_management_test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmstestsuite/unit_tests/server/contest/phase_management_test.py b/cmstestsuite/unit_tests/server/contest/phase_management_test.py index 7b8ab18a0a..fdcaf92772 100755 --- a/cmstestsuite/unit_tests/server/contest/phase_management_test.py +++ b/cmstestsuite/unit_tests/server/contest/phase_management_test.py @@ -200,7 +200,7 @@ def test_traditional(): 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")) + ("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", @@ -210,7 +210,7 @@ def test_traditional(): 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")) + ("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", From c1fa16eb78ebb6584fc254102b03dbfe2c2914ff Mon Sep 17 00:00:00 2001 From: Lorenzo Conti Date: Sun, 5 Feb 2023 16:13:07 +0100 Subject: [PATCH 8/8] Add restricted_time argument to missing test --- cmstestsuite/unit_tests/server/contest/phase_management_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmstestsuite/unit_tests/server/contest/phase_management_test.py b/cmstestsuite/unit_tests/server/contest/phase_management_test.py index fdcaf92772..6e08c6672c 100755 --- a/cmstestsuite/unit_tests/server/contest/phase_management_test.py +++ b/cmstestsuite/unit_tests/server/contest/phase_management_test.py @@ -159,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,