Add new IOI format
This commit is contained in:
parent
af8ab310ce
commit
dbb189444d
13 changed files with 327 additions and 52 deletions
|
@ -3,4 +3,5 @@ from judge.contest_format.default import DefaultContestFormat
|
|||
from judge.contest_format.ecoo import ECOOContestFormat
|
||||
from judge.contest_format.icpc import ICPCContestFormat
|
||||
from judge.contest_format.ioi import IOIContestFormat
|
||||
from judge.contest_format.new_ioi import NewIOIContestFormat
|
||||
from judge.contest_format.registry import choices, formats
|
||||
|
|
|
@ -107,7 +107,7 @@ class AtCoderContestFormat(DefaultContestFormat):
|
|||
|
||||
self.handle_frozen_state(participation, format_data)
|
||||
participation.cumtime = cumtime + penalty
|
||||
participation.score = points
|
||||
participation.score = round(points, self.contest.points_precision)
|
||||
participation.tiebreaker = 0
|
||||
participation.format_data = format_data
|
||||
participation.save()
|
||||
|
@ -141,7 +141,7 @@ class AtCoderContestFormat(DefaultContestFormat):
|
|||
"contest_user_submissions_ajax",
|
||||
args=[
|
||||
self.contest.key,
|
||||
participation.user.user.username,
|
||||
participation.id,
|
||||
contest_problem.problem.code,
|
||||
],
|
||||
),
|
||||
|
|
|
@ -56,7 +56,7 @@ class DefaultContestFormat(BaseContestFormat):
|
|||
|
||||
self.handle_frozen_state(participation, format_data)
|
||||
participation.cumtime = max(cumtime, 0)
|
||||
participation.score = points
|
||||
participation.score = round(points, self.contest.points_precision)
|
||||
participation.tiebreaker = 0
|
||||
participation.format_data = format_data
|
||||
participation.save()
|
||||
|
@ -82,7 +82,7 @@ class DefaultContestFormat(BaseContestFormat):
|
|||
"contest_user_submissions_ajax",
|
||||
args=[
|
||||
self.contest.key,
|
||||
participation.user.user.username,
|
||||
participation.id,
|
||||
contest_problem.problem.code,
|
||||
],
|
||||
),
|
||||
|
|
|
@ -117,7 +117,7 @@ class ECOOContestFormat(DefaultContestFormat):
|
|||
|
||||
self.handle_frozen_state(participation, format_data)
|
||||
participation.cumtime = cumtime
|
||||
participation.score = points
|
||||
participation.score = round(points, self.contest.points_precision)
|
||||
participation.tiebreaker = 0
|
||||
participation.format_data = format_data
|
||||
participation.save()
|
||||
|
@ -151,7 +151,7 @@ class ECOOContestFormat(DefaultContestFormat):
|
|||
"contest_user_submissions_ajax",
|
||||
args=[
|
||||
self.contest.key,
|
||||
participation.user.user.username,
|
||||
participation.id,
|
||||
contest_problem.problem.code,
|
||||
],
|
||||
),
|
||||
|
|
|
@ -109,7 +109,7 @@ class ICPCContestFormat(DefaultContestFormat):
|
|||
|
||||
self.handle_frozen_state(participation, format_data)
|
||||
participation.cumtime = max(0, cumtime + penalty)
|
||||
participation.score = score
|
||||
participation.score = round(score, self.contest.points_precision)
|
||||
participation.tiebreaker = last # field is sorted from least to greatest
|
||||
participation.format_data = format_data
|
||||
participation.save()
|
||||
|
@ -143,7 +143,7 @@ class ICPCContestFormat(DefaultContestFormat):
|
|||
"contest_user_submissions_ajax",
|
||||
args=[
|
||||
self.contest.key,
|
||||
participation.user.user.username,
|
||||
participation.id,
|
||||
contest_problem.problem.code,
|
||||
],
|
||||
),
|
||||
|
|
|
@ -81,7 +81,7 @@ class IOIContestFormat(DefaultContestFormat):
|
|||
|
||||
self.handle_frozen_state(participation, format_data)
|
||||
participation.cumtime = max(cumtime, 0)
|
||||
participation.score = score
|
||||
participation.score = round(score, self.contest.points_precision)
|
||||
participation.tiebreaker = 0
|
||||
participation.format_data = format_data
|
||||
participation.save()
|
||||
|
@ -107,7 +107,7 @@ class IOIContestFormat(DefaultContestFormat):
|
|||
"contest_user_submissions_ajax",
|
||||
args=[
|
||||
self.contest.key,
|
||||
participation.user.user.username,
|
||||
participation.id,
|
||||
contest_problem.problem.code,
|
||||
],
|
||||
),
|
||||
|
|
127
judge/contest_format/new_ioi.py
Normal file
127
judge/contest_format/new_ioi.py
Normal file
|
@ -0,0 +1,127 @@
|
|||
from django.db import connection
|
||||
from django.utils.translation import gettext as _, gettext_lazy
|
||||
|
||||
from judge.contest_format.ioi import IOIContestFormat
|
||||
from judge.contest_format.registry import register_contest_format
|
||||
from judge.timezone import from_database_time, to_database_time
|
||||
|
||||
|
||||
@register_contest_format("ioi16")
|
||||
class NewIOIContestFormat(IOIContestFormat):
|
||||
name = gettext_lazy("New IOI")
|
||||
config_defaults = {"cumtime": False}
|
||||
"""
|
||||
cumtime: Specify True if time penalties are to be computed. Defaults to False.
|
||||
"""
|
||||
|
||||
def get_results_by_subtask(self, participation, include_frozen=False):
|
||||
frozen_time = self.contest.end_time
|
||||
if self.contest.freeze_after and not include_frozen:
|
||||
frozen_time = participation.start + self.contest.freeze_after
|
||||
|
||||
with connection.cursor() as cursor:
|
||||
cursor.execute(
|
||||
"""
|
||||
SELECT q.prob,
|
||||
q.prob_points,
|
||||
MIN(q.date) as `date`,
|
||||
q.batch_points,
|
||||
q.total_batch_points,
|
||||
q.batch,
|
||||
q.subid
|
||||
FROM (
|
||||
SELECT cp.id as `prob`,
|
||||
cp.points as `prob_points`,
|
||||
sub.id as `subid`,
|
||||
sub.date as `date`,
|
||||
tc.points as `points`,
|
||||
tc.batch as `batch`,
|
||||
SUM(tc.points) as `batch_points`,
|
||||
SUM(tc.total) as `total_batch_points`
|
||||
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 AND sub.status = 'D')
|
||||
INNER JOIN judge_submissiontestcase tc
|
||||
ON sub.id = tc.submission_id
|
||||
WHERE sub.date < %s
|
||||
GROUP BY cp.id, tc.batch, sub.id
|
||||
) q
|
||||
INNER JOIN (
|
||||
SELECT prob, batch, MAX(r.batch_points) as max_batch_points
|
||||
FROM (
|
||||
SELECT cp.id as `prob`,
|
||||
tc.batch as `batch`,
|
||||
SUM(tc.points) as `batch_points`
|
||||
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 AND sub.status = 'D')
|
||||
INNER JOIN judge_submissiontestcase tc
|
||||
ON sub.id = tc.submission_id
|
||||
GROUP BY cp.id, tc.batch, sub.id
|
||||
) r
|
||||
GROUP BY prob, batch
|
||||
) p
|
||||
ON p.prob = q.prob AND (p.batch = q.batch OR p.batch is NULL AND q.batch is NULL)
|
||||
WHERE p.max_batch_points = q.batch_points
|
||||
GROUP BY q.prob, q.batch
|
||||
""",
|
||||
(participation.id, to_database_time(frozen_time), participation.id),
|
||||
)
|
||||
|
||||
return cursor.fetchall()
|
||||
|
||||
def update_participation(self, participation):
|
||||
cumtime = 0
|
||||
score = 0
|
||||
format_data = {}
|
||||
|
||||
for (
|
||||
problem_id,
|
||||
problem_points,
|
||||
time,
|
||||
subtask_points,
|
||||
total_subtask_points,
|
||||
subtask,
|
||||
sub_id,
|
||||
) in self.get_results_by_subtask(participation):
|
||||
problem_id = str(problem_id)
|
||||
time = from_database_time(time)
|
||||
if self.config["cumtime"]:
|
||||
dt = (time - participation.start).total_seconds()
|
||||
else:
|
||||
dt = 0
|
||||
|
||||
if format_data.get(problem_id) is None:
|
||||
format_data[problem_id] = {"points": 0, "time": 0, "total_points": 0}
|
||||
format_data[problem_id]["points"] += subtask_points
|
||||
format_data[problem_id]["time"] = max(dt, format_data[problem_id]["time"])
|
||||
format_data[problem_id]["problem_points"] = problem_points
|
||||
format_data[problem_id]["total_points"] += total_subtask_points
|
||||
|
||||
for problem_data in format_data.values():
|
||||
if not problem_data["total_points"]:
|
||||
continue
|
||||
penalty = problem_data["time"]
|
||||
problem_data["points"] = (
|
||||
problem_data["points"]
|
||||
/ problem_data["total_points"]
|
||||
* problem_data["problem_points"]
|
||||
)
|
||||
if self.config["cumtime"] and points:
|
||||
cumtime += penalty
|
||||
score += problem_data["points"]
|
||||
|
||||
self.handle_frozen_state(participation, format_data)
|
||||
participation.cumtime = max(cumtime, 0)
|
||||
participation.score = round(score, self.contest.points_precision)
|
||||
participation.tiebreaker = 0
|
||||
participation.format_data = format_data
|
||||
participation.save()
|
31
judge/migrations/0140_alter_contest_format_name.py
Normal file
31
judge/migrations/0140_alter_contest_format_name.py
Normal file
|
@ -0,0 +1,31 @@
|
|||
# Generated by Django 3.2.16 on 2022-11-21 22:30
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("judge", "0139_contest_freeze_after"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="contest",
|
||||
name="format_name",
|
||||
field=models.CharField(
|
||||
choices=[
|
||||
("atcoder", "AtCoder"),
|
||||
("default", "Default"),
|
||||
("ecoo", "ECOO"),
|
||||
("icpc", "ICPC"),
|
||||
("ioi", "IOI"),
|
||||
("ioi16", "New IOI"),
|
||||
],
|
||||
default="default",
|
||||
help_text="The contest format module to use.",
|
||||
max_length=32,
|
||||
verbose_name="contest format",
|
||||
),
|
||||
),
|
||||
]
|
|
@ -33,7 +33,7 @@ from django.views.generic import ListView
|
|||
|
||||
from judge import event_poster as event
|
||||
from judge.highlight_code import highlight_code
|
||||
from judge.models import Contest
|
||||
from judge.models import Contest, ContestParticipation
|
||||
from judge.models import Language
|
||||
from judge.models import Problem
|
||||
from judge.models import ProblemTestCase
|
||||
|
@ -472,10 +472,17 @@ class SubmissionsListBase(DiggPaginatorMixin, TitleMixin, ListView):
|
|||
|
||||
class UserMixin(object):
|
||||
def get(self, request, *args, **kwargs):
|
||||
if "user" not in kwargs:
|
||||
raise ImproperlyConfigured("Must pass a user")
|
||||
self.profile = get_object_or_404(Profile, user__username=kwargs["user"])
|
||||
self.username = kwargs["user"]
|
||||
if "user" not in kwargs and "participation" not in kwargs:
|
||||
raise ImproperlyConfigured("Must pass a user or participation")
|
||||
if "user" in kwargs:
|
||||
self.profile = get_object_or_404(Profile, user__username=kwargs["user"])
|
||||
self.username = kwargs["user"]
|
||||
else:
|
||||
self.participation = get_object_or_404(
|
||||
ContestParticipation, id=kwargs["participation"]
|
||||
)
|
||||
self.profile = self.participation.user
|
||||
self.username = self.profile.user.username
|
||||
if self.profile == request.profile:
|
||||
self.include_frozen = True
|
||||
return super(UserMixin, self).get(request, *args, **kwargs)
|
||||
|
@ -828,6 +835,53 @@ class UserContestSubmissionsAjax(UserContestSubmissions):
|
|||
return s.date - self.contest.start_time
|
||||
return None
|
||||
|
||||
def get_best_subtask_points(self):
|
||||
if self.contest.format_name == "ioi16":
|
||||
contest_problem = self.contest.contest_problems.get(problem=self.problem)
|
||||
best_subtasks = {}
|
||||
total_points = 0
|
||||
problem_points = 0
|
||||
achieved_points = 0
|
||||
|
||||
for (
|
||||
problem_id,
|
||||
pp,
|
||||
time,
|
||||
subtask_points,
|
||||
total_subtask_points,
|
||||
subtask,
|
||||
sub_id,
|
||||
) in self.contest.format.get_results_by_subtask(
|
||||
self.participation, self.include_frozen
|
||||
):
|
||||
if contest_problem.id != problem_id or total_subtask_points == 0:
|
||||
continue
|
||||
if not subtask:
|
||||
subtask = 0
|
||||
problem_points = pp
|
||||
submission = Submission.objects.get(id=sub_id)
|
||||
best_subtasks[subtask] = {
|
||||
"submission": submission,
|
||||
"contest_time": nice_repr(self.contest_time(submission), "noday"),
|
||||
"points": subtask_points,
|
||||
"total": total_subtask_points,
|
||||
}
|
||||
total_points += total_subtask_points
|
||||
achieved_points += subtask_points
|
||||
for subtask in best_subtasks.values():
|
||||
subtask["points"] = floatformat(
|
||||
subtask["points"] / total_points * problem_points,
|
||||
-self.contest.points_precision,
|
||||
)
|
||||
subtask["total"] = floatformat(
|
||||
subtask["total"] / total_points * problem_points,
|
||||
-self.contest.points_precision,
|
||||
)
|
||||
achieved_points = achieved_points / total_points * problem_points
|
||||
if best_subtasks:
|
||||
return best_subtasks, achieved_points, problem_points
|
||||
return None
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(UserContestSubmissionsAjax, self).get_context_data(**kwargs)
|
||||
context["contest"] = self.contest
|
||||
|
@ -852,6 +906,21 @@ class UserContestSubmissionsAjax(UserContestSubmissions):
|
|||
s.display_point = f"{points} / {total}"
|
||||
filtered_submissions.append(s)
|
||||
context["submissions"] = filtered_submissions
|
||||
|
||||
best_subtasks = self.get_best_subtask_points()
|
||||
if best_subtasks:
|
||||
(
|
||||
context["best_subtasks"],
|
||||
context["points"],
|
||||
context["total"],
|
||||
) = best_subtasks
|
||||
context["points"] = floatformat(
|
||||
context["points"], -self.contest.points_precision
|
||||
)
|
||||
context["total"] = floatformat(
|
||||
context["total"], -self.contest.points_precision
|
||||
)
|
||||
context["subtasks"] = sorted(context["best_subtasks"].keys())
|
||||
return context
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue