Rescore contest when parameters change

This commit is contained in:
cuom1999 2020-12-27 20:27:03 -06:00
parent b3d80b5100
commit a6be869658
5 changed files with 78 additions and 4 deletions

View file

@ -123,7 +123,6 @@ class ContestAdmin(VersionAdmin):
(_('Justice'), {'fields': ('banned_users',)}),
)
list_display = ('key', 'name', 'is_visible', 'is_rated', 'start_time', 'end_time', 'time_limit', 'user_count')
actions = ['make_visible', 'make_hidden']
inlines = [ContestProblemInline]
actions_on_top = True
actions_on_bottom = True
@ -132,6 +131,16 @@ class ContestAdmin(VersionAdmin):
filter_horizontal = ['rate_exclude']
date_hierarchy = 'start_time'
def get_actions(self, request):
actions = super(ContestAdmin, self).get_actions(request)
if request.user.has_perm('judge.change_contest_visibility') or \
request.user.has_perm('judge.create_private_contest'):
for action in ('make_visible', 'make_hidden'):
actions[action] = self.get_action(action)
return actions
def get_queryset(self, request):
queryset = Contest.objects.all()
if request.user.has_perm('judge.edit_all_contest'):
@ -147,8 +156,32 @@ class ContestAdmin(VersionAdmin):
readonly += ['access_code']
if not request.user.has_perm('judge.create_private_contest'):
readonly += ['is_private', 'private_contestants', 'is_organization_private', 'organizations']
if not request.user.has_perm('judge.change_contest_visibility'):
readonly += ['is_visible']
return readonly
def save_model(self, request, obj, form, change):
# `is_visible` will not appear in `cleaned_data` if user cannot edit it
if form.cleaned_data.get('is_visible') and not request.user.has_perm('judge.change_contest_visibility'):
if not form.cleaned_data['is_private'] and not form.cleaned_data['is_organization_private']:
raise PermissionDenied
if not request.user.has_perm('judge.create_private_contest'):
raise PermissionDenied
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 ('format_config', 'format_name')):
self._rescore(obj.key)
self._rescored = True
def save_related(self, request, form, formsets, change):
super().save_related(request, form, formsets, change)
# 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):
self._rescore(form.cleaned_data['key'])
def has_change_permission(self, request, obj=None):
if not request.user.has_perm('judge.edit_own_contest'):
return False
@ -156,7 +189,13 @@ class ContestAdmin(VersionAdmin):
return True
return obj.organizers.filter(id=request.profile.id).exists()
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):
if not request.user.has_perm('judge.change_contest_visibility'):
queryset = queryset.filter(Q(is_private=True) | Q(is_organization_private=True))
count = queryset.update(is_visible=True)
self.message_user(request, ungettext('%d contest successfully marked as visible.',
'%d contests successfully marked as visible.',
@ -164,7 +203,9 @@ class ContestAdmin(VersionAdmin):
make_visible.short_description = _('Mark contests as visible')
def make_hidden(self, request, queryset):
count = queryset.update(is_visible=False)
if not request.user.has_perm('judge.change_contest_visibility'):
queryset = queryset.filter(Q(is_private=True) | Q(is_organization_private=True))
count = queryset.update(is_visible=True)
self.message_user(request, ungettext('%d contest successfully marked as hidden.',
'%d contests successfully marked as hidden.',
count) % count)

View file

@ -0,0 +1,17 @@
# Generated by Django 2.2.17 on 2020-12-28 02:11
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('judge', '0112_contest_view_contest_scoreboard'),
]
operations = [
migrations.AlterModelOptions(
name='contest',
options={'permissions': (('see_private_contest', 'See private contests'), ('edit_own_contest', 'Edit own contests'), ('edit_all_contest', 'Edit all contests'), ('clone_contest', 'Clone contest'), ('moss_contest', 'MOSS contest'), ('contest_rating', 'Rate contests'), ('contest_access_code', 'Contest access codes'), ('create_private_contest', 'Create private contests'), ('change_contest_visibility', 'Change contest visibility')), 'verbose_name': 'contest', 'verbose_name_plural': 'contests'},
),
]

View file

@ -254,6 +254,7 @@ class Contest(models.Model):
('contest_rating', _('Rate contests')),
('contest_access_code', _('Contest access codes')),
('create_private_contest', _('Create private contests')),
('change_contest_visibility', _('Change contest visibility')),
)
verbose_name = _('contest')
verbose_name_plural = _('contests')

View file

@ -1,3 +1,3 @@
from judge.tasks.demo import *
from judge.tasks.moss import *
from judge.tasks.contest import *
from judge.tasks.submission import *

View file

@ -7,7 +7,22 @@ from moss import MOSS
from judge.models import Contest, ContestMoss, ContestParticipation, Submission
from judge.utils.celery import Progress
__all__ = ('run_moss',)
__all__ = ('rescore_contest', 'run_moss')
@shared_task(bind=True)
def rescore_contest(self, contest_key):
contest = Contest.objects.get(key=contest_key)
participations = contest.users
rescored = 0
with Progress(self, participations.count(), stage=_('Recalculating contest scores')) as p:
for participation in participations.iterator():
participation.recompute_results()
rescored += 1
if rescored % 10 == 0:
p.done = rescored
return rescored
@shared_task(bind=True)