forked from CGM_Public/pretix_original
Added python3.5-style type annotations to pretix.base
This commit is contained in:
@@ -3,11 +3,12 @@ import time
|
||||
|
||||
from django.core.cache import caches
|
||||
from django.db.models import Model
|
||||
from typing import Dict, List
|
||||
|
||||
|
||||
class NamespacedCache:
|
||||
|
||||
def __init__(self, prefixkey, cache: str='default'):
|
||||
def __init__(self, prefixkey: str, cache: str='default'):
|
||||
self.cache = caches[cache]
|
||||
self.prefixkey = prefixkey
|
||||
|
||||
@@ -30,7 +31,7 @@ class NamespacedCache:
|
||||
def _strip_prefix(self, key: str) -> str:
|
||||
return key.split(":", 2 + self.prefixkey.count(":"))[-1]
|
||||
|
||||
def clear(self):
|
||||
def clear(self) -> None:
|
||||
try:
|
||||
prefix = self.cache.incr(self.prefixkey, 1)
|
||||
except ValueError:
|
||||
@@ -43,14 +44,14 @@ class NamespacedCache:
|
||||
def get(self, key: str) -> str:
|
||||
return self.cache.get(self._prefix_key(key))
|
||||
|
||||
def get_many(self, keys: "list[str]") -> "dict[str, str]":
|
||||
def get_many(self, keys: List[str]) -> Dict[str, str]:
|
||||
values = self.cache.get_many([self._prefix_key(key) for key in keys])
|
||||
newvalues = {}
|
||||
for k, v in values.items():
|
||||
newvalues[self._strip_prefix(k)] = v
|
||||
return newvalues
|
||||
|
||||
def set_many(self, values: "dict[str, str]", timeout=3600):
|
||||
def set_many(self, values: Dict[str, str], timeout=3600):
|
||||
newvalues = {}
|
||||
for k, v in values.items():
|
||||
newvalues[self._prefix_key(k)] = v
|
||||
@@ -59,7 +60,7 @@ class NamespacedCache:
|
||||
def delete(self, key: str): # NOQA
|
||||
return self.cache.delete(self._prefix_key(key))
|
||||
|
||||
def delete_many(self, keys: "list[str]"): # NOQA
|
||||
def delete_many(self, keys: List[str]): # NOQA
|
||||
return self.cache.delete_many([self._prefix_key(key) for key in keys])
|
||||
|
||||
def incr(self, key: str, by: int=1): # NOQA
|
||||
@@ -86,6 +87,6 @@ class ObjectRelatedCache(NamespacedCache):
|
||||
times as you want.
|
||||
"""
|
||||
|
||||
def __init__(self, obj, cache: str='default'):
|
||||
def __init__(self, obj: Model, cache: str='default'):
|
||||
assert isinstance(obj, Model)
|
||||
super().__init__('%s:%s' % (obj._meta.object_name, obj.pk), cache)
|
||||
|
||||
@@ -2,6 +2,7 @@ import json
|
||||
|
||||
from django.core.serializers.json import DjangoJSONEncoder
|
||||
from django.dispatch import receiver
|
||||
from typing import Tuple
|
||||
|
||||
from pretix.base.signals import register_data_exporters
|
||||
|
||||
@@ -60,7 +61,7 @@ class BaseExporter:
|
||||
"""
|
||||
return {}
|
||||
|
||||
def render(self, form_data: dict) -> tuple:
|
||||
def render(self, form_data: dict) -> Tuple[str, str, str]:
|
||||
"""
|
||||
Render the exported file and return a tuple consisting of a filename, a file type
|
||||
and file content.
|
||||
|
||||
@@ -43,7 +43,7 @@ class LoginForm(forms.Form):
|
||||
|
||||
return self.cleaned_data
|
||||
|
||||
def confirm_login_allowed(self, user):
|
||||
def confirm_login_allowed(self, user: User):
|
||||
"""
|
||||
Controls whether the given User may log in. This is a policy setting,
|
||||
independent of end-user authentication. This default behavior is to
|
||||
|
||||
@@ -6,6 +6,7 @@ from django.conf import settings
|
||||
from django.db.models import SubfieldBase, TextField
|
||||
from django.utils import translation
|
||||
from django.utils.safestring import mark_safe
|
||||
from typing import Dict, List
|
||||
|
||||
|
||||
class LazyI18nString:
|
||||
@@ -13,7 +14,7 @@ class LazyI18nString:
|
||||
This represents an internationalized string that is/was/will be stored in the database.
|
||||
"""
|
||||
|
||||
def __init__(self, data):
|
||||
def __init__(self, data: Dict[str, str]):
|
||||
"""
|
||||
Input data should be a dictionary which maps language codes to content.
|
||||
"""
|
||||
@@ -26,7 +27,7 @@ class LazyI18nString:
|
||||
else:
|
||||
self.data = j
|
||||
|
||||
def __str__(self):
|
||||
def __str__(self) -> str:
|
||||
"""
|
||||
Evaluate the given string with respect to the currently active locale.
|
||||
This will rather return you a string in a wrong language than give you an
|
||||
@@ -53,10 +54,10 @@ class LazyI18nString:
|
||||
else:
|
||||
return str(self.data)
|
||||
|
||||
def __repr__(self):
|
||||
def __repr__(self) -> str:
|
||||
return '<LazyI18nString: %s>' % repr(self.data)
|
||||
|
||||
def __lt__(self, other): # NOQA
|
||||
def __lt__(self, other) -> bool: # NOQA
|
||||
return str(self) < str(other)
|
||||
|
||||
|
||||
@@ -68,7 +69,7 @@ class I18nWidget(forms.MultiWidget):
|
||||
"""
|
||||
widget = forms.TextInput
|
||||
|
||||
def __init__(self, langcodes, field, attrs=None):
|
||||
def __init__(self, langcodes: List[str], field: forms.Field, attrs=None):
|
||||
widgets = []
|
||||
self.langcodes = langcodes
|
||||
self.enabled_langcodes = langcodes
|
||||
|
||||
@@ -3,6 +3,7 @@ from collections import OrderedDict
|
||||
import pytz
|
||||
from django.conf import settings
|
||||
from django.core.urlresolvers import get_script_prefix
|
||||
from django.http import HttpRequest, HttpResponse
|
||||
from django.utils import timezone, translation
|
||||
from django.utils.cache import patch_vary_headers
|
||||
from django.utils.translation import LANGUAGE_SESSION_KEY
|
||||
@@ -21,7 +22,7 @@ class LocaleMiddleware:
|
||||
for a request.
|
||||
"""
|
||||
|
||||
def process_request(self, request):
|
||||
def process_request(self, request: HttpRequest):
|
||||
language = get_language_from_request(request)
|
||||
if hasattr(request, 'event') and not request.path.startswith(get_script_prefix() + 'control'):
|
||||
if language not in request.event.settings.locales:
|
||||
@@ -51,7 +52,7 @@ class LocaleMiddleware:
|
||||
else:
|
||||
timezone.deactivate()
|
||||
|
||||
def process_response(self, request, response):
|
||||
def process_response(self, request: HttpRequest, response: HttpResponse):
|
||||
language = translation.get_language()
|
||||
patch_vary_headers(response, ('Accept-Language',))
|
||||
if 'Content-Language' not in response:
|
||||
@@ -59,14 +60,14 @@ class LocaleMiddleware:
|
||||
return response
|
||||
|
||||
|
||||
def get_language_from_user_settings(request) -> str:
|
||||
def get_language_from_user_settings(request: HttpRequest) -> str:
|
||||
if request.user.is_authenticated():
|
||||
lang_code = request.user.locale
|
||||
if lang_code in _supported and lang_code is not None and check_for_language(lang_code):
|
||||
return lang_code
|
||||
|
||||
|
||||
def get_language_from_session_or_cookie(request) -> str:
|
||||
def get_language_from_session_or_cookie(request: HttpRequest) -> str:
|
||||
if hasattr(request, 'session'):
|
||||
lang_code = request.session.get(LANGUAGE_SESSION_KEY)
|
||||
if lang_code in _supported and lang_code is not None and check_for_language(lang_code):
|
||||
@@ -79,7 +80,7 @@ def get_language_from_session_or_cookie(request) -> str:
|
||||
pass
|
||||
|
||||
|
||||
def get_language_from_event(request) -> str:
|
||||
def get_language_from_event(request: HttpRequest) -> str:
|
||||
if hasattr(request, 'event'):
|
||||
lang_code = request.event.settings.locale
|
||||
try:
|
||||
@@ -88,7 +89,7 @@ def get_language_from_event(request) -> str:
|
||||
pass
|
||||
|
||||
|
||||
def get_language_from_browser(request) -> str:
|
||||
def get_language_from_browser(request: HttpRequest) -> str:
|
||||
accept = request.META.get('HTTP_ACCEPT_LANGUAGE', '')
|
||||
for accept_lang, unused in parse_accept_lang_header(accept):
|
||||
if accept_lang == '*':
|
||||
@@ -110,7 +111,7 @@ def get_default_language():
|
||||
return settings.LANGUAGE_CODE
|
||||
|
||||
|
||||
def get_language_from_request(request) -> str:
|
||||
def get_language_from_request(request: HttpRequest) -> str:
|
||||
"""
|
||||
Analyzes the request to find what language the user wants the system to
|
||||
show. Only languages listed in settings.LANGUAGES are taken into account.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
@@ -12,13 +12,13 @@ class UserManager(BaseUserManager):
|
||||
model documentation to see what's so special about our user model.
|
||||
"""
|
||||
|
||||
def create_user(self, email, password=None, **kwargs):
|
||||
def create_user(self, email: str, password: str=None, **kwargs):
|
||||
user = self.model(email=email, **kwargs)
|
||||
user.set_password(password)
|
||||
user.save()
|
||||
return user
|
||||
|
||||
def create_superuser(self, email, password=None): # NOQA
|
||||
def create_superuser(self, email: str, password: str=None): # NOQA
|
||||
# Not used in the software but required by Django
|
||||
if password is None:
|
||||
raise Exception("You must provide a password")
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import copy
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
|
||||
import six
|
||||
from django.db import models
|
||||
@@ -12,7 +13,7 @@ class Versionable(BaseVersionable):
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
def clone_shallow(self, forced_version_date=None):
|
||||
def clone_shallow(self, forced_version_date: datetime=None):
|
||||
"""
|
||||
This behaves like clone(), but misses all the Many2Many-relation-handling. This is
|
||||
a performance optimization for cases in which we have to handle the Many2Many relations
|
||||
@@ -63,7 +64,7 @@ class Versionable(BaseVersionable):
|
||||
})
|
||||
|
||||
|
||||
def cachedfile_name(instance, filename):
|
||||
def cachedfile_name(instance, filename: str) -> str:
|
||||
return 'cachedfiles/%s.%s' % (instance.id, filename.split('.')[-1])
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import sys
|
||||
from datetime import datetime
|
||||
from decimal import Decimal
|
||||
from itertools import product
|
||||
|
||||
from django.db import models
|
||||
@@ -6,6 +8,7 @@ from django.db.models import Q, Case, Count, Sum, When
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from typing import List, Tuple, Union
|
||||
from versions.models import VersionedForeignKey, VersionedManyToManyField
|
||||
|
||||
from pretix.base.i18n import I18nCharField, I18nTextField
|
||||
@@ -61,11 +64,11 @@ class ItemCategory(Versionable):
|
||||
def sortkey(self):
|
||||
return self.position, self.version_birth_date
|
||||
|
||||
def __lt__(self, other):
|
||||
def __lt__(self, other) -> bool:
|
||||
return self.sortkey < other.sortkey
|
||||
|
||||
|
||||
def itempicture_upload_to(instance, filename):
|
||||
def itempicture_upload_to(instance, filename: str) -> str:
|
||||
return '%s/%s/item-%s.%s' % (
|
||||
instance.event.organizer.slug, instance.event.slug, instance.identity,
|
||||
filename.split('.')[-1]
|
||||
@@ -170,7 +173,7 @@ class Item(Versionable):
|
||||
if self.event:
|
||||
self.event.get_cache().clear()
|
||||
|
||||
def get_all_variations(self, use_cache: bool=False) -> "list[VariationDict]":
|
||||
def get_all_variations(self, use_cache: bool=False) -> List[VariationDict]:
|
||||
"""
|
||||
This method returns a list containing all variations of this
|
||||
item. The list contains one VariationDict per variation, where
|
||||
@@ -438,10 +441,10 @@ class PropertyValue(Versionable):
|
||||
self.prop.event.get_cache().clear()
|
||||
|
||||
@property
|
||||
def sortkey(self):
|
||||
def sortkey(self) -> Tuple[int, datetime]:
|
||||
return self.position, self.version_birth_date
|
||||
|
||||
def __lt__(self, other):
|
||||
def __lt__(self, other) -> bool:
|
||||
return self.sortkey < other.sortkey
|
||||
|
||||
|
||||
@@ -508,7 +511,7 @@ class ItemVariation(Versionable):
|
||||
if self.item:
|
||||
self.item.event.get_cache().clear()
|
||||
|
||||
def check_quotas(self):
|
||||
def check_quotas(self) -> Tuple[int, int]:
|
||||
"""
|
||||
This method is used to determine whether this ItemVariation is currently
|
||||
available for sale in terms of quotas.
|
||||
@@ -518,7 +521,7 @@ class ItemVariation(Versionable):
|
||||
return min([q.availability() for q in self.quotas.all()],
|
||||
key=lambda s: (s[0], s[1] if s[1] is not None else sys.maxsize))
|
||||
|
||||
def to_variation_dict(self):
|
||||
def to_variation_dict(self) -> VariationDict:
|
||||
"""
|
||||
:return: a :py:class:`VariationDict` representing this variation.
|
||||
"""
|
||||
@@ -528,7 +531,7 @@ class ItemVariation(Versionable):
|
||||
vd['variation'] = self
|
||||
return vd
|
||||
|
||||
def check_restrictions(self):
|
||||
def check_restrictions(self) -> Union[bool, Decimal]:
|
||||
"""
|
||||
This method is used to determine whether this ItemVariation is restricted
|
||||
in sale by any restriction plugins.
|
||||
@@ -806,7 +809,7 @@ class Quota(Versionable):
|
||||
if self.event:
|
||||
self.event.get_cache().clear()
|
||||
|
||||
def availability(self):
|
||||
def availability(self) -> Tuple[int, int]:
|
||||
"""
|
||||
This method is used to determine whether Items or ItemVariations belonging
|
||||
to this quota should currently be available for sale.
|
||||
@@ -864,7 +867,7 @@ class Quota(Versionable):
|
||||
return o
|
||||
|
||||
@cached_property
|
||||
def _position_lookup(self):
|
||||
def _position_lookup(self) -> Q:
|
||||
return (
|
||||
( # Orders for items which do not have any variations
|
||||
Q(variation__isnull=True)
|
||||
|
||||
@@ -5,6 +5,7 @@ from datetime import datetime
|
||||
from django.db import models
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from typing import List, Union
|
||||
from versions.models import VersionedForeignKey
|
||||
|
||||
from .base import CachedFile, Versionable
|
||||
@@ -133,7 +134,7 @@ class Order(Versionable):
|
||||
verbose_name_plural = _("Orders")
|
||||
ordering = ("-datetime",)
|
||||
|
||||
def str(self):
|
||||
def __str__(self):
|
||||
return self.full_code
|
||||
|
||||
@property
|
||||
@@ -160,7 +161,7 @@ class Order(Versionable):
|
||||
return
|
||||
|
||||
@property
|
||||
def can_modify_answers(self):
|
||||
def can_modify_answers(self) -> bool:
|
||||
"""
|
||||
Is ``True`` if the user can change the question answers / attendee names that are
|
||||
related to the order. This checks order status and modification deadlines. It also
|
||||
@@ -187,7 +188,7 @@ class Order(Versionable):
|
||||
order.save()
|
||||
return order
|
||||
|
||||
def _can_be_paid(self):
|
||||
def _can_be_paid(self) -> Union[bool, str]:
|
||||
error_messages = {
|
||||
'late': _("The payment is too late to be accepted."),
|
||||
}
|
||||
@@ -202,7 +203,7 @@ class Order(Versionable):
|
||||
|
||||
return self._is_still_available()
|
||||
|
||||
def _is_still_available(self):
|
||||
def _is_still_available(self) -> Union[bool, str]:
|
||||
error_messages = {
|
||||
'unavailable': _('Some of the ordered products were no longer available.'),
|
||||
}
|
||||
@@ -339,7 +340,7 @@ class OrderPosition(ObjectWithAnswers, Versionable):
|
||||
verbose_name_plural = _("Order positions")
|
||||
|
||||
@classmethod
|
||||
def transform_cart_positions(cls, cp: list, order) -> list:
|
||||
def transform_cart_positions(cls, cp: List, order) -> list:
|
||||
ops = []
|
||||
for cartpos in cp:
|
||||
op = OrderPosition(
|
||||
|
||||
@@ -45,7 +45,7 @@ class Organizer(Versionable):
|
||||
verbose_name_plural = _("Organizers")
|
||||
ordering = ("name",)
|
||||
|
||||
def __str__(self):
|
||||
def __str__(self) -> str:
|
||||
return self.name
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
@@ -97,7 +97,7 @@ class OrganizerPermission(Versionable):
|
||||
verbose_name = _("Organizer permission")
|
||||
verbose_name_plural = _("Organizer permissions")
|
||||
|
||||
def __str__(self):
|
||||
def __str__(self) -> str:
|
||||
return _("%(name)s on %(object)s") % {
|
||||
'name': str(self.user),
|
||||
'object': str(self.organizer),
|
||||
|
||||
@@ -9,8 +9,9 @@ from django.forms import Form
|
||||
from django.http import HttpRequest
|
||||
from django.template.loader import get_template
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from typing import Any, Dict
|
||||
|
||||
from pretix.base.models import CartPosition, Order, Quota
|
||||
from pretix.base.models import CartPosition, Event, Order, Quota
|
||||
from pretix.base.settings import SettingsSandbox
|
||||
from pretix.base.signals import register_payment_providers
|
||||
|
||||
@@ -20,7 +21,7 @@ class BasePaymentProvider:
|
||||
This is the base class for all payment providers.
|
||||
"""
|
||||
|
||||
def __init__(self, event):
|
||||
def __init__(self, event: Event):
|
||||
self.event = event
|
||||
self.settings = SettingsSandbox('payment', self.identifier, event)
|
||||
|
||||
@@ -193,7 +194,7 @@ class BasePaymentProvider:
|
||||
"""
|
||||
raise NotImplementedError() # NOQA
|
||||
|
||||
def checkout_prepare(self, request: HttpRequest, cart: dict) -> "bool|str":
|
||||
def checkout_prepare(self, request: HttpRequest, cart: Dict[str, Any]) -> "bool|str":
|
||||
"""
|
||||
Will be called after the user selected this provider as his payment method.
|
||||
If you provided a form to the user to enter payment data, this method should
|
||||
@@ -395,7 +396,7 @@ class FreeOrderProvider(BasePaymentProvider):
|
||||
def identifier(self) -> str:
|
||||
return "free"
|
||||
|
||||
def checkout_confirm_render(self, request) -> str:
|
||||
def checkout_confirm_render(self, request: HttpRequest) -> str:
|
||||
return _("No payment is required as this order only includes products which are free of charge.")
|
||||
|
||||
def order_pending_render(self, request: HttpRequest, order: Order) -> str:
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from enum import Enum
|
||||
|
||||
from django.apps import apps
|
||||
from typing import List
|
||||
|
||||
|
||||
class PluginType(Enum):
|
||||
@@ -10,7 +11,7 @@ class PluginType(Enum):
|
||||
EXPORT = 4
|
||||
|
||||
|
||||
def get_all_plugins() -> "List[class]":
|
||||
def get_all_plugins() -> List[type]:
|
||||
"""
|
||||
Returns the PretixPluginMeta classes of all plugins found in the installed Django apps.
|
||||
"""
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
from datetime import timedelta
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from django.conf import settings
|
||||
from django.db.models import Q
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from typing import List, Optional, Tuple
|
||||
|
||||
from pretix.base.models import (
|
||||
CartPosition, Event, EventLock, Item, ItemVariation, Quota,
|
||||
@@ -29,7 +30,7 @@ error_messages = {
|
||||
}
|
||||
|
||||
|
||||
def _extend_existing(event, cart_id, expiry):
|
||||
def _extend_existing(event: Event, cart_id: str, expiry: datetime) -> None:
|
||||
# Extend this user's cart session to 30 minutes from now to ensure all items in the
|
||||
# cart expire at the same time
|
||||
# We can extend the reservation of items which are not yet expired without risk
|
||||
@@ -38,7 +39,7 @@ def _extend_existing(event, cart_id, expiry):
|
||||
).update(expires=expiry)
|
||||
|
||||
|
||||
def _re_add_expired_positions(items, event, cart_id):
|
||||
def _re_add_expired_positions(items: List[CartPosition], event: Event, cart_id: str) -> List[CartPosition]:
|
||||
positions = set()
|
||||
# For items that are already expired, we have to delete and re-add them, as they might
|
||||
# be no longer available or prices might have changed. Sorry!
|
||||
@@ -51,20 +52,21 @@ def _re_add_expired_positions(items, event, cart_id):
|
||||
return positions
|
||||
|
||||
|
||||
def _delete_expired(expired):
|
||||
def _delete_expired(expired: List[CartPosition]) -> None:
|
||||
for cp in expired:
|
||||
if cp.version_end_date is None:
|
||||
cp.delete()
|
||||
|
||||
|
||||
def _check_date(event):
|
||||
def _check_date(event: Event) -> None:
|
||||
if event.presale_start and now() < event.presale_start:
|
||||
raise CartError(error_messages['not_started'])
|
||||
if event.presale_end and now() > event.presale_end:
|
||||
raise CartError(error_messages['ended'])
|
||||
|
||||
|
||||
def _add_items(event, items, cart_id, expiry):
|
||||
def _add_items(event: Event, items: List[Tuple[str, Optional[str], int]],
|
||||
cart_id: str, expiry: datetime) -> Optional[str]:
|
||||
err = None
|
||||
|
||||
# Fetch items from the database
|
||||
@@ -129,7 +131,7 @@ def _add_items(event, items, cart_id, expiry):
|
||||
return err
|
||||
|
||||
|
||||
def _add_items_to_cart(event: Event, items: list, cart_id: str=None):
|
||||
def _add_items_to_cart(event: Event, items: List[Tuple[str, Optional[str], int]], cart_id: str=None) -> None:
|
||||
with event.lock():
|
||||
_check_date(event)
|
||||
existing = CartPosition.objects.current.filter(Q(cart_id=cart_id) & Q(event=event)).count()
|
||||
@@ -150,7 +152,7 @@ def _add_items_to_cart(event: Event, items: list, cart_id: str=None):
|
||||
raise CartError(err)
|
||||
|
||||
|
||||
def add_items_to_cart(event: str, items: list, cart_id: str=None):
|
||||
def add_items_to_cart(event: str, items: List[Tuple[str, Optional[str], int]], cart_id: str=None) -> None:
|
||||
"""
|
||||
Adds a list of items to a user's cart.
|
||||
:param event: The event ID in question
|
||||
@@ -160,12 +162,12 @@ def add_items_to_cart(event: str, items: list, cart_id: str=None):
|
||||
"""
|
||||
event = Event.objects.current.get(identity=event)
|
||||
try:
|
||||
return _add_items_to_cart(event, items, cart_id)
|
||||
_add_items_to_cart(event, items, cart_id)
|
||||
except EventLock.LockTimeoutException:
|
||||
raise CartError(error_messages['busy'])
|
||||
|
||||
|
||||
def remove_items_from_cart(event: str, items: list, cart_id: str=None):
|
||||
def remove_items_from_cart(event: str, items: List[Tuple[str, Optional[str], int]], cart_id: str=None) -> None:
|
||||
"""
|
||||
Removes a list of items from a user's cart.
|
||||
:param event: The event ID in question
|
||||
@@ -188,10 +190,10 @@ if settings.HAS_CELERY:
|
||||
from pretix.celery import app
|
||||
|
||||
@app.task(bind=True, max_retries=5, default_retry_delay=2)
|
||||
def add_items_to_cart_task(self, event: str, items: list, cart_id: str):
|
||||
def add_items_to_cart_task(self, event: str, items: List[Tuple[str, Optional[str], int]], cart_id: str):
|
||||
event = Event.objects.current.get(identity=event)
|
||||
try:
|
||||
return _add_items_to_cart(event, items, cart_id)
|
||||
_add_items_to_cart(event, items, cart_id)
|
||||
except EventLock.LockTimeoutException:
|
||||
self.retry(exc=CartError(error_messages['busy']))
|
||||
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
from django.conf import settings
|
||||
from django.core.files.base import ContentFile
|
||||
from typing import Any, Dict
|
||||
|
||||
from pretix.base.models import CachedFile, Event, cachedfile_name
|
||||
from pretix.base.signals import register_data_exporters
|
||||
|
||||
|
||||
def export(event, fileid, provider, form_data):
|
||||
def export(event: str, fileid: str, provider: str, form_data: Dict[str, Any]) -> None:
|
||||
event = Event.objects.current.get(identity=event)
|
||||
file = CachedFile.objects.get(id=fileid)
|
||||
responses = register_data_exporters.send(event)
|
||||
|
||||
@@ -5,6 +5,7 @@ from django.core.mail import EmailMessage
|
||||
from django.template.loader import get_template
|
||||
from django.utils import translation
|
||||
from django.utils.translation import ugettext as _
|
||||
from typing import Any, Dict
|
||||
|
||||
from pretix.base.i18n import LazyI18nString
|
||||
from pretix.base.models import Event
|
||||
@@ -12,7 +13,8 @@ from pretix.base.models import Event
|
||||
logger = logging.getLogger('pretix.base.mail')
|
||||
|
||||
|
||||
def mail(email: str, subject: str, template: str, context: dict=None, event: Event=None, locale: str=None):
|
||||
def mail(email: str, subject: str, template: str,
|
||||
context: Dict[str, Any]=None, event: Event=None, locale: str=None):
|
||||
"""
|
||||
Sends out an email to a user.
|
||||
|
||||
@@ -58,7 +60,7 @@ def mail(email: str, subject: str, template: str, context: dict=None, event: Eve
|
||||
translation.activate(_lng)
|
||||
|
||||
|
||||
def mail_send(to, subject, body, sender):
|
||||
def mail_send(to: str, subject: str, body: str, sender: str) -> bool:
|
||||
email = EmailMessage(subject, body, sender, to=to)
|
||||
|
||||
try:
|
||||
|
||||
@@ -4,6 +4,7 @@ from django.conf import settings
|
||||
from django.db import transaction
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from typing import List
|
||||
|
||||
from pretix.base.models import (
|
||||
CartPosition, Event, EventLock, Order, OrderPosition, Quota,
|
||||
@@ -30,7 +31,7 @@ error_messages = {
|
||||
|
||||
|
||||
def mark_order_paid(order: Order, provider: str=None, info: str=None, date: datetime=None, manual: bool=None,
|
||||
force: bool=False):
|
||||
force: bool=False) -> Order:
|
||||
"""
|
||||
Marks an order as paid. This clones the order object, sets the payment provider,
|
||||
info and date and returns the cloned order object.
|
||||
@@ -82,14 +83,14 @@ class OrderError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def _check_date(event):
|
||||
def _check_date(event: Event):
|
||||
if event.presale_start and now() < event.presale_start:
|
||||
raise OrderError(error_messages['not_started'])
|
||||
if event.presale_end and now() > event.presale_end:
|
||||
raise OrderError(error_messages['ended'])
|
||||
|
||||
|
||||
def _check_positions(event: Event, dt: datetime, positions: list):
|
||||
def _check_positions(event: Event, dt: datetime, positions: List[CartPosition]):
|
||||
err = None
|
||||
_check_date(event)
|
||||
|
||||
@@ -135,7 +136,7 @@ def _check_positions(event: Event, dt: datetime, positions: list):
|
||||
|
||||
|
||||
@transaction.atomic()
|
||||
def _create_order(event: Event, email: str, positions: list, dt: datetime,
|
||||
def _create_order(event: Event, email: str, positions: List[CartPosition], dt: datetime,
|
||||
payment_provider: BasePaymentProvider, locale: str=None):
|
||||
total = sum([c.price for c in positions])
|
||||
payment_fee = payment_provider.calculate_fee(total)
|
||||
@@ -159,7 +160,7 @@ def _create_order(event: Event, email: str, positions: list, dt: datetime,
|
||||
return order
|
||||
|
||||
|
||||
def _perform_order(event: str, payment_provider: str, position_ids: list,
|
||||
def _perform_order(event: str, payment_provider: str, position_ids: List[str],
|
||||
email: str, locale: str):
|
||||
event = Event.objects.current.get(identity=event)
|
||||
responses = register_payment_providers.send(event)
|
||||
@@ -197,7 +198,7 @@ def _perform_order(event: str, payment_provider: str, position_ids: list,
|
||||
return order.identity
|
||||
|
||||
|
||||
def perform_order(event: str, payment_provider: str, positions: list,
|
||||
def perform_order(event: str, payment_provider: str, positions: List[str],
|
||||
email: str=None, locale: str=None):
|
||||
try:
|
||||
return _perform_order(event, payment_provider, positions, email, locale)
|
||||
@@ -211,7 +212,7 @@ if settings.HAS_CELERY:
|
||||
from pretix.celery import app
|
||||
|
||||
@app.task(bind=True, max_retries=5, default_retry_delay=2)
|
||||
def perform_order_task(self, event: str, payment_provider: str, positions: list,
|
||||
def perform_order_task(self, event: str, payment_provider: str, positions: List[str],
|
||||
email: str=None, locale: str=None):
|
||||
try:
|
||||
return _perform_order(event, payment_provider, positions, email, locale)
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
from decimal import Decimal
|
||||
|
||||
from django.db.models import Count, Sum
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from typing import Any, Dict, Iterable, List, Tuple
|
||||
|
||||
from pretix.base.models import ItemCategory, Order, OrderPosition
|
||||
from pretix.base.models import Event, Item, ItemCategory, Order, OrderPosition
|
||||
from pretix.base.signals import register_payment_providers
|
||||
|
||||
|
||||
@@ -10,14 +13,14 @@ class DummyObject:
|
||||
|
||||
|
||||
class Dontsum:
|
||||
def __init__(self, value):
|
||||
def __init__(self, value: Any):
|
||||
self.value = value
|
||||
|
||||
def __str__(self):
|
||||
def __str__(self) -> str:
|
||||
return str(self.value)
|
||||
|
||||
|
||||
def tuplesum(tuples):
|
||||
def tuplesum(tuples: Iterable[Tuple]) -> Tuple:
|
||||
def mysum(it):
|
||||
sit = [i for i in it if not isinstance(i, Dontsum)]
|
||||
return sum(sit)
|
||||
@@ -25,7 +28,7 @@ def tuplesum(tuples):
|
||||
return tuple(map(mysum, zip(*list(tuples))))
|
||||
|
||||
|
||||
def order_overview(event):
|
||||
def order_overview(event: Event) -> Tuple[List[Tuple[ItemCategory, List[Item]]], Dict[str, Tuple[Decimal, Decimal]]]:
|
||||
items = event.items.all().select_related(
|
||||
'category', # for re-grouping
|
||||
).prefetch_related(
|
||||
|
||||
@@ -8,7 +8,7 @@ from pretix.base.models import CachedFile, CachedTicket, Order, cachedfile_name
|
||||
from pretix.base.signals import register_ticket_outputs
|
||||
|
||||
|
||||
def generate(order, provider):
|
||||
def generate(order: str, provider: str):
|
||||
order = Order.objects.current.select_related('event').get(identity=order)
|
||||
ct = CachedTicket.objects.get_or_create(order=order, provider=provider)[0]
|
||||
if not ct.cachedfile:
|
||||
|
||||
@@ -7,6 +7,7 @@ from django.conf import settings
|
||||
from django.core.files import File
|
||||
from django.core.files.storage import default_storage
|
||||
from django.db.models import Model
|
||||
from typing import Any, Callable, Dict, Optional, TypeVar, Union
|
||||
from versions.models import Versionable
|
||||
|
||||
DEFAULTS = {
|
||||
@@ -109,23 +110,23 @@ class SettingsProxy:
|
||||
you. It will return None for non-existing properties.
|
||||
"""
|
||||
|
||||
def __init__(self, obj, parent=None, type=None):
|
||||
def __init__(self, obj: Model, parent: Optional[Model]=None, type=None):
|
||||
self._obj = obj
|
||||
self._parent = parent
|
||||
self._cached_obj = None
|
||||
self._type = type
|
||||
|
||||
def _cache(self):
|
||||
def _cache(self) -> Dict[str, Any]:
|
||||
if self._cached_obj is None:
|
||||
self._cached_obj = {}
|
||||
for setting in self._obj.setting_objects.current.all():
|
||||
self._cached_obj[setting.key] = setting
|
||||
return self._cached_obj
|
||||
|
||||
def _flush(self):
|
||||
def _flush(self) -> None:
|
||||
self._cached_obj = None
|
||||
|
||||
def _unserialize(self, value, as_type):
|
||||
def _unserialize(self, value: str, as_type: type) -> Any:
|
||||
if as_type is not None and isinstance(value, as_type):
|
||||
return value
|
||||
elif value is None:
|
||||
@@ -155,7 +156,7 @@ class SettingsProxy:
|
||||
return as_type.objects.get(pk=value)
|
||||
return value
|
||||
|
||||
def _serialize(self, value):
|
||||
def _serialize(self, value: Any) -> str:
|
||||
if isinstance(value, str):
|
||||
return value
|
||||
elif isinstance(value, int) or isinstance(value, float) \
|
||||
@@ -174,7 +175,7 @@ class SettingsProxy:
|
||||
|
||||
raise TypeError('Unable to serialize %s into a setting.' % str(type(value)))
|
||||
|
||||
def get(self, key, default=None, as_type=None):
|
||||
def get(self, key: str, default: Any=None, as_type: type=None):
|
||||
"""
|
||||
Get a setting specified by key 'key'. Normally, settings are strings, but
|
||||
if you put non-strings into the settings object, you can request unserialization
|
||||
@@ -199,21 +200,21 @@ class SettingsProxy:
|
||||
|
||||
return self._unserialize(value, as_type)
|
||||
|
||||
def __getitem__(self, key):
|
||||
def __getitem__(self, key: str) -> Any:
|
||||
return self.get(key)
|
||||
|
||||
def __getattr__(self, key):
|
||||
def __getattr__(self, key: str) -> Any:
|
||||
return self.get(key)
|
||||
|
||||
def __setattr__(self, key, value):
|
||||
def __setattr__(self, key: str, value: Any) -> None:
|
||||
if key.startswith('_'):
|
||||
return super().__setattr__(key, value)
|
||||
self.set(key, value)
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
def __setitem__(self, key: str, value: Any) -> None:
|
||||
self.set(key, value)
|
||||
|
||||
def set(self, key, value):
|
||||
def set(self, key: str, value: Any) -> None:
|
||||
if key in self._cache():
|
||||
s = self._cache()[key]
|
||||
s = s.clone()
|
||||
@@ -223,12 +224,12 @@ class SettingsProxy:
|
||||
s.save()
|
||||
self._cache()[key] = s
|
||||
|
||||
def __delattr__(self, key):
|
||||
def __delattr__(self, key: str) -> None:
|
||||
if key.startswith('_'):
|
||||
return super().__delattr__(key)
|
||||
return self.__delitem__(key)
|
||||
|
||||
def __delitem__(self, key):
|
||||
def __delitem__(self, key: str) -> None:
|
||||
if key in self._cache():
|
||||
self._cache()[key].delete()
|
||||
del self._cache()[key]
|
||||
@@ -240,36 +241,36 @@ class SettingsSandbox:
|
||||
prefixes for you.
|
||||
"""
|
||||
|
||||
def __init__(self, type, key, event):
|
||||
def __init__(self, type: str, key: str, event: Versionable):
|
||||
self._event = event
|
||||
self._type = type
|
||||
self._key = key
|
||||
|
||||
def _convert_key(self, key):
|
||||
def _convert_key(self, key: str) -> str:
|
||||
return '%s_%s_%s' % (self._type, self._key, key)
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
def __setitem__(self, key: str, value: Any) -> None:
|
||||
self.set(key, value)
|
||||
|
||||
def __setattr__(self, key, value):
|
||||
def __setattr__(self, key: str, value: Any) -> None:
|
||||
if key.startswith('_'):
|
||||
return super().__setattr__(key, value)
|
||||
self.set(key, value)
|
||||
|
||||
def __getattr__(self, item):
|
||||
def __getattr__(self, item: str) -> Any:
|
||||
return self.get(item)
|
||||
|
||||
def __getitem__(self, item):
|
||||
def __getitem__(self, item: str) -> Any:
|
||||
return self.get(item)
|
||||
|
||||
def __delitem__(self, key):
|
||||
def __delitem__(self, key: str) -> None:
|
||||
del self._event.settings[self._convert_key(key)]
|
||||
|
||||
def __delattr__(self, key):
|
||||
def __delattr__(self, key: str) -> None:
|
||||
del self._event.settings[self._convert_key(key)]
|
||||
|
||||
def get(self, key, default=None, as_type=str):
|
||||
def get(self, key: str, default: Any=None, as_type: type=str):
|
||||
return self._event.settings.get(self._convert_key(key), default=default, as_type=as_type)
|
||||
|
||||
def set(self, key, value):
|
||||
def set(self, key: str, value: Any):
|
||||
self._event.settings.set(self._convert_key(key), value)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import django.dispatch
|
||||
from django.apps import apps
|
||||
from django.dispatch.dispatcher import NO_RECEIVERS
|
||||
from typing import Any, Callable, List, Tuple
|
||||
|
||||
from .models import Event
|
||||
|
||||
@@ -12,7 +13,7 @@ class EventPluginSignal(django.dispatch.Signal):
|
||||
Event.
|
||||
"""
|
||||
|
||||
def send(self, sender, **named):
|
||||
def send(self, sender: Event, **named) -> List[Tuple[Callable, Any]]:
|
||||
"""
|
||||
Send signal from sender to all connected receivers that belong to
|
||||
plugins enabled for the given Event.
|
||||
|
||||
@@ -3,8 +3,9 @@ from collections import OrderedDict
|
||||
from django import forms
|
||||
from django.http import HttpRequest
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from typing import Tuple
|
||||
|
||||
from pretix.base.models import Order
|
||||
from pretix.base.models import Event, Order
|
||||
from pretix.base.settings import SettingsSandbox
|
||||
|
||||
|
||||
@@ -13,7 +14,7 @@ class BaseTicketOutput:
|
||||
This is the base class for all ticket outputs.
|
||||
"""
|
||||
|
||||
def __init__(self, event):
|
||||
def __init__(self, event: Event):
|
||||
self.event = event
|
||||
self.settings = SettingsSandbox('ticketoutput', self.identifier, event)
|
||||
|
||||
@@ -28,7 +29,7 @@ class BaseTicketOutput:
|
||||
"""
|
||||
return self.settings.get('_enabled', as_type=bool)
|
||||
|
||||
def generate(self, order: Order) -> tuple:
|
||||
def generate(self, order: Order) -> Tuple[str, str, str]:
|
||||
"""
|
||||
This method should generate the download file and return a tuple consisting of a
|
||||
filename, a file type and file content.
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
from typing import Iterable, List, Tuple
|
||||
|
||||
|
||||
class VariationDict(dict):
|
||||
"""
|
||||
A VariationDict object behaves exactle the same as the Python built-in
|
||||
@@ -7,7 +10,7 @@ class VariationDict(dict):
|
||||
"""
|
||||
IGNORE_KEYS = ('variation', 'key', 'available', 'price')
|
||||
|
||||
def relevant_items(self) -> "list[(str, PropertyValue)]":
|
||||
def relevant_items(self) -> Iterable[Tuple]:
|
||||
"""
|
||||
Iterate over all items with numeric keys.
|
||||
|
||||
@@ -16,7 +19,7 @@ class VariationDict(dict):
|
||||
"""
|
||||
return (i for i in self.items() if i[0] not in self.IGNORE_KEYS)
|
||||
|
||||
def relevant_values(self) -> "list[PropertyValue]":
|
||||
def relevant_values(self) -> Iterable["PropertyValue"]:
|
||||
"""
|
||||
Iterate over all values with numeric keys.
|
||||
|
||||
@@ -60,14 +63,14 @@ class VariationDict(dict):
|
||||
else:
|
||||
return super().__eq__(other)
|
||||
|
||||
def empty(self):
|
||||
def empty(self) -> bool:
|
||||
"""
|
||||
Returns true, if this VariationDict does not contain any "real" data like
|
||||
references to PropertyValues, but only "metadata".
|
||||
"""
|
||||
return not next(self.relevant_items(), False)
|
||||
|
||||
def ordered_values(self) -> "list[ItemVariation]":
|
||||
def ordered_values(self) -> List["ItemVariation"]:
|
||||
"""
|
||||
Returns a list of values ordered by their keys
|
||||
"""
|
||||
@@ -79,7 +82,7 @@ class VariationDict(dict):
|
||||
)
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
def __str__(self) -> str:
|
||||
return " – ".join([str(v.value) for v in self.ordered_values()])
|
||||
|
||||
def copy(self) -> "VariationDict":
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from django.http import HttpResponse
|
||||
from django.http import HttpRequest, HttpResponse
|
||||
from django.shortcuts import get_object_or_404, redirect
|
||||
from django.utils.functional import cached_property
|
||||
from django.views.generic import TemplateView
|
||||
@@ -10,10 +10,10 @@ class DownloadView(TemplateView):
|
||||
template_name = "pretixbase/cachedfiles/pending.html"
|
||||
|
||||
@cached_property
|
||||
def object(self):
|
||||
def object(self) -> CachedFile:
|
||||
return get_object_or_404(CachedFile, id=self.kwargs['id'])
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
|
||||
if 'ajax' in request.GET:
|
||||
return HttpResponse('1' if self.object.file else '0')
|
||||
elif self.object.file:
|
||||
|
||||
Reference in New Issue
Block a user