Merge pull request #127 from anhkha2003/master

Fix contest rescore bug
This commit is contained in:
Phuoc Anh Kha Le 2024-10-02 20:43:13 -05:00 committed by GitHub
commit f771aac922
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 42 additions and 58 deletions

View file

@ -32,6 +32,7 @@ from judge.widgets import (
HeavyPreviewAdminPageDownWidget, HeavyPreviewAdminPageDownWidget,
) )
from judge.views.contests import recalculate_contest_summary_result from judge.views.contests import recalculate_contest_summary_result
from judge.utils.contest import maybe_trigger_contest_rescore
class AdminHeavySelect2Widget(AdminHeavySelect2Widget): class AdminHeavySelect2Widget(AdminHeavySelect2Widget):
@ -310,39 +311,14 @@ class ContestAdmin(CompareVersionAdmin):
super().save_model(request, obj, form, change) super().save_model(request, obj, form, change)
# We need this flag because `save_related` deals with the inlines, but does not know if we have already rescored
self._rescored = False
if form.changed_data and any(
f in form.changed_data
for f in (
"start_time",
"end_time",
"time_limit",
"format_config",
"format_name",
"freeze_after",
)
):
self._rescore(obj.key)
self._rescored = True
if form.changed_data and any(
f in form.changed_data
for f in (
"authors",
"curators",
"testers",
)
):
Contest._author_ids.dirty(obj)
Contest._curator_ids.dirty(obj)
Contest._tester_ids.dirty(obj)
def save_related(self, request, form, formsets, change): def save_related(self, request, form, formsets, change):
super().save_related(request, form, formsets, change) super().save_related(request, form, formsets, change)
# Only rescored if we did not already do so in `save_model` # Only rescored if we did not already do so in `save_model`
if not self._rescored and any(formset.has_changed() for formset in formsets): formset_changed = False
self._rescore(form.cleaned_data["key"]) if any(formset.has_changed() for formset in formsets):
formset_changed = True
maybe_trigger_contest_rescore(form, form.instance, formset_changed)
def has_change_permission(self, request, obj=None): def has_change_permission(self, request, obj=None):
if not request.user.has_perm("judge.edit_own_contest"): if not request.user.has_perm("judge.edit_own_contest"):
@ -351,11 +327,6 @@ class ContestAdmin(CompareVersionAdmin):
return True return True
return obj.is_editable_by(request.user) return obj.is_editable_by(request.user)
def _rescore(self, contest_key):
from judge.tasks import rescore_contest
transaction.on_commit(rescore_contest.s(contest_key).delay)
def make_visible(self, request, queryset): def make_visible(self, request, queryset):
if not request.user.has_perm("judge.change_contest_visibility"): if not request.user.has_perm("judge.change_contest_visibility"):
queryset = queryset.filter( queryset = queryset.filter(

View file

@ -5,8 +5,9 @@ from judge.models import (
) )
def maybe_trigger_contest_rescore(form, contest): def maybe_trigger_contest_rescore(form, contest, force_rescore=False):
if any( if (
any(
f in form.changed_data f in form.changed_data
for f in ( for f in (
"start_time", "start_time",
@ -16,6 +17,8 @@ def maybe_trigger_contest_rescore(form, contest):
"format_name", "format_name",
"freeze_after", "freeze_after",
) )
)
or force_rescore
): ):
transaction.on_commit(rescore_contest.s(contest.key).delay) transaction.on_commit(rescore_contest.s(contest.key).delay)

View file

@ -715,7 +715,10 @@ class EditCourseContest(CourseEditableMixin, FormView):
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
problem_formset = self.get_problem_formset(True) problem_formset = self.get_problem_formset(True)
if problem_formset.is_valid(): if problem_formset.is_valid():
self.problem_form_changes = False
for problem_form in problem_formset: for problem_form in problem_formset:
if problem_form.has_changed():
self.problem_form_changes = True
if problem_form.cleaned_data.get("DELETE") and problem_form.instance.pk: if problem_form.cleaned_data.get("DELETE") and problem_form.instance.pk:
problem_form.instance.delete() problem_form.instance.delete()
@ -741,7 +744,8 @@ class EditCourseContest(CourseEditableMixin, FormView):
revisions.set_comment(_("Edited from course") + " " + self.course.name) revisions.set_comment(_("Edited from course") + " " + self.course.name)
revisions.set_user(self.request.user) revisions.set_user(self.request.user)
maybe_trigger_contest_rescore(form, self.contest) if self.problem_form_changes:
maybe_trigger_contest_rescore(form, self.contest, True)
form.save() form.save()

View file

@ -1039,7 +1039,7 @@ class EditOrganizationContest(
self.object.is_organization_private = True self.object.is_organization_private = True
self.object.save() self.object.save()
maybe_trigger_contest_rescore(form, self.object) maybe_trigger_contest_rescore(form, self.object, True)
return res return res

View file

@ -843,6 +843,10 @@ noscript #noscript {
.nav-fa-icon { .nav-fa-icon {
display: none; display: none;
} }
.page-title {
margin-left: 0.5em;
}
} }
@media(min-width: 800px) { @media(min-width: 800px) {

View file

@ -22,7 +22,7 @@
} }
} }
td:first-child { th:first-child, td:first-child {
border-color: $border_gray; border-color: $border_gray;
border-width: 1px 1px 0 1px; border-width: 1px 1px 0 1px;
} }

View file

@ -179,6 +179,7 @@ th.header.rank {
vertical-align: top; vertical-align: top;
width: 225px; width: 225px;
float: right; float: right;
margin-top: 1em;
.select2-selection__arrow { .select2-selection__arrow {
display: none; display: none;

View file

@ -47,8 +47,8 @@
var rows = $('#users-table tbody tr').get(); var rows = $('#users-table tbody tr').get();
var sortBy = $(this).val(); var sortBy = $(this).val();
rows.sort(function(a, b) { rows.sort(function(a, b) {
var keyA = $(a).find(sortBy === 'username' ? '.user-name' : 'td:last-child').text().trim(); var keyA = $(a).find(sortBy === 'username' ? '.user-name' : '.total-score').text().trim();
var keyB = $(b).find(sortBy === 'username' ? '.user-name' : 'td:last-child').text().trim(); var keyB = $(b).find(sortBy === 'username' ? '.user-name' : '.total-score').text().trim();
if(sortBy === 'total') { if(sortBy === 'total') {
// Convert percentage string to number for comparison // Convert percentage string to number for comparison
@ -76,11 +76,13 @@
{% block middle_content %} {% block middle_content %}
<center><h2>{{content_title}}</h2></center> <center><h2>{{content_title}}</h2></center>
{{_("Sort by")}}: {{_("Sort by")}}:
<div>
<select id="sortSelect"> <select id="sortSelect">
<option value="username">{{_("Username")}}</option> <option value="username">{{_("Username")}}</option>
<option value="total">{{_("Score")}}</option> <option value="total">{{_("Score")}}</option>
</select> </select>
<input type="text" id="search-input" placeholder="{{_('Search')}}" autofocus> <input type="text" id="search-input" placeholder="{{_('Search')}}" autofocus>
</div>
<div style="overflow-x: auto; margin-top: 1em"> <div style="overflow-x: auto; margin-top: 1em">
<table class="table striped" id="users-table"> <table class="table striped" id="users-table">
<thead> <thead>
@ -119,7 +121,7 @@
{{student.first_name}} {{student.first_name}}
</div> </div>
</td> </td>
<td style="font-weight: bold"> <td class="total-score" style="font-weight: bold">
{% if grade_total_student %} {% if grade_total_student %}
{{ grade_total_student['percentage'] | floatformat(0) }}% {{ grade_total_student['percentage'] | floatformat(0) }}%
{% else %} {% else %}

View file

@ -10,7 +10,6 @@
</div> </div>
<div class="score">{{ breakdown.sub_points|floatformat(0) }} / {{ breakdown.sub_total|floatformat(0) }}</div> <div class="score">{{ breakdown.sub_points|floatformat(0) }} / {{ breakdown.sub_total|floatformat(0) }}</div>
<div class="language">{{ breakdown.sub_lang }}</div> <div class="language">{{ breakdown.sub_lang }}</div>
<span class="time">{{ relative_time(breakdown.sub_date) }}</span>
</div> </div>
</div> </div>
<div class="sub-pp sub-usage"> <div class="sub-pp sub-usage">
@ -19,7 +18,7 @@
</div> </div>
<div class="pp-weighted"> <div class="pp-weighted">
{% trans weight=breakdown.weight|floatformat(0) %} {% trans weight=breakdown.weight|floatformat(0) %}
weighted <b>{{ weight }}%</b> <b>{{ weight }}%</b>
{% endtrans %} {% endtrans %}
{% if breakdown.scaled_points < 10 %} {% if breakdown.scaled_points < 10 %}