import logging
import urllib.parse
from django import forms
from django.core.exceptions import ObjectDoesNotExist, PermissionDenied
from django.db.models import Q
from django.http import HttpResponseRedirect
from django.shortcuts import redirect
from django.template.loader import render_to_string
from django.template.response import TemplateResponse
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from oioioi.base.utils import get_user_display_name, request_cached
from oioioi.base.utils.query_helpers import Q_always_true
from oioioi.base.utils.redirect import safe_redirect
from oioioi.contests.controllers import ContestController, RegistrationController
from oioioi.contests.models import RegistrationAvailabilityConfig
from oioioi.contests.utils import can_see_personal_data, is_contest_admin
from oioioi.participants.models import (
OnsiteRegistration,
Participant,
RegistrationModel,
TermsAcceptedPhrase,
)
[docs]auditLogger = logging.getLogger(__name__ + ".audit")
[docs]class ParticipantsController(RegistrationController):
[docs] registration_template = 'participants/registration.html'
@property
@property
[docs] def participant_admin(self):
from oioioi.participants.admin import ParticipantAdmin
return ParticipantAdmin
@classmethod
[docs] def anonymous_can_enter_contest(cls):
return False
[docs] def allow_login_as_public_name(self):
"""Determines if participants may choose to stay anonymous,
i.e. use their logins as public names.
"""
return False
[docs] def filter_participants(self, queryset):
return queryset.filter(
participant__contest=self.contest, participant__status='ACTIVE'
)
[docs] def user_contests_query(self, request):
if not request.user.is_authenticated:
return Q(pk__isnull=True) # (False)
return Q(participant__user__id=request.user.id, participant__status='ACTIVE')
[docs] def filter_users_with_accessible_personal_data(self, queryset):
return self.filter_participants(queryset)
[docs] def can_register(self, request):
return False
[docs] def can_edit_registration(self, request, participant):
if self.form_class is None:
return False
if is_contest_admin(request):
return True
if participant.status == 'BANNED':
return False
return bool(request.user == participant.user)
[docs] def can_unregister(self, request, participant):
return self.can_edit_registration(request, participant)
[docs] def no_entry_view(self, request):
if self.can_register(request):
url = (
reverse('participants_register', kwargs={'contest_id': self.contest.id})
+ '?'
+ urllib.parse.urlencode(
{'next': request.build_absolute_uri()}
)
)
return HttpResponseRedirect(url)
return super(ParticipantsController, self).no_entry_view(request)
[docs] def get_model_class(self):
"""Returns registration model class used within current registration
controller.
The default implementation infers it from form_class form metadata.
If there is no form_class, the default implementation returns
``None``.
"""
if self.form_class is None:
return None
assert issubclass(
self.form_class, forms.ModelForm
), 'ParticipantsController.form_class must be a ModelForm'
model_class = self.form_class._meta.model
assert issubclass(model_class, RegistrationModel), (
'ParticipantsController.form_class\'s model must be a '
'subclass of RegistrationModel'
)
return model_class
[docs] def registration_view(self, request):
participant = self._get_participant_for_form(request)
form = self.get_form(request, participant)
assert form is not None, (
"can_register or can_edit_registration "
"returned True, but controller returns no registration form"
)
if request.method == 'POST':
if form.is_valid():
participant, created = Participant.objects.get_or_create(
contest=self.contest, user=request.user
)
self.handle_validated_form(request, form, participant)
if 'next' in request.GET:
return safe_redirect(request, request.GET['next'])
else:
return redirect('default_contest_view', contest_id=self.contest.id)
can_unregister = False
if participant:
can_unregister = self.can_unregister(request, participant)
context = {
'form': form,
'participant': participant,
'can_unregister': can_unregister,
}
return TemplateResponse(request, self.registration_template, context)
[docs] def get_terms_accepted_phrase(self):
try:
return self.contest.terms_accepted_phrase
except TermsAcceptedPhrase.DoesNotExist:
return None
[docs] def can_change_terms_accepted_phrase(self, request):
"""
:return: Whether the given contest has custom registered
participants (like the ones in OI and PA).
Then and only then we allow to change terms accepted phrase.
"""
return True
[docs] def is_registration_open(self, request):
try:
rvc = RegistrationAvailabilityConfig.objects.get(contest=request.contest)
return rvc.is_registration_open(request.timestamp)
except RegistrationAvailabilityConfig.DoesNotExist:
auditLogger.warning("RegistrationAvailabilityConfig does not exist for contest %s", request.contest)
return True
[docs]class OpenParticipantsController(ParticipantsController):
@property
@classmethod
[docs] def anonymous_can_enter_contest(cls):
return True
[docs] def allow_login_as_public_name(self):
return True
# Redundant because of filter_visible_contests, but saves a db query
[docs] def can_enter_contest(self, request):
return True
[docs] def visible_contests_query(self, request):
return Q_always_true()
[docs] def can_register(self, request):
return True
[docs] def can_unregister(self, request, participant):
return False
@request_cached
[docs]def anonymous_participants(request):
if not hasattr(request, 'contest'):
return frozenset({})
return frozenset(
(
p.user
for p in Participant.objects.filter(
contest=request.contest, anonymous=True
).select_related('user')
)
)
[docs]class EmailShowContestControllerMixin(object):
"""Contest controller defines whether in participants' data view email
should be shown. That is a case in OI-type contest.
"""
[docs] show_email_in_participants_data = False
ContestController.mix_in(EmailShowContestControllerMixin)
[docs]class AnonymousContestControllerMixin(object):
"""ContestController mixin that adds participants info for anonymous
contests.
"""
[docs] def get_user_public_name(self, request, user):
assert self.contest == request.contest
if (
request.user.is_superuser
or can_see_personal_data(request)
or user not in anonymous_participants(request)
):
return get_user_display_name(user)
else:
return user.username
[docs] def get_contest_participant_info_list(self, request, user):
prev = super(
AnonymousContestControllerMixin, self
).get_contest_participant_info_list(request, user)
try:
part = Participant.objects.get(user=user, contest=request.contest)
context = {'participant': part}
rendered_info = render_to_string(
'participants/participant_info.html', context=context, request=request
)
prev.append((98, rendered_info))
except Participant.DoesNotExist:
pass
return prev
ContestController.mix_in(AnonymousContestControllerMixin)
[docs]class OnsiteRegistrationController(ParticipantsController):
@property
[docs] def participant_admin(self):
from oioioi.participants.admin import OnsiteRegistrationParticipantAdmin
return OnsiteRegistrationParticipantAdmin
[docs] def get_model_class(self):
return OnsiteRegistration
[docs] def can_register(self, request):
return False
[docs] def can_edit_registration(self, request, participant):
return False
[docs] def get_contest_participant_info_list(self, request, user):
prev = super(
OnsiteRegistrationController, self
).get_contest_participant_info_list(request, user)
info = OnsiteRegistration.objects.filter(
participant__user=user, participant__contest=request.contest
)
if info.exists():
context = {'model': info[0]}
rendered_info = render_to_string(
'oi/participant_info.html', context=context, request=request
)
prev.append((98, rendered_info))
return prev
[docs]class OnsiteContestControllerMixin(object):
"""ContestController mixin that sets up an onsite contest."""
[docs] def registration_controller(self):
return OnsiteRegistrationController(self.contest)
[docs] def should_confirm_submission_receipt(self, request, submission):
return False
[docs] def is_onsite(self):
return True