Add frozen subtasks
This commit is contained in:
parent
bdd30f94b7
commit
6bd5bb290f
13 changed files with 117 additions and 34 deletions
|
@ -1099,7 +1099,7 @@ urlpatterns = [
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
url(r"^resolver/(?P<contest>\w+)",resolver.Resolver.as_view(), name="resolver")
|
url(r"^resolver/(?P<contest>\w+)", resolver.Resolver.as_view(), name="resolver"),
|
||||||
] + url_static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
] + url_static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||||
|
|
||||||
# if hasattr(settings, "INTERNAL_IPS"):
|
# if hasattr(settings, "INTERNAL_IPS"):
|
||||||
|
|
|
@ -3,7 +3,7 @@ from django.contrib import admin
|
||||||
from django.core.exceptions import PermissionDenied
|
from django.core.exceptions import PermissionDenied
|
||||||
from django.db import connection, transaction
|
from django.db import connection, transaction
|
||||||
from django.db.models import Q, TextField
|
from django.db.models import Q, TextField
|
||||||
from django.forms import ModelForm, ModelMultipleChoiceField
|
from django.forms import ModelForm, ModelMultipleChoiceField, TextInput
|
||||||
from django.http import Http404, HttpResponseRedirect
|
from django.http import Http404, HttpResponseRedirect
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
from django.urls import reverse, reverse_lazy
|
from django.urls import reverse, reverse_lazy
|
||||||
|
@ -66,7 +66,13 @@ class ContestTagAdmin(admin.ModelAdmin):
|
||||||
|
|
||||||
class ContestProblemInlineForm(ModelForm):
|
class ContestProblemInlineForm(ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
widgets = {"problem": AdminHeavySelect2Widget(data_view="problem_select2")}
|
widgets = {
|
||||||
|
"problem": AdminHeavySelect2Widget(data_view="problem_select2"),
|
||||||
|
"frozen_subtasks": TextInput(attrs={"size": "3"}),
|
||||||
|
"points": TextInput(attrs={"size": "1"}),
|
||||||
|
"order": TextInput(attrs={"size": "1"}),
|
||||||
|
"output_prefix_override": TextInput(attrs={"size": "1"}),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class ContestProblemInline(admin.TabularInline):
|
class ContestProblemInline(admin.TabularInline):
|
||||||
|
@ -79,6 +85,7 @@ class ContestProblemInline(admin.TabularInline):
|
||||||
"partial",
|
"partial",
|
||||||
"is_pretested",
|
"is_pretested",
|
||||||
"max_submissions",
|
"max_submissions",
|
||||||
|
"frozen_subtasks",
|
||||||
"output_prefix_override",
|
"output_prefix_override",
|
||||||
"order",
|
"order",
|
||||||
"rejudge_column",
|
"rejudge_column",
|
||||||
|
|
|
@ -8,4 +8,3 @@ def finished_submission(sub):
|
||||||
keys += ["contest_complete:%d" % participation.id]
|
keys += ["contest_complete:%d" % participation.id]
|
||||||
keys += ["contest_attempted:%d" % participation.id]
|
keys += ["contest_attempted:%d" % participation.id]
|
||||||
cache.delete_many(keys)
|
cache.delete_many(keys)
|
||||||
|
|
||||||
|
|
|
@ -98,15 +98,22 @@ class BaseContestFormat(metaclass=ABCMeta):
|
||||||
return "partial-score"
|
return "partial-score"
|
||||||
|
|
||||||
def handle_frozen_state(self, participation, format_data):
|
def handle_frozen_state(self, participation, format_data):
|
||||||
if not self.contest.freeze_after:
|
frozen_subtasks = {}
|
||||||
return
|
if hasattr(self, "get_frozen_subtasks"):
|
||||||
|
frozen_subtasks = self.get_frozen_subtasks()
|
||||||
|
|
||||||
queryset = participation.submissions.values("problem_id").annotate(
|
queryset = participation.submissions.values("problem_id").annotate(
|
||||||
time=Max("submission__date")
|
time=Max("submission__date")
|
||||||
)
|
)
|
||||||
for result in queryset:
|
for result in queryset:
|
||||||
problem = str(result["problem_id"])
|
problem = str(result["problem_id"])
|
||||||
if format_data.get(problem):
|
if format_data.get(problem):
|
||||||
if result["time"] >= self.contest.freeze_after + participation.start:
|
is_after_freeze = (
|
||||||
|
self.contest.freeze_after
|
||||||
|
and result["time"]
|
||||||
|
>= self.contest.freeze_after + participation.start
|
||||||
|
)
|
||||||
|
if is_after_freeze or frozen_subtasks.get(problem):
|
||||||
format_data[problem]["frozen"] = True
|
format_data[problem]["frozen"] = True
|
||||||
else:
|
else:
|
||||||
format_data[problem] = {"time": 0, "points": 0, "frozen": True}
|
format_data[problem] = {"time": 0, "points": 0, "frozen": True}
|
||||||
|
|
|
@ -111,7 +111,9 @@ class IOIContestFormat(DefaultContestFormat):
|
||||||
contest_problem.problem.code,
|
contest_problem.problem.code,
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
points=floatformat(format_data["points"]),
|
points=floatformat(
|
||||||
|
format_data["points"], -self.contest.points_precision
|
||||||
|
),
|
||||||
time=nice_repr(timedelta(seconds=format_data["time"]), "noday")
|
time=nice_repr(timedelta(seconds=format_data["time"]), "noday")
|
||||||
if self.config["cumtime"]
|
if self.config["cumtime"]
|
||||||
else "",
|
else "",
|
||||||
|
@ -122,7 +124,7 @@ class IOIContestFormat(DefaultContestFormat):
|
||||||
def display_participation_result(self, participation):
|
def display_participation_result(self, participation):
|
||||||
return format_html(
|
return format_html(
|
||||||
'<td class="user-points">{points}<div class="solving-time">{cumtime}</div></td>',
|
'<td class="user-points">{points}<div class="solving-time">{cumtime}</div></td>',
|
||||||
points=floatformat(participation.score),
|
points=floatformat(participation.score, -self.contest.points_precision),
|
||||||
cumtime=nice_repr(timedelta(seconds=participation.cumtime), "noday")
|
cumtime=nice_repr(timedelta(seconds=participation.cumtime), "noday")
|
||||||
if self.config["cumtime"]
|
if self.config["cumtime"]
|
||||||
else "",
|
else "",
|
||||||
|
|
|
@ -14,6 +14,21 @@ class NewIOIContestFormat(IOIContestFormat):
|
||||||
cumtime: Specify True if time penalties are to be computed. Defaults to False.
|
cumtime: Specify True if time penalties are to be computed. Defaults to False.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
def get_frozen_subtasks(self):
|
||||||
|
queryset = self.contest.contest_problems.values_list("id", "frozen_subtasks")
|
||||||
|
res = {}
|
||||||
|
for problem_id, frozen_subtasks in queryset:
|
||||||
|
subtasks = set()
|
||||||
|
if frozen_subtasks:
|
||||||
|
frozen_subtasks = frozen_subtasks.split(",")
|
||||||
|
for i in frozen_subtasks:
|
||||||
|
try:
|
||||||
|
subtasks.add(int(i))
|
||||||
|
except Exception as e:
|
||||||
|
pass
|
||||||
|
res[str(problem_id)] = subtasks
|
||||||
|
return res
|
||||||
|
|
||||||
def get_results_by_subtask(self, participation, include_frozen=False):
|
def get_results_by_subtask(self, participation, include_frozen=False):
|
||||||
frozen_time = self.contest.end_time
|
frozen_time = self.contest.end_time
|
||||||
if self.contest.freeze_after and not include_frozen:
|
if self.contest.freeze_after and not include_frozen:
|
||||||
|
@ -65,6 +80,7 @@ class NewIOIContestFormat(IOIContestFormat):
|
||||||
ON (sub.id = cs.submission_id AND sub.status = 'D')
|
ON (sub.id = cs.submission_id AND sub.status = 'D')
|
||||||
INNER JOIN judge_submissiontestcase tc
|
INNER JOIN judge_submissiontestcase tc
|
||||||
ON sub.id = tc.submission_id
|
ON sub.id = tc.submission_id
|
||||||
|
WHERE sub.date < %s
|
||||||
GROUP BY cp.id, tc.batch, sub.id
|
GROUP BY cp.id, tc.batch, sub.id
|
||||||
) r
|
) r
|
||||||
GROUP BY prob, batch
|
GROUP BY prob, batch
|
||||||
|
@ -73,7 +89,12 @@ class NewIOIContestFormat(IOIContestFormat):
|
||||||
WHERE p.max_batch_points = q.batch_points
|
WHERE p.max_batch_points = q.batch_points
|
||||||
GROUP BY q.prob, q.batch
|
GROUP BY q.prob, q.batch
|
||||||
""",
|
""",
|
||||||
(participation.id, to_database_time(frozen_time), participation.id),
|
(
|
||||||
|
participation.id,
|
||||||
|
to_database_time(frozen_time),
|
||||||
|
participation.id,
|
||||||
|
to_database_time(frozen_time),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
return cursor.fetchall()
|
return cursor.fetchall()
|
||||||
|
@ -82,6 +103,7 @@ class NewIOIContestFormat(IOIContestFormat):
|
||||||
cumtime = 0
|
cumtime = 0
|
||||||
score = 0
|
score = 0
|
||||||
format_data = {}
|
format_data = {}
|
||||||
|
frozen_subtasks = self.get_frozen_subtasks()
|
||||||
|
|
||||||
for (
|
for (
|
||||||
problem_id,
|
problem_id,
|
||||||
|
@ -101,10 +123,11 @@ class NewIOIContestFormat(IOIContestFormat):
|
||||||
|
|
||||||
if format_data.get(problem_id) is None:
|
if format_data.get(problem_id) is None:
|
||||||
format_data[problem_id] = {"points": 0, "time": 0, "total_points": 0}
|
format_data[problem_id] = {"points": 0, "time": 0, "total_points": 0}
|
||||||
format_data[problem_id]["points"] += subtask_points
|
if subtask not in frozen_subtasks.get(problem_id, set()):
|
||||||
|
format_data[problem_id]["points"] += subtask_points
|
||||||
|
format_data[problem_id]["total_points"] += total_subtask_points
|
||||||
format_data[problem_id]["time"] = max(dt, format_data[problem_id]["time"])
|
format_data[problem_id]["time"] = max(dt, format_data[problem_id]["time"])
|
||||||
format_data[problem_id]["problem_points"] = problem_points
|
format_data[problem_id]["problem_points"] = problem_points
|
||||||
format_data[problem_id]["total_points"] += total_subtask_points
|
|
||||||
|
|
||||||
for problem_data in format_data.values():
|
for problem_data in format_data.values():
|
||||||
if not problem_data["total_points"]:
|
if not problem_data["total_points"]:
|
||||||
|
|
|
@ -81,4 +81,4 @@ class DarkModeMiddleware(object):
|
||||||
return HttpResponseRedirect(
|
return HttpResponseRedirect(
|
||||||
reverse("toggle_darkmode") + "?next=" + urlquote(request.path)
|
reverse("toggle_darkmode") + "?next=" + urlquote(request.path)
|
||||||
)
|
)
|
||||||
return self.get_response(request)
|
return self.get_response(request)
|
||||||
|
|
24
judge/migrations/0141_contestproblem_frozen_subtasks.py
Normal file
24
judge/migrations/0141_contestproblem_frozen_subtasks.py
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
# Generated by Django 3.2.16 on 2022-12-20 06:15
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("judge", "0140_alter_contest_format_name"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="contestproblem",
|
||||||
|
name="frozen_subtasks",
|
||||||
|
field=models.CharField(
|
||||||
|
blank=True,
|
||||||
|
help_text="Only for format new IOI. Separated by commas, e.g: 2, 3",
|
||||||
|
max_length=20,
|
||||||
|
null=True,
|
||||||
|
verbose_name="frozen subtasks",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
|
@ -774,6 +774,13 @@ class ContestProblem(models.Model):
|
||||||
MinValueValidator(0, _("Why include a problem you " "can't submit to?"))
|
MinValueValidator(0, _("Why include a problem you " "can't submit to?"))
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
frozen_subtasks = models.CharField(
|
||||||
|
help_text=_("Only for format new IOI. Separated by commas, e.g: 2, 3"),
|
||||||
|
verbose_name=_("frozen subtasks"),
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
max_length=20,
|
||||||
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def clarifications(self):
|
def clarifications(self):
|
||||||
|
|
|
@ -2,6 +2,7 @@ from django.views.generic import TemplateView
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
from django.http import HttpResponseForbidden
|
from django.http import HttpResponseForbidden
|
||||||
|
|
||||||
|
|
||||||
class Resolver(TemplateView):
|
class Resolver(TemplateView):
|
||||||
title = _("Resolver")
|
title = _("Resolver")
|
||||||
template_name = "resolver/resolver.html"
|
template_name = "resolver/resolver.html"
|
||||||
|
@ -15,5 +16,3 @@ class Resolver(TemplateView):
|
||||||
if request.user.is_superuser:
|
if request.user.is_superuser:
|
||||||
return super(Resolver, self).get(request, *args, **kwargs)
|
return super(Resolver, self).get(request, *args, **kwargs)
|
||||||
return HttpResponseForbidden()
|
return HttpResponseForbidden()
|
||||||
|
|
||||||
|
|
|
@ -842,6 +842,7 @@ class UserContestSubmissionsAjax(UserContestSubmissions):
|
||||||
total_points = 0
|
total_points = 0
|
||||||
problem_points = 0
|
problem_points = 0
|
||||||
achieved_points = 0
|
achieved_points = 0
|
||||||
|
frozen_subtasks = self.contest.format.get_frozen_subtasks()
|
||||||
|
|
||||||
for (
|
for (
|
||||||
problem_id,
|
problem_id,
|
||||||
|
@ -860,25 +861,38 @@ class UserContestSubmissionsAjax(UserContestSubmissions):
|
||||||
subtask = 0
|
subtask = 0
|
||||||
problem_points = pp
|
problem_points = pp
|
||||||
submission = Submission.objects.get(id=sub_id)
|
submission = Submission.objects.get(id=sub_id)
|
||||||
best_subtasks[subtask] = {
|
if subtask in frozen_subtasks.get(str(problem_id), set()):
|
||||||
"submission": submission,
|
best_subtasks[subtask] = {
|
||||||
"contest_time": nice_repr(self.contest_time(submission), "noday"),
|
"submission": submission,
|
||||||
"points": subtask_points,
|
"contest_time": nice_repr(
|
||||||
"total": total_subtask_points,
|
self.contest_time(submission), "noday"
|
||||||
}
|
),
|
||||||
|
"points": "???",
|
||||||
|
"total": total_subtask_points,
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
best_subtasks[subtask] = {
|
||||||
|
"submission": submission,
|
||||||
|
"contest_time": nice_repr(
|
||||||
|
self.contest_time(submission), "noday"
|
||||||
|
),
|
||||||
|
"points": subtask_points,
|
||||||
|
"total": total_subtask_points,
|
||||||
|
}
|
||||||
|
achieved_points += subtask_points
|
||||||
total_points += total_subtask_points
|
total_points += total_subtask_points
|
||||||
achieved_points += subtask_points
|
|
||||||
for subtask in best_subtasks.values():
|
for subtask in best_subtasks.values():
|
||||||
subtask["points"] = floatformat(
|
if subtask["points"] != "???":
|
||||||
subtask["points"] / total_points * problem_points,
|
subtask["points"] = floatformat(
|
||||||
-self.contest.points_precision,
|
subtask["points"] / total_points * problem_points,
|
||||||
)
|
-self.contest.points_precision,
|
||||||
|
)
|
||||||
subtask["total"] = floatformat(
|
subtask["total"] = floatformat(
|
||||||
subtask["total"] / total_points * problem_points,
|
subtask["total"] / total_points * problem_points,
|
||||||
-self.contest.points_precision,
|
-self.contest.points_precision,
|
||||||
)
|
)
|
||||||
achieved_points = achieved_points / total_points * problem_points
|
if total_points > 0 and best_subtasks:
|
||||||
if best_subtasks:
|
achieved_points = achieved_points / total_points * problem_points
|
||||||
return best_subtasks, achieved_points, problem_points
|
return best_subtasks, achieved_points, problem_points
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@ -914,9 +928,10 @@ class UserContestSubmissionsAjax(UserContestSubmissions):
|
||||||
context["points"],
|
context["points"],
|
||||||
context["total"],
|
context["total"],
|
||||||
) = best_subtasks
|
) = best_subtasks
|
||||||
context["points"] = floatformat(
|
if context["points"] != "???":
|
||||||
context["points"], -self.contest.points_precision
|
context["points"] = floatformat(
|
||||||
)
|
context["points"], -self.contest.points_precision
|
||||||
|
)
|
||||||
context["total"] = floatformat(
|
context["total"] = floatformat(
|
||||||
context["total"], -self.contest.points_precision
|
context["total"], -self.contest.points_precision
|
||||||
)
|
)
|
||||||
|
|
|
@ -630,4 +630,4 @@ def toggle_darkmode(request):
|
||||||
if not path:
|
if not path:
|
||||||
return HttpResponseBadRequest()
|
return HttpResponseBadRequest()
|
||||||
request.session["darkmode"] = not request.session.get("darkmode", False)
|
request.session["darkmode"] = not request.session.get("darkmode", False)
|
||||||
return HttpResponseRedirect(path)
|
return HttpResponseRedirect(path)
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
<td class="lightbox-submissions-time">
|
<td class="lightbox-submissions-time">
|
||||||
<b>{{_('Subtask')}} {{subtask}}:</b>
|
<b>{{_('Subtask')}} {{subtask}}:</b>
|
||||||
</td>
|
</td>
|
||||||
<td class="lightbox-submissions-time case-{{'AC' if cur_subtask.points == cur_subtask.total else 'WA'}}">
|
<td class="lightbox-submissions-time case-{{'AC' if cur_subtask.points == cur_subtask.total else ('TLE' if cur_subtask.points == '???' else 'WA')}}">
|
||||||
{{ cur_subtask.points }} / {{ cur_subtask.total }}
|
{{ cur_subtask.points }} / {{ cur_subtask.total }}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
|
@ -28,7 +28,7 @@
|
||||||
<td class="lightbox-submissions-time">
|
<td class="lightbox-submissions-time">
|
||||||
<b>{{_('Total')}}:</b>
|
<b>{{_('Total')}}:</b>
|
||||||
</td>
|
</td>
|
||||||
<td style="border-top: 1px black solid" class="case-{{'AC' if points == total else 'WA'}}">
|
<td style="border-top: 1px solid" class="case-{{'AC' if points == total else 'WA'}}">
|
||||||
{{ points }} / {{ total }}
|
{{ points }} / {{ total }}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
Loading…
Add table
Reference in a new issue