Add contest freeze

This commit is contained in:
cuom1999 2022-11-18 16:59:58 -06:00
parent 2c39774ff7
commit a35ea0f6d5
13 changed files with 338 additions and 215 deletions

View file

@ -10,7 +10,7 @@ from django.utils.translation import gettext_lazy
from judge.contest_format.default import DefaultContestFormat
from judge.contest_format.registry import register_contest_format
from judge.timezone import from_database_time
from judge.timezone import from_database_time, to_database_time
from judge.utils.timedelta import nice_repr
@ -54,6 +54,10 @@ class AtCoderContestFormat(DefaultContestFormat):
points = 0
format_data = {}
frozen_time = self.contest.end_time
if self.contest.freeze_after:
frozen_time = participation.start + self.contest.freeze_after
with connection.cursor() as cursor:
cursor.execute(
"""
@ -66,9 +70,10 @@ class AtCoderContestFormat(DefaultContestFormat):
FROM judge_contestproblem cp INNER JOIN
judge_contestsubmission cs ON (cs.problem_id = cp.id AND cs.participation_id = %s) LEFT OUTER JOIN
judge_submission sub ON (sub.id = cs.submission_id)
WHERE sub.date < %s
GROUP BY cp.id
""",
(participation.id, participation.id),
(participation.id, participation.id, to_database_time(frozen_time)),
)
for score, time, prob in cursor.fetchall():
@ -100,6 +105,7 @@ class AtCoderContestFormat(DefaultContestFormat):
format_data[str(prob)] = {"time": dt, "points": score, "penalty": prev}
points += score
self.handle_frozen_state(participation, format_data)
participation.cumtime = cumtime + penalty
participation.score = points
participation.tiebreaker = 0
@ -114,7 +120,7 @@ class AtCoderContestFormat(DefaultContestFormat):
'<small style="color:red"> ({penalty})</small>',
penalty=floatformat(format_data["penalty"]),
)
if format_data["penalty"]
if format_data.get("penalty")
else ""
)
return format_html(
@ -129,6 +135,7 @@ class AtCoderContestFormat(DefaultContestFormat):
+ self.best_solution_state(
format_data["points"], contest_problem.points
)
+ (" frozen" if format_data.get("frozen") else "")
),
url=reverse(
"contest_user_submissions_ajax",

View file

@ -1,4 +1,5 @@
from abc import ABCMeta, abstractmethod, abstractproperty
from django.db.models import Max
class abstractclassmethod(classmethod):
@ -95,3 +96,17 @@ class BaseContestFormat(metaclass=ABCMeta):
if points == total:
return "full-score"
return "partial-score"
def handle_frozen_state(self, participation, format_data):
if not self.contest.freeze_after:
return
queryset = participation.submissions.values("problem_id").annotate(
time=Max("submission__date")
)
for result in queryset:
problem = str(result["problem_id"])
if format_data.get(problem):
if result["time"] >= self.contest.freeze_after + participation.start:
format_data[problem]["frozen"] = True
else:
format_data[problem] = {"time": 0, "points": 0, "frozen": True}

View file

@ -32,10 +32,19 @@ class DefaultContestFormat(BaseContestFormat):
points = 0
format_data = {}
for result in participation.submissions.values("problem_id").annotate(
queryset = participation.submissions
if self.contest.freeze_after:
queryset = queryset.filter(
submission__date__lt=participation.start + self.contest.freeze_after
)
queryset = queryset.values("problem_id").annotate(
time=Max("submission__date"),
points=Max("points"),
):
)
for result in queryset:
dt = (result["time"] - participation.start).total_seconds()
if result["points"]:
cumtime += dt
@ -45,6 +54,7 @@ class DefaultContestFormat(BaseContestFormat):
}
points += result["points"]
self.handle_frozen_state(participation, format_data)
participation.cumtime = max(cumtime, 0)
participation.score = points
participation.tiebreaker = 0
@ -66,6 +76,7 @@ class DefaultContestFormat(BaseContestFormat):
+ self.best_solution_state(
format_data["points"], contest_problem.points
)
+ (" frozen" if format_data.get("frozen") else "")
),
url=reverse(
"contest_user_submissions_ajax",

View file

@ -10,7 +10,7 @@ from django.utils.translation import gettext_lazy
from judge.contest_format.default import DefaultContestFormat
from judge.contest_format.registry import register_contest_format
from judge.timezone import from_database_time
from judge.timezone import from_database_time, to_database_time
from judge.utils.timedelta import nice_repr
@ -60,6 +60,10 @@ class ECOOContestFormat(DefaultContestFormat):
points = 0
format_data = {}
frozen_time = self.contest.end_time
if self.contest.freeze_after:
frozen_time = participation.start + self.contest.freeze_after
with connection.cursor() as cursor:
cursor.execute(
"""
@ -77,9 +81,15 @@ class ECOOContestFormat(DefaultContestFormat):
FROM judge_contestproblem cp INNER JOIN
judge_contestsubmission cs ON (cs.problem_id = cp.id AND cs.participation_id = %s) LEFT OUTER JOIN
judge_submission sub ON (sub.id = cs.submission_id)
WHERE sub.date < %s
GROUP BY cp.id
""",
(participation.id, participation.id, participation.id),
(
participation.id,
participation.id,
participation.id,
to_database_time(frozen_time),
),
)
for score, time, prob, subs, max_score in cursor.fetchall():
@ -105,6 +115,7 @@ class ECOOContestFormat(DefaultContestFormat):
format_data[str(prob)] = {"time": dt, "points": score, "bonus": bonus}
points += score
self.handle_frozen_state(participation, format_data)
participation.cumtime = cumtime
participation.score = points
participation.tiebreaker = 0
@ -134,6 +145,7 @@ class ECOOContestFormat(DefaultContestFormat):
+ self.best_solution_state(
format_data["points"], contest_problem.points
)
+ (" frozen" if format_data.get("frozen") else "")
),
url=reverse(
"contest_user_submissions_ajax",

View file

@ -10,7 +10,7 @@ from django.utils.translation import gettext_lazy
from judge.contest_format.default import DefaultContestFormat
from judge.contest_format.registry import register_contest_format
from judge.timezone import from_database_time
from judge.timezone import from_database_time, to_database_time
from judge.utils.timedelta import nice_repr
@ -55,6 +55,10 @@ class ICPCContestFormat(DefaultContestFormat):
score = 0
format_data = {}
frozen_time = self.contest.end_time
if self.contest.freeze_after:
frozen_time = participation.start + self.contest.freeze_after
with connection.cursor() as cursor:
cursor.execute(
"""
@ -67,9 +71,10 @@ class ICPCContestFormat(DefaultContestFormat):
FROM judge_contestproblem cp INNER JOIN
judge_contestsubmission cs ON (cs.problem_id = cp.id AND cs.participation_id = %s) LEFT OUTER JOIN
judge_submission sub ON (sub.id = cs.submission_id)
WHERE sub.date < %s
GROUP BY cp.id
""",
(participation.id, participation.id),
(participation.id, participation.id, to_database_time(frozen_time)),
)
for points, time, prob in cursor.fetchall():
@ -102,6 +107,7 @@ class ICPCContestFormat(DefaultContestFormat):
format_data[str(prob)] = {"time": dt, "points": points, "penalty": prev}
score += points
self.handle_frozen_state(participation, format_data)
participation.cumtime = max(0, cumtime + penalty)
participation.score = score
participation.tiebreaker = last # field is sorted from least to greatest
@ -116,7 +122,7 @@ class ICPCContestFormat(DefaultContestFormat):
'<small style="color:red"> +{penalty}</small>',
penalty=floatformat(format_data["penalty"]),
)
if format_data["penalty"]
if format_data.get("penalty")
else ""
)
return format_html(
@ -131,6 +137,7 @@ class ICPCContestFormat(DefaultContestFormat):
+ self.best_solution_state(
format_data["points"], contest_problem.points
)
+ (" frozen" if format_data.get("frozen") else "")
),
url=reverse(
"contest_user_submissions_ajax",

View file

@ -12,6 +12,7 @@ from judge.contest_format.default import DefaultContestFormat
from judge.contest_format.registry import register_contest_format
from judge.timezone import from_database_time
from judge.utils.timedelta import nice_repr
from django.db.models import Min, OuterRef, Subquery
@register_contest_format("ioi")
@ -45,41 +46,42 @@ class IOIContestFormat(DefaultContestFormat):
def update_participation(self, participation):
cumtime = 0
points = 0
score = 0
format_data = {}
with connection.cursor() as cursor:
cursor.execute(
"""
SELECT MAX(cs.points) as `score`, (
SELECT MIN(csub.date)
FROM judge_contestsubmission ccs LEFT OUTER JOIN
judge_submission csub ON (csub.id = ccs.submission_id)
WHERE ccs.problem_id = cp.id AND ccs.participation_id = %s AND ccs.points = MAX(cs.points)
) AS `time`, cp.id AS `prob`
FROM judge_contestproblem cp INNER JOIN
judge_contestsubmission cs ON (cs.problem_id = cp.id AND cs.participation_id = %s) LEFT OUTER JOIN
judge_submission sub ON (sub.id = cs.submission_id)
GROUP BY cp.id
""",
(participation.id, participation.id),
queryset = participation.submissions
if self.contest.freeze_after:
queryset = queryset.filter(
submission__date__lt=participation.start + self.contest.freeze_after
)
for score, time, prob in cursor.fetchall():
if self.config["cumtime"]:
dt = (
from_database_time(time) - participation.start
).total_seconds()
if score:
cumtime += dt
else:
dt = 0
queryset = (
queryset.values("problem_id")
.filter(
points=Subquery(
queryset.filter(problem_id=OuterRef("problem_id"))
.order_by("-points")
.values("points")[:1]
)
)
.annotate(time=Min("submission__date"))
.values_list("problem_id", "time", "points")
)
format_data[str(prob)] = {"time": dt, "points": score}
points += score
for problem_id, time, points in queryset:
if self.config["cumtime"]:
dt = (time - participation.start).total_seconds()
if points:
cumtime += dt
else:
dt = 0
format_data[str(problem_id)] = {"points": points, "time": dt}
score += points
self.handle_frozen_state(participation, format_data)
participation.cumtime = max(cumtime, 0)
participation.score = points
participation.score = score
participation.tiebreaker = 0
participation.format_data = format_data
participation.save()
@ -99,6 +101,7 @@ class IOIContestFormat(DefaultContestFormat):
+ self.best_solution_state(
format_data["points"], contest_problem.points
)
+ (" frozen" if format_data.get("frozen") else "")
),
url=reverse(
"contest_user_submissions_ajax",