diff --git a/src/tixlbase/cache.py b/src/tixlbase/cache.py index c84369b25..f6099d4b3 100644 --- a/src/tixlbase/cache.py +++ b/src/tixlbase/cache.py @@ -15,8 +15,9 @@ class EventRelatedCache: main purpose of this is to be able to flush all cached data related to this event at once. - The object is stateless, all state is in the cache, so you can - instantiate it as many times as you want. + The EventRelatedCache instance itself is stateless, all state is + 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'): diff --git a/src/tixlbase/forms.py b/src/tixlbase/forms.py index ae46dc43d..3b661dc4e 100644 --- a/src/tixlbase/forms.py +++ b/src/tixlbase/forms.py @@ -4,6 +4,9 @@ from versions.models import Versionable class VersionedBaseModelForm(BaseModelForm): + """ + This is a helperclass to construct VersionedModelForm + """ def save(self, commit=True): if self.instance.pk is not None and isinstance(self.instance, Versionable): if self.has_changed(): @@ -12,4 +15,11 @@ class VersionedBaseModelForm(BaseModelForm): 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 diff --git a/src/tixlbase/models.py b/src/tixlbase/models.py index b09b570d9..37c74278d 100644 --- a/src/tixlbase/models.py +++ b/src/tixlbase/models.py @@ -349,7 +349,8 @@ class EventPermission(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, @@ -385,9 +386,8 @@ class ItemCategory(Versionable): class Property(Versionable): """ - A property is a modifier which can be applied to an - Item. For example 'Size' would be a property associated - with the item 'T-Shirt'. + A property is a modifier which can be applied to an Item. For example + 'Size' would be a property associated with the item 'T-Shirt'. """ event = VersionedForeignKey( diff --git a/src/tixlbase/plugins.py b/src/tixlbase/plugins.py index ffe7856fd..a1814fffa 100644 --- a/src/tixlbase/plugins.py +++ b/src/tixlbase/plugins.py @@ -1,7 +1,7 @@ try: # NOQA from enum import Enum 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 @@ -10,7 +10,10 @@ class PluginType(Enum): 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 = [] for app in apps.get_app_configs(): if hasattr(app, 'TixlPluginMeta'): diff --git a/src/tixlbase/signals.py b/src/tixlbase/signals.py index cccaaa92e..36603fdd4 100644 --- a/src/tixlbase/signals.py +++ b/src/tixlbase/signals.py @@ -4,6 +4,11 @@ from django.dispatch.dispatcher import NO_RECEIVERS 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): """ @@ -34,6 +39,11 @@ class EventPluginSignal(django.dispatch.Signal): responses.append((receiver, response)) 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( providing_args=["item", "variations", "context", "cache"] ) diff --git a/src/tixlbase/views.py b/src/tixlbase/views.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/tixlcontrol/admin.py b/src/tixlcontrol/admin.py deleted file mode 100644 index 8c38f3f3d..000000000 --- a/src/tixlcontrol/admin.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.contrib import admin - -# Register your models here. diff --git a/src/tixlcontrol/context.py b/src/tixlcontrol/context.py index 3970ac98d..6c4d7b569 100644 --- a/src/tixlcontrol/context.py +++ b/src/tixlcontrol/context.py @@ -3,6 +3,9 @@ from django.core.urlresolvers import resolve def contextprocessor(request): + """ + Adds data to all template contexts + """ ctx = { 'url_name': resolve(request.path_info).url_name, 'settings': settings, diff --git a/src/tixlcontrol/middleware.py b/src/tixlcontrol/middleware.py index b38fa2fd5..d124a7bd7 100644 --- a/src/tixlcontrol/middleware.py +++ b/src/tixlcontrol/middleware.py @@ -13,8 +13,7 @@ from tixlbase.models import Event class PermissionMiddleware: """ - This middleware enforces all requests to the control app - to require login. + This middleware enforces all requests to the control app to require login. Additionally, it enforces all requests to "control:event." URLs to be for an event the user has basic access to. """ @@ -56,4 +55,5 @@ class PermissionMiddleware: organizer__slug=url.kwargs['organizer'], ) 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.")) diff --git a/src/tixlcontrol/permissions.py b/src/tixlcontrol/permissions.py index 64d1b0b3b..7b1f9a7c2 100644 --- a/src/tixlcontrol/permissions.py +++ b/src/tixlcontrol/permissions.py @@ -5,6 +5,10 @@ from tixlbase.models import EventPermission 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 wrapper(request, *args, **kw): if not request.user.is_authenticated(): @@ -26,6 +30,10 @@ def event_permission_required(permission): class EventPermissionRequiredMixin: + """ + This mixin is equivalent to the event_permission_required view decorator but + is in a form suitable for class-based views. + """ permission = '' @classmethod diff --git a/src/tixlcontrol/signals.py b/src/tixlcontrol/signals.py index 2d660d986..7f5d9ff73 100644 --- a/src/tixlcontrol/signals.py +++ b/src/tixlcontrol/signals.py @@ -1,6 +1,10 @@ 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( providing_args=["item"] ) diff --git a/src/tixlcontrol/urls.py b/src/tixlcontrol/urls.py index a4fd4cd10..ef9395106 100644 --- a/src/tixlcontrol/urls.py +++ b/src/tixlcontrol/urls.py @@ -22,21 +22,30 @@ urlpatterns += patterns( 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/(?P[0-9a-f-]+)/$', item.ItemUpdateGeneral.as_view(), name='event.item'), - url(r'^items/(?P[0-9a-f-]+)/variations$', item.ItemVariations.as_view(), name='event.item.variations'), - url(r'^items/(?P[0-9a-f-]+)/restrictions$', item.ItemRestrictions.as_view(), name='event.item.restrictions'), + url(r'^items/(?P[0-9a-f-]+)/variations$', item.ItemVariations.as_view(), + name='event.item.variations'), + url(r'^items/(?P[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/(?P[0-9a-f-]+)/delete$', item.CategoryDelete.as_view(), name='event.items.categories.delete'), + url(r'^categories/(?P[0-9a-f-]+)/delete$', item.CategoryDelete.as_view(), + name='event.items.categories.delete'), url(r'^categories/(?P[0-9a-f-]+)/up$', item.category_move_up, name='event.items.categories.up'), - url(r'^categories/(?P[0-9a-f-]+)/down$', item.category_move_down, name='event.items.categories.down'), - url(r'^categories/(?P[0-9a-f-]+)/$', item.CategoryUpdate.as_view(), name='event.items.categories.edit'), + url(r'^categories/(?P[0-9a-f-]+)/down$', item.category_move_down, + name='event.items.categories.down'), + url(r'^categories/(?P[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'^questions/$', item.QuestionList.as_view(), name='event.items.questions'), - url(r'^questions/(?P[0-9a-f-]+)/delete$', item.QuestionDelete.as_view(), name='event.items.questions.delete'), - url(r'^questions/(?P[0-9a-f-]+)/$', item.QuestionUpdate.as_view(), name='event.items.questions.edit'), + url(r'^questions/(?P[0-9a-f-]+)/delete$', item.QuestionDelete.as_view(), + name='event.items.questions.delete'), + url(r'^questions/(?P[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'^properties/$', item.PropertyList.as_view(), name='event.items.properties'), - url(r'^properties/(?P[0-9a-f-]+)/$', item.PropertyUpdate.as_view(), name='event.items.properties.edit'), - url(r'^properties/(?P[0-9a-f-]+)/delete$', item.PropertyDelete.as_view(), name='event.items.properties.delete'), + url(r'^properties/(?P[0-9a-f-]+)/$', item.PropertyUpdate.as_view(), + name='event.items.properties.edit'), + url(r'^properties/(?P[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'^quotas/$', item.QuotaList.as_view(), name='event.items.quotas'), url(r'^quotas/(?P[0-9a-f-]+)/$', item.QuotaUpdate.as_view(), name='event.items.quotas.edit'), diff --git a/src/tixlcontrol/views/auth.py b/src/tixlcontrol/views/auth.py index f66493114..56dcec29e 100644 --- a/src/tixlcontrol/views/auth.py +++ b/src/tixlcontrol/views/auth.py @@ -9,8 +9,8 @@ from django.contrib.auth import logout as auth_logout class AuthenticationForm(BaseAuthenticationForm): """ - The login form, providing an email and password field. The - form does already implement validation for correct user data. + The login form, providing an email and password field. The form already implements + validation for correct user data. """ email = forms.EmailField(label=_("E-mail address"), max_length=254) password = forms.CharField(label=_("Password"), widget=forms.PasswordInput) @@ -68,5 +68,8 @@ def login(request): def logout(request): + """ + Log the user out of the current session, then redirect to login page. + """ auth_logout(request) return redirect('control:auth.login') diff --git a/src/tixlcontrol/views/event.py b/src/tixlcontrol/views/event.py index 63e7f16f5..289664091 100644 --- a/src/tixlcontrol/views/event.py +++ b/src/tixlcontrol/views/event.py @@ -70,7 +70,7 @@ class EventPlugins(EventPermissionRequiredMixin, TemplateView, SingleObjectMixin permission = 'can_change_settings' template_name = 'tixlcontrol/event/plugins.html' - def get_object(self, queryset=None): + def get_object(self, queryset=None) -> Event: return self.request.event def get_context_data(self, *args, **kwargs) -> dict: diff --git a/src/tixlcontrol/views/forms.py b/src/tixlcontrol/views/forms.py index dd8fb80b4..640d78a58 100644 --- a/src/tixlcontrol/views/forms.py +++ b/src/tixlcontrol/views/forms.py @@ -13,6 +13,14 @@ from tixlbase.models import ItemVariation, PropertyValue, Item 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: """ Returns True if data differs from initial. Contrary to the default @@ -99,6 +107,10 @@ class RestrictionInlineFormset(forms.BaseInlineFormSet): 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): self.name = name @@ -207,10 +219,17 @@ class VariationsFieldRenderer(forms.widgets.CheckboxFieldRenderer): class VariationsCheckboxRenderer(VariationsFieldRenderer): + """ + This is the same as VariationsFieldRenderer but with the choice input class + forced to checkboxes + """ choice_input_class = forms.widgets.CheckboxChoiceInput class VariationsSelectMultiple(forms.CheckboxSelectMultiple): + """ + This is the default widget for a VariationsField + """ renderer = VariationsCheckboxRenderer _empty_value = [] @@ -240,7 +259,7 @@ class VariationsField(forms.ModelMultipleChoiceField): """ We can't use a normal QuerySet as there theoretically might be 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 use the ItemVariation objects primary key as our choice, key, in the latter case we use a string constructed from the values diff --git a/src/tixlcontrol/views/item.py b/src/tixlcontrol/views/item.py index 9afb31422..e47a7d637 100644 --- a/src/tixlcontrol/views/item.py +++ b/src/tixlcontrol/views/item.py @@ -47,7 +47,7 @@ class CategoryDelete(EventPermissionRequiredMixin, DeleteView): permission = 'can_change_items' context_object_name = 'category' - def get_object(self, queryset=None): + def get_object(self, queryset=None) -> ItemCategory: url = resolve(self.request.path_info) return self.request.event.categories.current.get( identity=url.kwargs['category'] @@ -62,7 +62,7 @@ class CategoryDelete(EventPermissionRequiredMixin, DeleteView): self.object.delete() return HttpResponseRedirect(success_url) - def get_success_url(self): + def get_success_url(self) -> str: return reverse('control:event.items.categories', kwargs={ 'organizer': self.request.event.organizer.slug, 'event': self.request.event.slug, @@ -76,13 +76,13 @@ class CategoryUpdate(EventPermissionRequiredMixin, UpdateView): permission = 'can_change_items' context_object_name = 'category' - def get_object(self, queryset=None): + def get_object(self, queryset=None) -> ItemCategory: url = resolve(self.request.path_info) return self.request.event.categories.current.get( identity=url.kwargs['category'] ) - def get_success_url(self): + def get_success_url(self) -> str: return reverse('control:event.items.categories', kwargs={ 'organizer': self.request.event.organizer.slug, 'event': self.request.event.slug, @@ -96,7 +96,7 @@ class CategoryCreate(EventPermissionRequiredMixin, CreateView): permission = 'can_change_items' context_object_name = 'category' - def get_success_url(self): + def get_success_url(self) -> str: return reverse('control:event.items.categories', kwargs={ 'organizer': self.request.event.organizer.slug, 'event': self.request.event.slug, @@ -116,7 +116,12 @@ class CategoryList(ListView): 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( identity=category ) @@ -136,7 +141,7 @@ def category_move(request, organizer, event, category, up=True): @event_permission_required("can_change_items") 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={ 'organizer': request.event.organizer.slug, 'event': request.event.slug, @@ -145,7 +150,7 @@ def category_move_up(request, organizer, event, category): @event_permission_required("can_change_items") 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={ 'organizer': request.event.organizer.slug, 'event': request.event.slug, @@ -188,13 +193,13 @@ class PropertyUpdate(EventPermissionRequiredMixin, UpdateView): permission = 'can_change_items' context_object_name = 'property' - def get_object(self, queryset=None): + def get_object(self, queryset=None) -> Property: url = resolve(self.request.path_info) return self.request.event.properties.current.get( identity=url.kwargs['property'] ) - def get_success_url(self): + def get_success_url(self) -> str: url = resolve(self.request.path_info) return reverse('control:event.items.properties.edit', kwargs={ 'organizer': self.request.event.organizer.slug, @@ -214,7 +219,7 @@ class PropertyUpdate(EventPermissionRequiredMixin, UpdateView): formset = formsetclass(**kwargs) 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['formset'] = self.get_formset() return context @@ -249,7 +254,7 @@ class PropertyCreate(EventPermissionRequiredMixin, CreateView): permission = 'can_change_items' context_object_name = 'property' - def get_success_url(self): + def get_success_url(self) -> str: return reverse('control:event.items.properties', kwargs={ 'organizer': self.request.event.organizer.slug, 'event': self.request.event.slug, @@ -265,7 +270,7 @@ class PropertyCreate(EventPermissionRequiredMixin, CreateView): formset = formsetclass(**self.get_form_kwargs()) return formset - def get_context_data(self, *args, **kwargs): + def get_context_data(self, *args, **kwargs) -> dict: self.object = None context = super().get_context_data(*args, **kwargs) context['formset'] = self.get_formset() @@ -297,16 +302,16 @@ class PropertyDelete(EventPermissionRequiredMixin, DeleteView): permission = 'can_change_items' 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['dependent'] = self.get_object().items.current.all() context['possible'] = self.is_allowed() return context - def is_allowed(self): + def is_allowed(self) -> bool: 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: url = resolve(self.request.path_info) self.object = self.request.event.properties.current.get( @@ -322,7 +327,7 @@ class PropertyDelete(EventPermissionRequiredMixin, DeleteView): else: return HttpResponseForbidden() - def get_success_url(self): + def get_success_url(self) -> str: return reverse('control:event.items.properties', kwargs={ 'organizer': self.request.event.organizer.slug, 'event': self.request.event.slug, @@ -356,13 +361,13 @@ class QuestionDelete(EventPermissionRequiredMixin, DeleteView): permission = 'can_change_items' context_object_name = 'question' - def get_object(self, queryset=None): + def get_object(self, queryset=None) -> Question: url = resolve(self.request.path_info) return self.request.event.questions.current.get( 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['dependent'] = list(self.get_object().items.current.all()) return context @@ -373,7 +378,7 @@ class QuestionDelete(EventPermissionRequiredMixin, DeleteView): self.object.delete() return HttpResponseRedirect(success_url) - def get_success_url(self): + def get_success_url(self) -> str: return reverse('control:event.items.questions', kwargs={ 'organizer': self.request.event.organizer.slug, 'event': self.request.event.slug, @@ -387,13 +392,13 @@ class QuestionUpdate(EventPermissionRequiredMixin, UpdateView): permission = 'can_change_items' context_object_name = 'question' - def get_object(self, queryset=None): + def get_object(self, queryset=None) -> Question: url = resolve(self.request.path_info) return self.request.event.questions.current.get( identity=url.kwargs['question'] ) - def get_success_url(self): + def get_success_url(self) -> str: return reverse('control:event.items.questions', kwargs={ 'organizer': self.request.event.organizer.slug, 'event': self.request.event.slug, @@ -407,7 +412,7 @@ class QuestionCreate(EventPermissionRequiredMixin, CreateView): permission = 'can_change_items' context_object_name = 'question' - def get_success_url(self): + def get_success_url(self) -> str: return reverse('control:event.items.questions', kwargs={ 'organizer': self.request.event.organizer.slug, 'event': self.request.event.slug, @@ -447,7 +452,7 @@ class QuotaCreate(EventPermissionRequiredMixin, CreateView): permission = 'can_change_items' context_object_name = 'quota' - def get_success_url(self): + def get_success_url(self) -> str: return reverse('control:event.items.quotas', kwargs={ 'organizer': self.request.event.organizer.slug, 'event': self.request.event.slug, @@ -465,13 +470,13 @@ class QuotaUpdate(EventPermissionRequiredMixin, UpdateView): permission = 'can_change_items' context_object_name = 'quota' - def get_object(self, queryset=None): + def get_object(self, queryset=None) -> Quota: url = resolve(self.request.path_info) return self.request.event.quotas.current.get( identity=url.kwargs['quota'] ) - def get_success_url(self): + def get_success_url(self) -> str: return reverse('control:event.items.quotas', kwargs={ 'organizer': self.request.event.organizer.slug, 'event': self.request.event.slug, @@ -484,13 +489,13 @@ class QuotaDelete(EventPermissionRequiredMixin, DeleteView): permission = 'can_change_items' context_object_name = 'quota' - def get_object(self, queryset=None): + def get_object(self, queryset=None) -> Quota: url = resolve(self.request.path_info) return self.request.event.quotas.current.get( 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['dependent'] = list(self.get_object().items.current.all()) return context @@ -501,7 +506,7 @@ class QuotaDelete(EventPermissionRequiredMixin, DeleteView): self.object.delete() return HttpResponseRedirect(success_url) - def get_success_url(self): + def get_success_url(self) -> str: return reverse('control:event.items.quotas', kwargs={ 'organizer': self.request.event.organizer.slug, 'event': self.request.event.slug, @@ -512,7 +517,7 @@ class ItemDetailMixin(SingleObjectMixin): model = 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: url = resolve(self.request.path_info) self.item = self.request.event.items.current.get( @@ -551,7 +556,7 @@ class ItemUpdateGeneral(ItemDetailMixin, EventPermissionRequiredMixin, UpdateVie template_name = 'tixlcontrol/item/index.html' permission = 'can_change_items' - def get_success_url(self): + def get_success_url(self) -> str: return reverse('control:event.item', kwargs={ 'organizer': self.request.event.organizer.slug, 'event': self.request.event.slug, @@ -578,7 +583,7 @@ class ItemVariations(ItemDetailMixin, EventPermissionRequiredMixin, TemplateView super().__init__(*args, **kwargs) 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 dictionaries in the format of Item.get_all_variations() @@ -603,7 +608,7 @@ class ItemVariations(ItemDetailMixin, EventPermissionRequiredMixin, TemplateView form.values = values return form - def get_forms(self): + def get_forms(self) -> tuple: """ Returns one form per possible item variation. The forms are returned 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 return self.render_to_response(context) - def get_template_names(self): + def get_template_names(self) -> "List[str]": if self.dimension == 0: return ['tixlcontrol/item/variations_0d.html'] elif self.dimension == 1: @@ -713,7 +718,7 @@ class ItemVariations(ItemDetailMixin, EventPermissionRequiredMixin, TemplateView elif self.dimension >= 2: 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['forms'] = self.forms context['properties'] = self.properties @@ -768,12 +773,12 @@ class ItemRestrictions(ItemDetailMixin, EventPermissionRequiredMixin, TemplateVi context = self.get_context_data(object=self.object) 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['formsets'] = self.formsets return context - def get_success_url(self): + def get_success_url(self) -> str: return reverse('control:event.item.restrictions', kwargs={ 'organizer': self.request.event.organizer.slug, 'event': self.request.event.slug,