Source code for oioioi.contests.admin

import threading
from functools import partial
from django.conf import settings

import urllib.parse

from django.contrib.admin import AllValuesFieldListFilter, SimpleListFilter
from django.contrib.admin.sites import NotRegistered
from django.contrib.admin.utils import quote, unquote
from django.db.models import Case, F, OuterRef, Q, Subquery, Value, When
from django.db.models.functions import Coalesce
from django.forms import ModelForm
from django.forms.models import modelform_factory
from django.http import HttpResponseRedirect
from django.shortcuts import redirect, render
from django.urls import re_path, reverse
from django.utils.encoding import force_str
from django.utils.html import format_html
from django.utils.translation import get_language
from django.utils.translation import gettext_lazy as _
from django.utils.translation import ngettext_lazy
from oioioi.base import admin
from oioioi.base.admin import NO_CATEGORY, delete_selected
from oioioi.base.utils import make_html_link, make_html_links
from oioioi.base.utils.filters import ProblemNameListFilter
from oioioi.base.utils.user_selection import UserSelectionField
from oioioi.contests.current_contest import set_cc_id
from oioioi.contests.forms import (
    ProblemInstanceForm,
    SimpleContestForm,
    TestsSelectionForm,
)
from oioioi.contests.menu import (
    contest_admin_menu_registry,
    contest_observer_menu_registry,
)
from oioioi.contests.models import (
    Contest,
    ContestAttachment,
    ContestLink,
    ContestPermission,
    ProblemInstance,
    Round,
    RoundTimeExtension,
    Submission,
    SubmissionReport,
    submission_kinds,
)
from oioioi.contests.utils import (
    can_admin_contest,
    is_contest_admin,
    is_contest_basicadmin,
    is_contest_observer,
)
from oioioi.problems.models import ProblemName, ProblemPackage, ProblemSite
from oioioi.problems.utils import can_admin_problem
from oioioi.programs.models import Test, TestReport


[docs]class ContestProxyAdminSite(admin.AdminSite): def __init__(self, orig): super(ContestProxyAdminSite, self).__init__(orig.name) self._orig = orig
[docs] def register(self, model_or_iterable, admin_class=None, **options): self._orig.register(model_or_iterable, admin_class, **options)
[docs] def unregister(self, model_or_iterable): self._orig.unregister(model_or_iterable) try: super(ContestProxyAdminSite, self).unregister(model_or_iterable) except NotRegistered: pass
[docs] def contest_register(self, model_or_iterable, admin_class=None, **options): super(ContestProxyAdminSite, self).register( model_or_iterable, admin_class, **options )
[docs] def contest_unregister(self, model_or_iterable): super(ContestProxyAdminSite, self).unregister(model_or_iterable)
[docs] def get_urls(self): self._registry.update(self._orig._registry) return super(ContestProxyAdminSite, self).get_urls()
[docs] def index(self, request, extra_context=None): if request.contest: return super(ContestProxyAdminSite, self).index(request, extra_context) return self._orig.index(request, extra_context)
[docs] def app_index(self, request, app_label, extra_context=None): if request.contest: return super(ContestProxyAdminSite, self).app_index( request, app_label, extra_context ) return self._orig.app_index(request, app_label, extra_context)
#: Every contest-dependent model admin should be registered in this site #: using the ``contest_register`` method. You can also register non-dependent #: model admins like you would normally do using the ``register`` method. #: Model admins registered using the ``contest_register`` method "don't exist" #: when there is no active contest, that is, they can only be accessed #: by a contest-prefixed URL and they don't show up in ``/admin/`` (but they #: do in ``/c/<contest_id>/admin/``).
[docs]contest_site = ContestProxyAdminSite(admin.site)
[docs]class RoundInline(admin.StackedInline):
[docs] model = Round
[docs] extra = 0
[docs] inline_classes = ('collapse open',)
[docs] category = NO_CATEGORY
[docs] def has_add_permission(self, request, obj=None): return True
[docs] def has_change_permission(self, request, obj=None): return True
[docs] def has_delete_permission(self, request, obj=None): return True
[docs] def get_fieldsets(self, request, obj=None): fields = [ 'name', 'start_date', 'end_date', 'results_date', 'public_results_date', 'is_trial', ] fields_no_public_results = [ 'name', 'start_date', 'end_date', 'results_date', 'is_trial', ] if ( request.contest is not None and request.contest.controller.separate_public_results() ): fdsets = [(None, {'fields': fields})] else: fdsets = [(None, {'fields': fields_no_public_results})] return fdsets
[docs]class AttachmentInline(admin.StackedInline):
[docs] model = ContestAttachment
[docs] extra = 0
[docs] readonly_fields = ['content_link']
[docs] category = NO_CATEGORY
[docs] def has_add_permission(self, request, obj=None): return True
[docs] def has_change_permission(self, request, obj=None): return True
[docs] def has_delete_permission(self, request, obj=None): return True
[docs] content_link.short_description = _("Content file")
[docs] def formfield_for_foreignkey(self, db_field, request, **kwargs): if db_field.name == 'round': kwargs['queryset'] = Round.objects.filter(contest=request.contest) return super(AttachmentInline, self).formfield_for_foreignkey( db_field, request, **kwargs )
[docs]class ContestLinkInline(admin.TabularInline):
[docs] model = ContestLink
[docs] extra = 0
[docs] category = _("Advanced")
[docs]class ContestAdmin(admin.ModelAdmin):
[docs] inlines = [RoundInline, AttachmentInline, ContestLinkInline]
[docs] readonly_fields = ['creation_date']
[docs] prepopulated_fields = {'id': ('name',)}
[docs] list_display = ['name', 'id', 'creation_date']
[docs] ordering = ['-creation_date']
[docs] def has_add_permission(self, request): return request.user.is_superuser
[docs] def has_change_permission(self, request, obj=None): if not obj: return request.user.is_superuser return can_admin_contest(request.user, obj)
[docs] def get_fields(self, request, obj=None): fields = [ 'name', 'id', 'controller_name', 'default_submissions_limit', 'contact_email', ] if settings.USE_ACE_EDITOR: fields.append('enable_editor') if request.user.is_superuser: fields += ['judging_priority', 'judging_weight'] return fields
[docs] def get_fieldsets(self, request, obj=None): if obj and not request.GET.get('simple', False): return super(ContestAdmin, self).get_fieldsets(request, obj) fields = list(SimpleContestForm().base_fields.keys()) return [(None, {'fields': fields})]
[docs] def get_readonly_fields(self, request, obj=None): if obj: return self.readonly_fields + ['id', 'controller_name'] return []
[docs] def get_prepopulated_fields(self, request, obj=None): if obj: return {} return self.prepopulated_fields
[docs] def get_form(self, request, obj=None, **kwargs): if not self.has_change_permission(request, obj): return super(ContestAdmin, self).get_form(request, obj, **kwargs) if obj and not request.GET.get('simple', False): return super(ContestAdmin, self).get_form(request, obj, **kwargs) return modelform_factory( self.model, form=SimpleContestForm, formfield_callback=partial(self.formfield_for_dbfield, request=request), exclude=self.get_readonly_fields(request, obj), )
[docs] def get_formsets(self, request, obj=None): if obj and not request.GET.get('simple', False): return super(ContestAdmin, self).get_formsets(request, obj) return []
[docs] def response_change(self, request, obj): # Never redirect to the list of contests. Just re-display the edit # view. if '_popup' not in request.POST: return HttpResponseRedirect(request.get_full_path()) return super(ContestAdmin, self).response_change(request, obj)
[docs] def response_add(self, request, obj, post_url_continue=None): default_redirection = super(ContestAdmin, self).response_add( request, obj, post_url_continue ) if '_continue' in request.POST or '_addanother' in request.POST: return default_redirection else: return redirect('default_contest_view', contest_id=obj.id)
[docs] def response_delete(self, request): set_cc_id(None) return super(ContestAdmin, self).response_delete(request)
[docs] def _get_extra_context(self, extra_context): extra_context = extra_context or {} extra_context['categories'] = sorted( set([getattr(inline, 'category', None) for inline in self.inlines]) ) extra_context['no_category'] = NO_CATEGORY return extra_context
[docs] def add_view(self, request, form_url='', extra_context=None): extra_context = self._get_extra_context(extra_context) return super(ContestAdmin, self).add_view(request, form_url, extra_context)
[docs] def change_view(self, request, object_id, form_url='', extra_context=None): extra_context = self._get_extra_context(extra_context) # The contest's edit view uses request.contest, so editing a contest # when a different contest is active would produce weird results. contest_id = unquote(object_id) if not request.contest or request.contest.id != contest_id: return redirect( 'oioioiadmin:contests_contest_change', object_id, contest_id=contest_id ) return super(ContestAdmin, self).change_view( request, object_id, form_url, extra_context )
[docs] def delete_selected_contests(self, modeladmin, request, queryset): # Redirect to contest-unprefixed view, just in case we deleted the current contest. return delete_selected( modeladmin, request, queryset, specific_redirect='noncontest:oioioiadmin:contests_contest_changelist', )
[docs] def get_actions(self, request): # Use delete_selected with a custom redirect. actions = super(ContestAdmin, self).get_actions(request) actions['delete_selected'] = ( self.delete_selected_contests, 'delete_selected', delete_selected.short_description, ) return actions
[docs]class BaseContestAdmin(admin.MixinsAdmin):
[docs] default_model_admin = ContestAdmin
[docs] def _mixins_for_instance(self, request, instance=None): if instance: controller = instance.controller return ( controller.mixins_for_admin() + controller.registration_controller().mixins_for_admin() )
contest_site.register(Contest, BaseContestAdmin) contest_admin_menu_registry.register( 'contest_change', _("Settings"), lambda request: reverse( 'oioioiadmin:contests_contest_change', args=(quote(request.contest.id),) ), order=20, )
[docs]class ProblemInstanceAdmin(admin.ModelAdmin):
[docs] form = ProblemInstanceForm
[docs] fields = ('contest', 'round', 'problem', 'short_name', 'submissions_limit')
[docs] list_display = ('name_link', 'short_name_link', 'round', 'package', 'actions_field')
[docs] readonly_fields = ('contest', 'problem')
[docs] ordering = ('-round__start_date', 'short_name')
def __init__(self, *args, **kwargs): # creating a thread local variable to store the request self._request_local = threading.local() self._request_local.request = None super(ProblemInstanceAdmin, self).__init__(*args, **kwargs)
[docs] def has_add_permission(self, request): return False
[docs] def has_change_permission(self, request, obj=None): return is_contest_basicadmin(request)
[docs] def has_delete_permission(self, request, obj=None): return self.has_change_permission(request, obj)
[docs] def _problem_change_href(self, instance): came_from = reverse('oioioiadmin:contests_probleminstance_changelist') came_from_arg = urllib.parse.urlencode({'came_from': came_from}) problem_change_base_href = reverse( 'oioioiadmin:problems_problem_change', args=(instance.problem_id,) ) return '%s?%s' % (problem_change_base_href, came_from_arg)
[docs] def _rejudge_all_submissions_for_problem_href(self, instance): return reverse('rejudge_all_submissions_for_problem', args=(instance.id,))
[docs] def _set_needs_rejudge_to_false_href(self, instance): return reverse('rejudge_not_needed', args=(instance.id,))
[docs] def _model_solutions_href(self, instance): return reverse('model_solutions', args=(instance.id,))
[docs] def _problem_site_href(self, instance): return reverse('problem_site', args=(instance.problem.problemsite.url_key,))
[docs] def _reset_limits_href(self, instance): return reverse('reset_tests_limits_for_probleminstance', args=(instance.id,))
[docs] def _reattach_problem_href(self, instance): return reverse('reattach_problem_contest_list', args=(instance.id,))
[docs] def _add_or_update_href(self, instance): return ( reverse('problemset_add_or_update') + '?' + urllib.parse.urlencode( {'problem': instance.problem_id, 'key': 'upload'} ) )
[docs] def _replace_statement_href(self, instance): return ( reverse('problem_site', args=(instance.problem.problemsite.url_key,)) + '?' + urllib.parse.urlencode({'key': 'replace_problem_statement'}) )
[docs] def _package_manage_href(self, instance): return ( reverse('problem_site', args=(instance.problem.problemsite.url_key,)) + '?' + urllib.parse.urlencode({'key': 'manage_files_problem_package'}) )
[docs] def _edit_quiz_href(self, instance): return reverse('oioioiadmin:quizzes_quiz_change', args=[instance.problem.pk])
[docs] def _move_href(self, instance): return reverse( 'oioioiadmin:contests_probleminstance_change', args=(instance.id,) )
[docs] def get_list_display(self, request): items = super(ProblemInstanceAdmin, self).get_list_display(request) if not is_contest_admin(request): disallowed_items = ['package'] items = [item for item in items if item not in disallowed_items] return items
[docs] def inline_actions(self, instance): result = [] assert ProblemSite.objects.filter(problem=instance.problem).exists() move_href = self._move_href(instance) result.append((move_href, _("Edit"))) is_quiz = hasattr(instance.problem, 'quiz') and instance.problem.quiz if is_quiz: edit_quiz_href = self._edit_quiz_href(instance) result.append((edit_quiz_href, _("Quiz questions"))) else: models_href = self._model_solutions_href(instance) limits_href = self._reset_limits_href(instance) result.extend( [ (models_href, _("Model solutions")), (limits_href, _("Reset tests limits")), ] ) reattach_href = self._reattach_problem_href(instance) result.append((reattach_href, _("Attach to another contest"))) problem_count = len(ProblemInstance.objects.filter(problem=instance.problem_id)) # Problem package can only be reuploaded if the problem instance # is only in one contest and in the problem base # Package reupload does not apply to quizzes. if problem_count <= 2 and not is_quiz: add_or_update_href = self._add_or_update_href(instance) result.append((add_or_update_href, _("Reupload package"))) if instance.needs_rejudge: rejudge_all_href = self._rejudge_all_submissions_for_problem_href(instance) result.append((rejudge_all_href, _("Rejudge all submissions for problem"))) rejudge_not_needed_href = self._set_needs_rejudge_to_false_href(instance) result.append((rejudge_not_needed_href, _("Rejudge not needed"))) problem_change_href = self._problem_change_href(instance) replace_statement_href = self._replace_statement_href(instance) package_manage_href = self._package_manage_href(instance) request = self._request_local.request if can_admin_problem(request, instance.problem): result.append((problem_change_href, _("Advanced settings"))) result.append((replace_statement_href, _("Replace statement"))) result.append((package_manage_href, _("Edit package"))) return result
[docs] def actions_field(self, instance): return make_html_links(self.inline_actions(instance))
[docs] actions_field.short_description = _("Actions")
name_link.short_description = _("Problem")
[docs] name_link.admin_order_field = 'ordering_name'
short_name_link.short_description = _("Symbol") short_name_link.admin_order_field = 'short_name'
[docs] def package(self, instance): problem_package = ProblemPackage.objects.filter( problem=instance.problem ).first() request = self._request_local.request if ( problem_package and problem_package.package_file and can_admin_problem(request, instance.problem) ): href = reverse( 'download_package', kwargs={'package_id': str(problem_package.id)} ) return make_html_link(href, problem_package.package_file) return None
package.short_description = _("Package file")
[docs] def get_actions(self, request): self._request_local.request = request # Disable delete_selected. actions = super(ProblemInstanceAdmin, self).get_actions(request) if 'delete_selected' in actions: del actions['delete_selected'] return actions
[docs] def get_queryset(self, request): qs = super(ProblemInstanceAdmin, self).get_queryset(request) qs = ( qs.filter(contest=request.contest) .annotate( localized_name=Subquery( ProblemName.objects.filter( problem=OuterRef('problem__pk'), language=get_language() ).values('name') ) ) .annotate( ordering_name=Case( When(localized_name__isnull=True, then=F('problem__legacy_name')), default=F('localized_name'), ) ) ) return qs
contest_site.contest_register(ProblemInstance, ProblemInstanceAdmin) contest_admin_menu_registry.register( 'problems_change', _("Problems"), lambda request: reverse('oioioiadmin:contests_probleminstance_changelist'), order=30, )
[docs]class ProblemFilter(AllValuesFieldListFilter):
[docs] title = _("problem")
[docs]class ContestsProblemNameListFilter(ProblemNameListFilter):
[docs] initial_query_manager = Submission.objects
[docs] contest_field = F('problem_instance__contest')
[docs] related_names = 'problem_instance__problem__names'
[docs] legacy_name_field = F('problem_instance__problem__legacy_name')
[docs] outer_ref = OuterRef('problem_instance__problem__pk')
[docs]class SubmissionKindListFilter(SimpleListFilter):
[docs] title = _("kind")
[docs] parameter_name = 'kind'
[docs] def lookups(self, request, model_admin): return submission_kinds
[docs] def queryset(self, request, queryset): if self.value(): return queryset.filter(kind=self.value()) else: return queryset
[docs]class SubmissionRoundListFilter(SimpleListFilter):
[docs] title = _("round")
[docs] parameter_name = 'round'
[docs] def lookups(self, request, model_admin): r = [] if request.contest: r = Round.objects.filter(contest=request.contest) return [(x, x) for x in r]
[docs] def queryset(self, request, queryset): if self.value(): return queryset.filter(problem_instance__round__name=self.value()) else: return queryset
[docs]class ContestListFilter(SimpleListFilter):
[docs] title = _("contest")
[docs] parameter_name = 'contest'
[docs] def lookups(self, request, model_admin): contests = list(Contest.objects.all()) return [(x, x) for x in contests]
[docs] def queryset(self, request, queryset): if self.value(): return queryset.filter(problem_instance__contest__name=self.value()) else: return queryset
[docs]class SystemErrorListFilter(SimpleListFilter):
[docs] title = _("has active system error")
[docs] parameter_name = 'has_active_system_error'
[docs] def lookups(self, request, model_admin): return [('no', _("No")), ('yes', _("Yes"))]
[docs] def queryset(self, request, queryset): q = Q( submissionreport__status='ACTIVE', submissionreport__failurereport__isnull=False, ) | Q( submissionreport__status='ACTIVE', submissionreport__testreport__status='SE' ) if self.value() == 'yes': return queryset.filter(q) elif self.value() == 'no': return queryset.exclude(q) else: return queryset
[docs]class SubmissionAdmin(admin.ModelAdmin):
[docs] date_hierarchy = 'date'
[docs] actions = ['rejudge_action']
[docs] search_fields = [ 'user__username', 'user__last_name', 'problem_instance__problem__legacy_name', 'problem_instance__short_name', ]
[docs] class Media:
[docs] js = ('admin/js/jquery.init.js', 'js/admin-filter-collapse.js')
# We're using functions instead of lists because we want to # have different columns and filters depending on whether # contest is in url or not.
[docs] def get_list_display(self, request): list_display = [ 'id', 'user_login', 'user_full_name', 'date', 'problem_instance_display', 'contest_display', 'status_display', 'score_display', ] if request.contest: list_display.remove('contest_display') return list_display
[docs] def get_list_filter(self, request): list_filter = [ ContestsProblemNameListFilter, ContestListFilter, SubmissionKindListFilter, 'status', SubmissionRoundListFilter, SystemErrorListFilter, ] if request.contest: list_filter.remove(ContestListFilter) else: list_filter.remove(SubmissionRoundListFilter) return list_filter
[docs] def get_urls(self): urls = [re_path(r'^rejudge/$', self.rejudge_view)] return urls + super(SubmissionAdmin, self).get_urls()
[docs] def get_search_results(self, request, queryset, search_term): matched_problems = set( problem_name.problem.pk for problem_name in ProblemName.objects.filter(name__icontains=search_term) ) queryset, _ = super(SubmissionAdmin, self).get_search_results( request, queryset, search_term ) contest_filtered = self.get_queryset(request) queryset |= contest_filtered.filter( problem_instance__problem__in=matched_problems ) queryset = queryset.order_by('-id') return queryset, True
[docs] def rejudge_view(self, request): tests = request.POST.getlist('tests', []) subs_ids = [int(x) for x in request.POST.getlist('submissions', [])] rejudge_type = request.POST['rejudge_type'] submissions = Submission.objects.in_bulk(subs_ids) all_reports_exist = True for sub in submissions.values(): if not SubmissionReport.objects.filter( submission=sub, status='ACTIVE' ).exists(): all_reports_exist = False break if all_reports_exist or rejudge_type == 'FULL': for sub in submissions.values(): sub.problem_instance.controller.judge( sub, is_rejudge=True, extra_args={'tests_to_judge': tests, 'rejudge_type': rejudge_type}, ) counter = len(submissions) self.message_user( request, ngettext_lazy( "Queued one submission for rejudge.", "Queued %(counter)d submissions for rejudge.", counter, ) % {'counter': counter}, ) else: self.message_user( request, _( "Cannot rejudge submissions due to lack of active reports " "for one or more submissions" ), ) return redirect('oioioiadmin:contests_submission_changelist')
[docs] def has_add_permission(self, request): return False
[docs] def has_change_permission(self, request, obj=None): if obj: return False # is_contest_observer() is required in here, because otherwise # observers get a 403 response. Any actions that modify submissions # will be blocked in get_actions() return is_contest_basicadmin(request) or is_contest_observer(request)
[docs] def has_delete_permission(self, request, obj=None): return is_contest_basicadmin(request)
[docs] def has_rejudge_permission(self, request): return is_contest_basicadmin(request)
[docs] def get_actions(self, request): actions = super(SubmissionAdmin, self).get_actions(request) if not request.user.is_superuser: if not self.has_delete_permission(request): del actions['delete_selected'] if not self.has_rejudge_permission(request): del actions['rejudge_action'] return actions
[docs] def user_login(self, instance): if not instance.user: return '' return instance.user.username
[docs] user_login.short_description = _("Login")
[docs] user_login.admin_order_field = 'user__username'
[docs] def user_full_name(self, instance): if not instance.user: return '' return instance.user.get_full_name()
user_full_name.short_description = _("User name") user_full_name.admin_order_field = 'user__last_name'
[docs] def problem_instance_display(self, instance): if instance.kind != 'NORMAL': return '%s (%s)' % ( force_str(instance.problem_instance), force_str(instance.get_kind_display()), ) else: return instance.problem_instance
problem_instance_display.short_description = _("Problem") problem_instance_display.admin_order_field = 'problem_instance'
[docs] def status_display(self, instance): return format_html( u'<span class="submission-admin submission submission--{}">{}</span>', instance.status, instance.get_status_display(), )
status_display.short_description = _("Status") status_display.admin_order_field = 'status'
[docs] def score_display(self, instance): return instance.get_score_display() or ''
score_display.short_description = _("Score") score_display.admin_order_field = 'score_with_nulls_smallest'
[docs] def contest_display(self, instance): return instance.problem_instance.contest
contest_display.short_description = _("Contest") contest_display.admin_order_field = 'problem_instance__contest'
[docs] def rejudge_action(self, request, queryset): # Otherwise the submissions are rejudged in their default display # order which is "newest first" queryset = queryset.order_by('id') pis = {s.problem_instance for s in queryset} pis_count = len(pis) sub_count = len(queryset) self.message_user( request, _( "You have selected %(sub_count)d submission(s) from " "%(pis_count)d problem(s)" ) % {'sub_count': sub_count, 'pis_count': pis_count}, ) uses_is_active = False for pi in pis: if Test.objects.filter(problem_instance=pi, is_active=False).exists(): uses_is_active = True break if not uses_is_active: for sub in queryset: if TestReport.objects.filter( submission_report__submission=sub, submission_report__status='ACTIVE', test__is_active=False, ).exists(): uses_is_active = True break return render( request, 'contests/tests_choice.html', {'form': TestsSelectionForm(request, queryset, pis_count, uses_is_active)}, )
rejudge_action.short_description = _("Rejudge selected submissions")
[docs] def get_queryset(self, request): queryset = super(SubmissionAdmin, self).get_queryset(request) if request.contest: queryset = queryset.filter(problem_instance__contest=request.contest) queryset = queryset.order_by('-id') # Because nulls are treated as highest by default, # this is a workaround to make them smaller than other values. queryset = queryset.annotate( score_with_nulls_smallest=Coalesce('score', Value('')) ) return queryset
[docs] def lookup_allowed(self, key, value): if key == 'user__username': return True return super(SubmissionAdmin, self).lookup_allowed(key, value)
[docs] def change_view(self, request, object_id, form_url='', extra_context=None): _contest_id = None if request.contest: _contest_id = request.contest.id if _contest_id is None: contest = Submission.objects.get(pk=object_id).problem_instance.contest if contest: _contest_id = contest.id return redirect( 'submission', contest_id=_contest_id, submission_id=unquote(object_id) )
contest_site.register(Submission, SubmissionAdmin) contest_admin_menu_registry.register( 'submissions_admin', _("Submissions"), lambda request: reverse('oioioiadmin:contests_submission_changelist'), order=40, ) contest_observer_menu_registry.register( 'submissions_admin', _("Submissions"), lambda request: reverse('oioioiadmin:contests_submission_changelist'), order=40, ) admin.system_admin_menu_registry.register( 'managesubmissions_admin', _("All submissions"), lambda request: reverse( 'oioioiadmin:contests_submission_changelist', kwargs={'contest_id': None} ), order=50, )
[docs]class RoundTimeRoundListFilter(SimpleListFilter):
[docs] title = _("round")
[docs] parameter_name = 'round'
[docs] def lookups(self, request, model_admin): qs = model_admin.get_queryset(request) return Round.objects.filter(id__in=qs.values_list('round')).values_list( 'id', 'name' )
[docs] def queryset(self, request, queryset): if self.value(): return queryset.filter(round=self.value()) else: return queryset
[docs]class RoundTimeExtensionAdmin(admin.ModelAdmin):
[docs] list_display = ['user_login', 'user_full_name', 'round', 'extra_time']
[docs] list_filter = [RoundTimeRoundListFilter]
[docs] search_fields = ['user__username', 'user__last_name']
[docs] def has_add_permission(self, request): return is_contest_admin(request)
[docs] def has_change_permission(self, request, obj=None): return is_contest_admin(request)
[docs] def has_delete_permission(self, request, obj=None): return self.has_change_permission(request, obj)
[docs] def user_login(self, instance): if not instance.user: return '' return make_html_link( reverse( 'user_info', kwargs={ 'contest_id': instance.round.contest.id, 'user_id': instance.user.id, }, ), instance.user.username, )
[docs] user_login.short_description = _("Login")
[docs] user_login.admin_order_field = 'user__username'
[docs] def user_full_name(self, instance): if not instance.user: return '' return instance.user.get_full_name()
user_full_name.short_description = _("User name") user_full_name.admin_order_field = 'user__last_name'
[docs] def get_queryset(self, request): qs = super(RoundTimeExtensionAdmin, self).get_queryset(request) return qs.filter(round__contest=request.contest)
[docs] def formfield_for_foreignkey(self, db_field, request, **kwargs): if db_field.name == 'round': kwargs['queryset'] = Round.objects.filter(contest=request.contest) return super(RoundTimeExtensionAdmin, self).formfield_for_foreignkey( db_field, request, **kwargs )
contest_site.contest_register(RoundTimeExtension, RoundTimeExtensionAdmin) contest_admin_menu_registry.register( 'roundtimeextension_admin', _("Round extensions"), lambda request: reverse('oioioiadmin:contests_roundtimeextension_changelist'), is_contest_admin, order=50, )
[docs]class ContestPermissionAdminForm(ModelForm):
[docs] user = UserSelectionField(label=_("Username"))
[docs] class Meta(object):
[docs] model = ContestPermission
[docs] fields = ('user', 'contest', 'permission')
[docs]class ContestPermissionAdmin(admin.ModelAdmin):
[docs] list_display = ['permission', 'user', 'user_full_name']
[docs] ordering = ['permission', 'user']
[docs] form = ContestPermissionAdminForm
[docs] def user_full_name(self, instance): if not instance.user: return '' return instance.user.get_full_name()
[docs] user_full_name.short_description = _("User name")
[docs] user_full_name.admin_order_field = 'user__last_name'
[docs] def get_queryset(self, request): qs = super(ContestPermissionAdmin, self).get_queryset(request) if request.contest: qs = qs.filter(contest=request.contest) return qs
[docs] def formfield_for_foreignkey(self, db_field, request, **kwargs): if db_field.name == 'contest': qs = Contest.objects if request.contest: qs = qs.filter(id=request.contest.id) kwargs['initial'] = request.contest kwargs['queryset'] = qs return super(ContestPermissionAdmin, self).formfield_for_foreignkey( db_field, request, **kwargs )
contest_site.register(ContestPermission, ContestPermissionAdmin) admin.system_admin_menu_registry.register( 'contestspermission_admin', _("Contest rights"), lambda request: reverse('oioioiadmin:contests_contestpermission_changelist'), order=50, )