Code documentation improvements

This commit is contained in:
Raphael Michel
2015-01-07 18:28:05 +01:00
parent 48109720cc
commit a5a976b16a
16 changed files with 136 additions and 64 deletions

View File

@@ -15,8 +15,9 @@ class EventRelatedCache:
main purpose of this is to be able to flush all cached data related main purpose of this is to be able to flush all cached data related
to this event at once. to this event at once.
The object is stateless, all state is in the cache, so you can The EventRelatedCache instance itself is stateless, all state is
instantiate it as many times as you want. stored in the cache backend, so you can instantiate this class as many
times as you want.
""" """
def __init__(self, event: Event, cache: str='default'): def __init__(self, event: Event, cache: str='default'):

View File

@@ -4,6 +4,9 @@ from versions.models import Versionable
class VersionedBaseModelForm(BaseModelForm): class VersionedBaseModelForm(BaseModelForm):
"""
This is a helperclass to construct VersionedModelForm
"""
def save(self, commit=True): def save(self, commit=True):
if self.instance.pk is not None and isinstance(self.instance, Versionable): if self.instance.pk is not None and isinstance(self.instance, Versionable):
if self.has_changed(): if self.has_changed():
@@ -12,4 +15,11 @@ class VersionedBaseModelForm(BaseModelForm):
class VersionedModelForm(six.with_metaclass(ModelFormMetaclass, VersionedBaseModelForm)): class VersionedModelForm(six.with_metaclass(ModelFormMetaclass, VersionedBaseModelForm)):
"""
This is a modified version of Django's ModelForm which differs from ModelForm in
only one way: It executes the .clone() method of an object before saving it back to
the database, if the model is a sub-class of versions.models.Versionable. You can
safely use this as a base class for all your model forms, it will work out correctly
with both versioned and non-versioned models.
"""
pass pass

View File

@@ -349,7 +349,8 @@ class EventPermission(Versionable):
class ItemCategory(Versionable): class ItemCategory(Versionable):
""" """
Items can be sorted into categories Items can be sorted into categories, which only have a name and a
configurable order
""" """
event = VersionedForeignKey( event = VersionedForeignKey(
Event, Event,
@@ -385,9 +386,8 @@ class ItemCategory(Versionable):
class Property(Versionable): class Property(Versionable):
""" """
A property is a modifier which can be applied to an A property is a modifier which can be applied to an Item. For example
Item. For example 'Size' would be a property associated 'Size' would be a property associated with the item 'T-Shirt'.
with the item 'T-Shirt'.
""" """
event = VersionedForeignKey( event = VersionedForeignKey(

View File

@@ -1,7 +1,7 @@
try: # NOQA try: # NOQA
from enum import Enum from enum import Enum
except ImportError: # NOQA except ImportError: # NOQA
from flufl.enum import Enum from flufl.enum import Enum # remove this dependency when support for python <=3.3 is dropped
from django.apps import apps from django.apps import apps
@@ -10,7 +10,10 @@ class PluginType(Enum):
RESTRICTION = 1 RESTRICTION = 1
def get_all_plugins() -> "class": def get_all_plugins() -> "List[class]":
"""
Returns the TixlPluginMeta classes of all plugins found in the installed Django apps.
"""
plugins = [] plugins = []
for app in apps.get_app_configs(): for app in apps.get_app_configs():
if hasattr(app, 'TixlPluginMeta'): if hasattr(app, 'TixlPluginMeta'):

View File

@@ -4,6 +4,11 @@ from django.dispatch.dispatcher import NO_RECEIVERS
class EventPluginSignal(django.dispatch.Signal): class EventPluginSignal(django.dispatch.Signal):
"""
This is an extension to Django's built-in signals which differs in a way that it sends
out it's events only to receivers which belong to plugins that are enabled for the given
Event.
"""
def send(self, sender, **named): def send(self, sender, **named):
""" """
@@ -34,6 +39,11 @@ class EventPluginSignal(django.dispatch.Signal):
responses.append((receiver, response)) responses.append((receiver, response))
return responses return responses
"""
This signal is sent out every time some component of tixl wants to know whether a specific
item or variation is available for sell. The item will only be sold, if all (active) receivers
return a positive result (see plugin API documentation for details).
"""
determine_availability = EventPluginSignal( determine_availability = EventPluginSignal(
providing_args=["item", "variations", "context", "cache"] providing_args=["item", "variations", "context", "cache"]
) )

View File

View File

@@ -1,3 +0,0 @@
from django.contrib import admin
# Register your models here.

View File

@@ -3,6 +3,9 @@ from django.core.urlresolvers import resolve
def contextprocessor(request): def contextprocessor(request):
"""
Adds data to all template contexts
"""
ctx = { ctx = {
'url_name': resolve(request.path_info).url_name, 'url_name': resolve(request.path_info).url_name,
'settings': settings, 'settings': settings,

View File

@@ -13,8 +13,7 @@ from tixlbase.models import Event
class PermissionMiddleware: class PermissionMiddleware:
""" """
This middleware enforces all requests to the control app This middleware enforces all requests to the control app to require login.
to require login.
Additionally, it enforces all requests to "control:event." URLs Additionally, it enforces all requests to "control:event." URLs
to be for an event the user has basic access to. to be for an event the user has basic access to.
""" """
@@ -56,4 +55,5 @@ class PermissionMiddleware:
organizer__slug=url.kwargs['organizer'], organizer__slug=url.kwargs['organizer'],
) )
except Event.DoesNotExist: except Event.DoesNotExist:
return HttpResponseNotFound(_("The selected event was not found or you have no permission to administrate it.")) return HttpResponseNotFound(_("The selected event was not found or you "
"have no permission to administrate it."))

View File

@@ -5,6 +5,10 @@ from tixlbase.models import EventPermission
def event_permission_required(permission): def event_permission_required(permission):
"""
This view decorator rejects all requests with a 403 response which are not from
users having the given permission for the event the request is associated with.
"""
def decorator(function): def decorator(function):
def wrapper(request, *args, **kw): def wrapper(request, *args, **kw):
if not request.user.is_authenticated(): if not request.user.is_authenticated():
@@ -26,6 +30,10 @@ def event_permission_required(permission):
class EventPermissionRequiredMixin: class EventPermissionRequiredMixin:
"""
This mixin is equivalent to the event_permission_required view decorator but
is in a form suitable for class-based views.
"""
permission = '' permission = ''
@classmethod @classmethod

View File

@@ -1,6 +1,10 @@
from tixlbase.signals import EventPluginSignal from tixlbase.signals import EventPluginSignal
"""
This signal is sent out to build configuration forms for all restriction formsets
(see plugin API documentation for details).
"""
restriction_formset = EventPluginSignal( restriction_formset = EventPluginSignal(
providing_args=["item"] providing_args=["item"]
) )

View File

@@ -22,21 +22,30 @@ urlpatterns += patterns(
url(r'^settings/plugins$', event.EventPlugins.as_view(), name='event.settings.plugins'), url(r'^settings/plugins$', event.EventPlugins.as_view(), name='event.settings.plugins'),
url(r'^items/$', item.ItemList.as_view(), name='event.items'), url(r'^items/$', item.ItemList.as_view(), name='event.items'),
url(r'^items/(?P<item>[0-9a-f-]+)/$', item.ItemUpdateGeneral.as_view(), name='event.item'), url(r'^items/(?P<item>[0-9a-f-]+)/$', item.ItemUpdateGeneral.as_view(), name='event.item'),
url(r'^items/(?P<item>[0-9a-f-]+)/variations$', item.ItemVariations.as_view(), name='event.item.variations'), url(r'^items/(?P<item>[0-9a-f-]+)/variations$', item.ItemVariations.as_view(),
url(r'^items/(?P<item>[0-9a-f-]+)/restrictions$', item.ItemRestrictions.as_view(), name='event.item.restrictions'), name='event.item.variations'),
url(r'^items/(?P<item>[0-9a-f-]+)/restrictions$', item.ItemRestrictions.as_view(),
name='event.item.restrictions'),
url(r'^categories/$', item.CategoryList.as_view(), name='event.items.categories'), url(r'^categories/$', item.CategoryList.as_view(), name='event.items.categories'),
url(r'^categories/(?P<category>[0-9a-f-]+)/delete$', item.CategoryDelete.as_view(), name='event.items.categories.delete'), url(r'^categories/(?P<category>[0-9a-f-]+)/delete$', item.CategoryDelete.as_view(),
name='event.items.categories.delete'),
url(r'^categories/(?P<category>[0-9a-f-]+)/up$', item.category_move_up, name='event.items.categories.up'), url(r'^categories/(?P<category>[0-9a-f-]+)/up$', item.category_move_up, name='event.items.categories.up'),
url(r'^categories/(?P<category>[0-9a-f-]+)/down$', item.category_move_down, name='event.items.categories.down'), url(r'^categories/(?P<category>[0-9a-f-]+)/down$', item.category_move_down,
url(r'^categories/(?P<category>[0-9a-f-]+)/$', item.CategoryUpdate.as_view(), name='event.items.categories.edit'), name='event.items.categories.down'),
url(r'^categories/(?P<category>[0-9a-f-]+)/$', item.CategoryUpdate.as_view(),
name='event.items.categories.edit'),
url(r'^categories/add$', item.CategoryCreate.as_view(), name='event.items.categories.add'), url(r'^categories/add$', item.CategoryCreate.as_view(), name='event.items.categories.add'),
url(r'^questions/$', item.QuestionList.as_view(), name='event.items.questions'), url(r'^questions/$', item.QuestionList.as_view(), name='event.items.questions'),
url(r'^questions/(?P<question>[0-9a-f-]+)/delete$', item.QuestionDelete.as_view(), name='event.items.questions.delete'), url(r'^questions/(?P<question>[0-9a-f-]+)/delete$', item.QuestionDelete.as_view(),
url(r'^questions/(?P<question>[0-9a-f-]+)/$', item.QuestionUpdate.as_view(), name='event.items.questions.edit'), name='event.items.questions.delete'),
url(r'^questions/(?P<question>[0-9a-f-]+)/$', item.QuestionUpdate.as_view(),
name='event.items.questions.edit'),
url(r'^questions/add$', item.QuestionCreate.as_view(), name='event.items.questions.add'), url(r'^questions/add$', item.QuestionCreate.as_view(), name='event.items.questions.add'),
url(r'^properties/$', item.PropertyList.as_view(), name='event.items.properties'), url(r'^properties/$', item.PropertyList.as_view(), name='event.items.properties'),
url(r'^properties/(?P<property>[0-9a-f-]+)/$', item.PropertyUpdate.as_view(), name='event.items.properties.edit'), url(r'^properties/(?P<property>[0-9a-f-]+)/$', item.PropertyUpdate.as_view(),
url(r'^properties/(?P<property>[0-9a-f-]+)/delete$', item.PropertyDelete.as_view(), name='event.items.properties.delete'), name='event.items.properties.edit'),
url(r'^properties/(?P<property>[0-9a-f-]+)/delete$', item.PropertyDelete.as_view(),
name='event.items.properties.delete'),
url(r'^properties/add$', item.PropertyCreate.as_view(), name='event.items.properties.add'), url(r'^properties/add$', item.PropertyCreate.as_view(), name='event.items.properties.add'),
url(r'^quotas/$', item.QuotaList.as_view(), name='event.items.quotas'), url(r'^quotas/$', item.QuotaList.as_view(), name='event.items.quotas'),
url(r'^quotas/(?P<quota>[0-9a-f-]+)/$', item.QuotaUpdate.as_view(), name='event.items.quotas.edit'), url(r'^quotas/(?P<quota>[0-9a-f-]+)/$', item.QuotaUpdate.as_view(), name='event.items.quotas.edit'),

View File

@@ -9,8 +9,8 @@ from django.contrib.auth import logout as auth_logout
class AuthenticationForm(BaseAuthenticationForm): class AuthenticationForm(BaseAuthenticationForm):
""" """
The login form, providing an email and password field. The The login form, providing an email and password field. The form already implements
form does already implement validation for correct user data. validation for correct user data.
""" """
email = forms.EmailField(label=_("E-mail address"), max_length=254) email = forms.EmailField(label=_("E-mail address"), max_length=254)
password = forms.CharField(label=_("Password"), widget=forms.PasswordInput) password = forms.CharField(label=_("Password"), widget=forms.PasswordInput)
@@ -68,5 +68,8 @@ def login(request):
def logout(request): def logout(request):
"""
Log the user out of the current session, then redirect to login page.
"""
auth_logout(request) auth_logout(request)
return redirect('control:auth.login') return redirect('control:auth.login')

View File

@@ -70,7 +70,7 @@ class EventPlugins(EventPermissionRequiredMixin, TemplateView, SingleObjectMixin
permission = 'can_change_settings' permission = 'can_change_settings'
template_name = 'tixlcontrol/event/plugins.html' template_name = 'tixlcontrol/event/plugins.html'
def get_object(self, queryset=None): def get_object(self, queryset=None) -> Event:
return self.request.event return self.request.event
def get_context_data(self, *args, **kwargs) -> dict: def get_context_data(self, *args, **kwargs) -> dict:

View File

@@ -13,6 +13,14 @@ from tixlbase.models import ItemVariation, PropertyValue, Item
class TolerantFormsetModelForm(VersionedModelForm): class TolerantFormsetModelForm(VersionedModelForm):
"""
This is equivalent to a normal VersionedModelForm, but works around a problem that
arises when the form is used inside a FormSet with can_order=True and django-formset-js
enabled. In this configuration, even empty "extra" forms might have an ORDER value
sent and Django marks the form as empty and raises validation errors because the other
fields have not been filled.
"""
def has_changed(self) -> bool: def has_changed(self) -> bool:
""" """
Returns True if data differs from initial. Contrary to the default Returns True if data differs from initial. Contrary to the default
@@ -99,6 +107,10 @@ class RestrictionInlineFormset(forms.BaseInlineFormSet):
class VariationsFieldRenderer(forms.widgets.CheckboxFieldRenderer): class VariationsFieldRenderer(forms.widgets.CheckboxFieldRenderer):
"""
This is the default renderer for a VariationsField. Based on the choice input class
this renders a list or a matrix of checkboxes/radio buttons/...
"""
def __init__(self, name, value, attrs, choices): def __init__(self, name, value, attrs, choices):
self.name = name self.name = name
@@ -207,10 +219,17 @@ class VariationsFieldRenderer(forms.widgets.CheckboxFieldRenderer):
class VariationsCheckboxRenderer(VariationsFieldRenderer): class VariationsCheckboxRenderer(VariationsFieldRenderer):
"""
This is the same as VariationsFieldRenderer but with the choice input class
forced to checkboxes
"""
choice_input_class = forms.widgets.CheckboxChoiceInput choice_input_class = forms.widgets.CheckboxChoiceInput
class VariationsSelectMultiple(forms.CheckboxSelectMultiple): class VariationsSelectMultiple(forms.CheckboxSelectMultiple):
"""
This is the default widget for a VariationsField
"""
renderer = VariationsCheckboxRenderer renderer = VariationsCheckboxRenderer
_empty_value = [] _empty_value = []
@@ -240,7 +259,7 @@ class VariationsField(forms.ModelMultipleChoiceField):
""" """
We can't use a normal QuerySet as there theoretically might be We can't use a normal QuerySet as there theoretically might be
two types of variations: Some who already have a ItemVariation two types of variations: Some who already have a ItemVariation
object associated with tham and some who don't. We therefore use object associated with them and some who don't. We therefore use
the item's ``get_all_variations`` method. In the first case, we the item's ``get_all_variations`` method. In the first case, we
use the ItemVariation objects primary key as our choice, key, use the ItemVariation objects primary key as our choice, key,
in the latter case we use a string constructed from the values in the latter case we use a string constructed from the values

View File

@@ -47,7 +47,7 @@ class CategoryDelete(EventPermissionRequiredMixin, DeleteView):
permission = 'can_change_items' permission = 'can_change_items'
context_object_name = 'category' context_object_name = 'category'
def get_object(self, queryset=None): def get_object(self, queryset=None) -> ItemCategory:
url = resolve(self.request.path_info) url = resolve(self.request.path_info)
return self.request.event.categories.current.get( return self.request.event.categories.current.get(
identity=url.kwargs['category'] identity=url.kwargs['category']
@@ -62,7 +62,7 @@ class CategoryDelete(EventPermissionRequiredMixin, DeleteView):
self.object.delete() self.object.delete()
return HttpResponseRedirect(success_url) return HttpResponseRedirect(success_url)
def get_success_url(self): def get_success_url(self) -> str:
return reverse('control:event.items.categories', kwargs={ return reverse('control:event.items.categories', kwargs={
'organizer': self.request.event.organizer.slug, 'organizer': self.request.event.organizer.slug,
'event': self.request.event.slug, 'event': self.request.event.slug,
@@ -76,13 +76,13 @@ class CategoryUpdate(EventPermissionRequiredMixin, UpdateView):
permission = 'can_change_items' permission = 'can_change_items'
context_object_name = 'category' context_object_name = 'category'
def get_object(self, queryset=None): def get_object(self, queryset=None) -> ItemCategory:
url = resolve(self.request.path_info) url = resolve(self.request.path_info)
return self.request.event.categories.current.get( return self.request.event.categories.current.get(
identity=url.kwargs['category'] identity=url.kwargs['category']
) )
def get_success_url(self): def get_success_url(self) -> str:
return reverse('control:event.items.categories', kwargs={ return reverse('control:event.items.categories', kwargs={
'organizer': self.request.event.organizer.slug, 'organizer': self.request.event.organizer.slug,
'event': self.request.event.slug, 'event': self.request.event.slug,
@@ -96,7 +96,7 @@ class CategoryCreate(EventPermissionRequiredMixin, CreateView):
permission = 'can_change_items' permission = 'can_change_items'
context_object_name = 'category' context_object_name = 'category'
def get_success_url(self): def get_success_url(self) -> str:
return reverse('control:event.items.categories', kwargs={ return reverse('control:event.items.categories', kwargs={
'organizer': self.request.event.organizer.slug, 'organizer': self.request.event.organizer.slug,
'event': self.request.event.slug, 'event': self.request.event.slug,
@@ -116,7 +116,12 @@ class CategoryList(ListView):
return self.request.event.categories.current.all() return self.request.event.categories.current.all()
def category_move(request, organizer, event, category, up=True): def category_move(request, category, up=True):
"""
This is a helper function to avoid duplicating code in category_move_up and
category_move_down. It takes a category and a direction and then tries to bring
all categories for this event in a new order.
"""
category = request.event.categories.current.get( category = request.event.categories.current.get(
identity=category identity=category
) )
@@ -136,7 +141,7 @@ def category_move(request, organizer, event, category, up=True):
@event_permission_required("can_change_items") @event_permission_required("can_change_items")
def category_move_up(request, organizer, event, category): def category_move_up(request, organizer, event, category):
category_move(request, organizer, event, category, up=True) category_move(request, category, up=True)
return redirect(reverse('control:event.items.categories', kwargs={ return redirect(reverse('control:event.items.categories', kwargs={
'organizer': request.event.organizer.slug, 'organizer': request.event.organizer.slug,
'event': request.event.slug, 'event': request.event.slug,
@@ -145,7 +150,7 @@ def category_move_up(request, organizer, event, category):
@event_permission_required("can_change_items") @event_permission_required("can_change_items")
def category_move_down(request, organizer, event, category): def category_move_down(request, organizer, event, category):
category_move(request, organizer, event, category, up=False) category_move(request, category, up=False)
return redirect(reverse('control:event.items.categories', kwargs={ return redirect(reverse('control:event.items.categories', kwargs={
'organizer': request.event.organizer.slug, 'organizer': request.event.organizer.slug,
'event': request.event.slug, 'event': request.event.slug,
@@ -188,13 +193,13 @@ class PropertyUpdate(EventPermissionRequiredMixin, UpdateView):
permission = 'can_change_items' permission = 'can_change_items'
context_object_name = 'property' context_object_name = 'property'
def get_object(self, queryset=None): def get_object(self, queryset=None) -> Property:
url = resolve(self.request.path_info) url = resolve(self.request.path_info)
return self.request.event.properties.current.get( return self.request.event.properties.current.get(
identity=url.kwargs['property'] identity=url.kwargs['property']
) )
def get_success_url(self): def get_success_url(self) -> str:
url = resolve(self.request.path_info) url = resolve(self.request.path_info)
return reverse('control:event.items.properties.edit', kwargs={ return reverse('control:event.items.properties.edit', kwargs={
'organizer': self.request.event.organizer.slug, 'organizer': self.request.event.organizer.slug,
@@ -214,7 +219,7 @@ class PropertyUpdate(EventPermissionRequiredMixin, UpdateView):
formset = formsetclass(**kwargs) formset = formsetclass(**kwargs)
return formset return formset
def get_context_data(self, *args, **kwargs): def get_context_data(self, *args, **kwargs) -> dict:
context = super().get_context_data(*args, **kwargs) context = super().get_context_data(*args, **kwargs)
context['formset'] = self.get_formset() context['formset'] = self.get_formset()
return context return context
@@ -249,7 +254,7 @@ class PropertyCreate(EventPermissionRequiredMixin, CreateView):
permission = 'can_change_items' permission = 'can_change_items'
context_object_name = 'property' context_object_name = 'property'
def get_success_url(self): def get_success_url(self) -> str:
return reverse('control:event.items.properties', kwargs={ return reverse('control:event.items.properties', kwargs={
'organizer': self.request.event.organizer.slug, 'organizer': self.request.event.organizer.slug,
'event': self.request.event.slug, 'event': self.request.event.slug,
@@ -265,7 +270,7 @@ class PropertyCreate(EventPermissionRequiredMixin, CreateView):
formset = formsetclass(**self.get_form_kwargs()) formset = formsetclass(**self.get_form_kwargs())
return formset return formset
def get_context_data(self, *args, **kwargs): def get_context_data(self, *args, **kwargs) -> dict:
self.object = None self.object = None
context = super().get_context_data(*args, **kwargs) context = super().get_context_data(*args, **kwargs)
context['formset'] = self.get_formset() context['formset'] = self.get_formset()
@@ -297,16 +302,16 @@ class PropertyDelete(EventPermissionRequiredMixin, DeleteView):
permission = 'can_change_items' permission = 'can_change_items'
context_object_name = 'property' context_object_name = 'property'
def get_context_data(self, *args, **kwargs): def get_context_data(self, *args, **kwargs) -> dict:
context = super().get_context_data(*args, **kwargs) context = super().get_context_data(*args, **kwargs)
context['dependent'] = self.get_object().items.current.all() context['dependent'] = self.get_object().items.current.all()
context['possible'] = self.is_allowed() context['possible'] = self.is_allowed()
return context return context
def is_allowed(self): def is_allowed(self) -> bool:
return self.get_object().items.current.count() == 0 return self.get_object().items.current.count() == 0
def get_object(self, queryset=None): def get_object(self, queryset=None) -> Property:
if not hasattr(self, 'object') or not self.object: if not hasattr(self, 'object') or not self.object:
url = resolve(self.request.path_info) url = resolve(self.request.path_info)
self.object = self.request.event.properties.current.get( self.object = self.request.event.properties.current.get(
@@ -322,7 +327,7 @@ class PropertyDelete(EventPermissionRequiredMixin, DeleteView):
else: else:
return HttpResponseForbidden() return HttpResponseForbidden()
def get_success_url(self): def get_success_url(self) -> str:
return reverse('control:event.items.properties', kwargs={ return reverse('control:event.items.properties', kwargs={
'organizer': self.request.event.organizer.slug, 'organizer': self.request.event.organizer.slug,
'event': self.request.event.slug, 'event': self.request.event.slug,
@@ -356,13 +361,13 @@ class QuestionDelete(EventPermissionRequiredMixin, DeleteView):
permission = 'can_change_items' permission = 'can_change_items'
context_object_name = 'question' context_object_name = 'question'
def get_object(self, queryset=None): def get_object(self, queryset=None) -> Question:
url = resolve(self.request.path_info) url = resolve(self.request.path_info)
return self.request.event.questions.current.get( return self.request.event.questions.current.get(
identity=url.kwargs['question'] identity=url.kwargs['question']
) )
def get_context_data(self, *args, **kwargs): def get_context_data(self, *args, **kwargs) -> dict:
context = super().get_context_data(*args, **kwargs) context = super().get_context_data(*args, **kwargs)
context['dependent'] = list(self.get_object().items.current.all()) context['dependent'] = list(self.get_object().items.current.all())
return context return context
@@ -373,7 +378,7 @@ class QuestionDelete(EventPermissionRequiredMixin, DeleteView):
self.object.delete() self.object.delete()
return HttpResponseRedirect(success_url) return HttpResponseRedirect(success_url)
def get_success_url(self): def get_success_url(self) -> str:
return reverse('control:event.items.questions', kwargs={ return reverse('control:event.items.questions', kwargs={
'organizer': self.request.event.organizer.slug, 'organizer': self.request.event.organizer.slug,
'event': self.request.event.slug, 'event': self.request.event.slug,
@@ -387,13 +392,13 @@ class QuestionUpdate(EventPermissionRequiredMixin, UpdateView):
permission = 'can_change_items' permission = 'can_change_items'
context_object_name = 'question' context_object_name = 'question'
def get_object(self, queryset=None): def get_object(self, queryset=None) -> Question:
url = resolve(self.request.path_info) url = resolve(self.request.path_info)
return self.request.event.questions.current.get( return self.request.event.questions.current.get(
identity=url.kwargs['question'] identity=url.kwargs['question']
) )
def get_success_url(self): def get_success_url(self) -> str:
return reverse('control:event.items.questions', kwargs={ return reverse('control:event.items.questions', kwargs={
'organizer': self.request.event.organizer.slug, 'organizer': self.request.event.organizer.slug,
'event': self.request.event.slug, 'event': self.request.event.slug,
@@ -407,7 +412,7 @@ class QuestionCreate(EventPermissionRequiredMixin, CreateView):
permission = 'can_change_items' permission = 'can_change_items'
context_object_name = 'question' context_object_name = 'question'
def get_success_url(self): def get_success_url(self) -> str:
return reverse('control:event.items.questions', kwargs={ return reverse('control:event.items.questions', kwargs={
'organizer': self.request.event.organizer.slug, 'organizer': self.request.event.organizer.slug,
'event': self.request.event.slug, 'event': self.request.event.slug,
@@ -447,7 +452,7 @@ class QuotaCreate(EventPermissionRequiredMixin, CreateView):
permission = 'can_change_items' permission = 'can_change_items'
context_object_name = 'quota' context_object_name = 'quota'
def get_success_url(self): def get_success_url(self) -> str:
return reverse('control:event.items.quotas', kwargs={ return reverse('control:event.items.quotas', kwargs={
'organizer': self.request.event.organizer.slug, 'organizer': self.request.event.organizer.slug,
'event': self.request.event.slug, 'event': self.request.event.slug,
@@ -465,13 +470,13 @@ class QuotaUpdate(EventPermissionRequiredMixin, UpdateView):
permission = 'can_change_items' permission = 'can_change_items'
context_object_name = 'quota' context_object_name = 'quota'
def get_object(self, queryset=None): def get_object(self, queryset=None) -> Quota:
url = resolve(self.request.path_info) url = resolve(self.request.path_info)
return self.request.event.quotas.current.get( return self.request.event.quotas.current.get(
identity=url.kwargs['quota'] identity=url.kwargs['quota']
) )
def get_success_url(self): def get_success_url(self) -> str:
return reverse('control:event.items.quotas', kwargs={ return reverse('control:event.items.quotas', kwargs={
'organizer': self.request.event.organizer.slug, 'organizer': self.request.event.organizer.slug,
'event': self.request.event.slug, 'event': self.request.event.slug,
@@ -484,13 +489,13 @@ class QuotaDelete(EventPermissionRequiredMixin, DeleteView):
permission = 'can_change_items' permission = 'can_change_items'
context_object_name = 'quota' context_object_name = 'quota'
def get_object(self, queryset=None): def get_object(self, queryset=None) -> Quota:
url = resolve(self.request.path_info) url = resolve(self.request.path_info)
return self.request.event.quotas.current.get( return self.request.event.quotas.current.get(
identity=url.kwargs['quota'] identity=url.kwargs['quota']
) )
def get_context_data(self, *args, **kwargs): def get_context_data(self, *args, **kwargs) -> dict:
context = super().get_context_data(*args, **kwargs) context = super().get_context_data(*args, **kwargs)
context['dependent'] = list(self.get_object().items.current.all()) context['dependent'] = list(self.get_object().items.current.all())
return context return context
@@ -501,7 +506,7 @@ class QuotaDelete(EventPermissionRequiredMixin, DeleteView):
self.object.delete() self.object.delete()
return HttpResponseRedirect(success_url) return HttpResponseRedirect(success_url)
def get_success_url(self): def get_success_url(self) -> str:
return reverse('control:event.items.quotas', kwargs={ return reverse('control:event.items.quotas', kwargs={
'organizer': self.request.event.organizer.slug, 'organizer': self.request.event.organizer.slug,
'event': self.request.event.slug, 'event': self.request.event.slug,
@@ -512,7 +517,7 @@ class ItemDetailMixin(SingleObjectMixin):
model = Item model = Item
context_object_name = 'item' context_object_name = 'item'
def get_object(self, queryset=None): def get_object(self, queryset=None) -> Item:
if not hasattr(self, 'object') or not self.object: if not hasattr(self, 'object') or not self.object:
url = resolve(self.request.path_info) url = resolve(self.request.path_info)
self.item = self.request.event.items.current.get( self.item = self.request.event.items.current.get(
@@ -551,7 +556,7 @@ class ItemUpdateGeneral(ItemDetailMixin, EventPermissionRequiredMixin, UpdateVie
template_name = 'tixlcontrol/item/index.html' template_name = 'tixlcontrol/item/index.html'
permission = 'can_change_items' permission = 'can_change_items'
def get_success_url(self): def get_success_url(self) -> str:
return reverse('control:event.item', kwargs={ return reverse('control:event.item', kwargs={
'organizer': self.request.event.organizer.slug, 'organizer': self.request.event.organizer.slug,
'event': self.request.event.slug, 'event': self.request.event.slug,
@@ -578,7 +583,7 @@ class ItemVariations(ItemDetailMixin, EventPermissionRequiredMixin, TemplateView
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.item = None self.item = None
def get_form(self, variation, data=None): def get_form(self, variation, data=None) -> ItemVariationForm:
""" """
Return the dict for one given variation. Variations are expected to be Return the dict for one given variation. Variations are expected to be
dictionaries in the format of Item.get_all_variations() dictionaries in the format of Item.get_all_variations()
@@ -603,7 +608,7 @@ class ItemVariations(ItemDetailMixin, EventPermissionRequiredMixin, TemplateView
form.values = values form.values = values
return form return form
def get_forms(self): def get_forms(self) -> tuple:
""" """
Returns one form per possible item variation. The forms are returned Returns one form per possible item variation. The forms are returned
twice: The first entry in the returned tuple contains a 1-, 2- or twice: The first entry in the returned tuple contains a 1-, 2- or
@@ -705,7 +710,7 @@ class ItemVariations(ItemDetailMixin, EventPermissionRequiredMixin, TemplateView
# TODO: Redirect to success message # TODO: Redirect to success message
return self.render_to_response(context) return self.render_to_response(context)
def get_template_names(self): def get_template_names(self) -> "List[str]":
if self.dimension == 0: if self.dimension == 0:
return ['tixlcontrol/item/variations_0d.html'] return ['tixlcontrol/item/variations_0d.html']
elif self.dimension == 1: elif self.dimension == 1:
@@ -713,7 +718,7 @@ class ItemVariations(ItemDetailMixin, EventPermissionRequiredMixin, TemplateView
elif self.dimension >= 2: elif self.dimension >= 2:
return ['tixlcontrol/item/variations_nd.html'] return ['tixlcontrol/item/variations_nd.html']
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs) -> dict:
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
context['forms'] = self.forms context['forms'] = self.forms
context['properties'] = self.properties context['properties'] = self.properties
@@ -768,12 +773,12 @@ class ItemRestrictions(ItemDetailMixin, EventPermissionRequiredMixin, TemplateVi
context = self.get_context_data(object=self.object) context = self.get_context_data(object=self.object)
return self.render_to_response(context) return self.render_to_response(context)
def get_context_data(self, *args, **kwargs): def get_context_data(self, *args, **kwargs) -> dict:
context = super().get_context_data(*args, **kwargs) context = super().get_context_data(*args, **kwargs)
context['formsets'] = self.formsets context['formsets'] = self.formsets
return context return context
def get_success_url(self): def get_success_url(self) -> str:
return reverse('control:event.item.restrictions', kwargs={ return reverse('control:event.item.restrictions', kwargs={
'organizer': self.request.event.organizer.slug, 'organizer': self.request.event.organizer.slug,
'event': self.request.event.slug, 'event': self.request.event.slug,