diff --git a/src/pretix/base/services/quotas.py b/src/pretix/base/services/quotas.py index 4b7893db3..02ead7ca2 100644 --- a/src/pretix/base/services/quotas.py +++ b/src/pretix/base/services/quotas.py @@ -113,10 +113,8 @@ class QuotaAvailability: be a few minutes outdated. In this case, you may not rely on the results in the ``count_*`` properties. """ now_dt = now_dt or now() - quotas = list(set(self._queue)) - quotas_original = list(self._queue) - self._queue.clear() - if not quotas: + quota_ids_set = {q.id for q in self._queue} + if not quota_ids_set: return if allow_cache: @@ -129,7 +127,7 @@ class QuotaAvailability: elif settings.HAS_REDIS: rc = get_redis_connection("redis") quotas_by_event = defaultdict(list) - for q in quotas_original: + for q in [_q for _q in self._queue if _q.id in quota_ids_set]: quotas_by_event[q.event_id].append(q) for eventid, evquotas in quotas_by_event.items(): @@ -139,16 +137,19 @@ class QuotaAvailability: data = [rv for rv in redisval.decode().split(',')] # Except for some rare situations, we don't want to use cache entries older than 2 minutes if time.time() - int(data[2]) < 120 or allow_cache_stale: - quotas_original.remove(q) - quotas.remove(q) + quota_ids_set.remove(q.id) if data[1] == "None": self.results[q] = int(data[0]), None else: self.results[q] = int(data[0]), int(data[1]) - if not quotas: + if not quota_ids_set: return + quotas = [_q for _q in self._queue if _q.id in quota_ids_set] + quotas_original = list(quotas) + self._queue.clear() + self._compute(quotas, now_dt) for q in quotas_original: @@ -284,15 +285,16 @@ class QuotaAvailability: seq = Q(subevent_id__in=subevents) if None in subevents: seq |= Q(subevent__isnull=True) + quota_ids = {q.pk for q in quotas} op_lookup = OrderPosition.objects.filter( order__status__in=[Order.STATUS_PAID, Order.STATUS_PENDING], order__event_id__in=events, ).filter(seq).filter( Q( Q(variation_id__isnull=True) & - Q(item_id__in={i['item_id'] for i in q_items if self._quota_objects[i['quota_id']] in quotas}) + Q(item_id__in={i['item_id'] for i in q_items if i['quota_id'] in quota_ids}) ) | Q( - variation_id__in={i['itemvariation_id'] for i in q_vars if self._quota_objects[i['quota_id']] in quotas}) + variation_id__in={i['itemvariation_id'] for i in q_vars if i['quota_id'] in quota_ids}) ).order_by() if any(q.release_after_exit for q in quotas): op_lookup = op_lookup.annotate( @@ -359,6 +361,7 @@ class QuotaAvailability: func = 'GREATEST' subevents = {q.subevent_id for q in quotas} + quota_ids = {q.pk for q in quotas} seq = Q(subevent_id__in=subevents) if None in subevents: seq |= Q(subevent__isnull=True) @@ -370,10 +373,9 @@ class QuotaAvailability: Q( Q( Q(variation_id__isnull=True) & - Q(item_id__in={i['item_id'] for i in q_items if self._quota_objects[i['quota_id']] in quotas}) + Q(item_id__in={i['item_id'] for i in q_items if i['quota_id'] in quota_ids}) ) | Q( - variation_id__in={i['itemvariation_id'] for i in q_vars if - self._quota_objects[i['quota_id']] in quotas} + variation_id__in={i['itemvariation_id'] for i in q_vars if i['quota_id'] in quota_ids} ) | Q( quota_id__in=[q.pk for q in quotas] ) @@ -398,6 +400,7 @@ class QuotaAvailability: def _compute_carts(self, quotas, q_items, q_vars, size_left, now_dt): events = {q.event_id for q in quotas} subevents = {q.subevent_id for q in quotas} + quota_ids = {q.pk for q in quotas} seq = Q(subevent_id__in=subevents) if None in subevents: seq |= Q(subevent__isnull=True) @@ -413,9 +416,9 @@ class QuotaAvailability: Q( Q( Q(variation_id__isnull=True) & - Q(item_id__in={i['item_id'] for i in q_items if self._quota_objects[i['quota_id']] in quotas}) + Q(item_id__in={i['item_id'] for i in q_items if i['quota_id'] in quota_ids}) ) | Q( - variation_id__in={i['itemvariation_id'] for i in q_vars if self._quota_objects[i['quota_id']] in quotas} + variation_id__in={i['itemvariation_id'] for i in q_vars if i['quota_id'] in quota_ids} ) ) ).order_by().values('item_id', 'subevent_id', 'variation_id').annotate(c=Count('*')) @@ -434,6 +437,7 @@ class QuotaAvailability: def _compute_waitinglist(self, quotas, q_items, q_vars, size_left): events = {q.event_id for q in quotas} subevents = {q.subevent_id for q in quotas} + quota_ids = {q.pk for q in quotas} seq = Q(subevent_id__in=subevents) if None in subevents: seq |= Q(subevent__isnull=True) @@ -444,9 +448,8 @@ class QuotaAvailability: Q( Q( Q(variation_id__isnull=True) & - Q(item_id__in={i['item_id'] for i in q_items if self._quota_objects[i['quota_id']] in quotas}) - ) | Q(variation_id__in={i['itemvariation_id'] for i in q_vars if - self._quota_objects[i['quota_id']] in quotas}) + Q(item_id__in={i['item_id'] for i in q_items if i['quota_id'] in quota_ids}) + ) | Q(variation_id__in={i['itemvariation_id'] for i in q_vars if i['quota_id'] in quota_ids}) ) ).order_by().values('item_id', 'subevent_id', 'variation_id').annotate(c=Count('*')) for line in w_lookup: diff --git a/src/pretix/helpers/daterange.py b/src/pretix/helpers/daterange.py index 4bec2e2fb..98fed992f 100644 --- a/src/pretix/helpers/daterange.py +++ b/src/pretix/helpers/daterange.py @@ -32,10 +32,11 @@ # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations under the License. -from django.template.defaultfilters import date as _date from django.utils.html import format_html from django.utils.translation import get_language, gettext_lazy as _ +from pretix.helpers.templatetags.date_fast import date_fast as _date + def daterange(df, dt, as_html=False): lng = get_language() diff --git a/src/pretix/helpers/templatetags/date_fast.py b/src/pretix/helpers/templatetags/date_fast.py new file mode 100644 index 000000000..668ed65b8 --- /dev/null +++ b/src/pretix/helpers/templatetags/date_fast.py @@ -0,0 +1,61 @@ +# +# This file is part of pretix (Community Edition). +# +# Copyright (C) 2014-2020 Raphael Michel and contributors +# Copyright (C) 2020-2021 rami.io GmbH and contributors +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General +# Public License as published by the Free Software Foundation in version 3 of the License. +# +# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are +# applicable granting you additional permissions and placing additional restrictions on your usage of this software. +# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive +# this file, see . +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied +# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more +# details. +# +# You should have received a copy of the GNU Affero General Public License along with this program. If not, see +# . +# +import functools + +from django import template +from django.utils import dateformat +from django.utils.formats import get_format +from django.utils.translation import get_language + +register = template.Library() + + +@functools.lru_cache(maxsize=32) +def _get_format(format_type, lang): + return get_format(format_type, lang) + + +@register.filter(expects_localtime=True, is_safe=False) +def date_fast(value, arg=None): + """ + Slightly quicker version of |date if the filter is called a lot. The speedup is achieved through + LRU caching for formats. + + Django's built-in |date filter has a caching mechanism if you call it with a named format, + i.e. ``|date_fast:"SHORT_DATE_FORMAT"`` will only be ~6% faster than ``|date:"SHORT_DATE_FORMAT"``. + + However, Django's built-in caching has a flaw with unnamed formats, therefore ``|date_fast:"Y-m-d"`` + will be ~12% faster than ``|date:"Y-m-d"``. + """ + if value in (None, ''): + return '' + + lang = get_language() + format = _get_format(arg, lang) + + try: + return dateformat.format(value, format) + except AttributeError: + try: + return format(value, arg) + except AttributeError: + return '' diff --git a/src/pretix/presale/templates/pretixpresale/fragment_calendar.html b/src/pretix/presale/templates/pretixpresale/fragment_calendar.html index 5d8182f09..c407e5615 100644 --- a/src/pretix/presale/templates/pretixpresale/fragment_calendar.html +++ b/src/pretix/presale/templates/pretixpresale/fragment_calendar.html @@ -1,16 +1,17 @@ {% load i18n %} +{% load date_fast %}
- - - - - - - + + + + + + + @@ -19,8 +20,8 @@ {% for day in week %} {% if day %}
{% trans "Calendar" %}
{{ weeks.1.0.date|date:"l" }}{{ weeks.1.1.date|date:"l" }}{{ weeks.1.2.date|date:"l" }}{{ weeks.1.3.date|date:"l" }}{{ weeks.1.4.date|date:"l" }}{{ weeks.1.5.date|date:"l" }}{{ weeks.1.6.date|date:"l" }}{{ weeks.1.0.date|date_fast:"l" }}{{ weeks.1.1.date|date_fast:"l" }}{{ weeks.1.2.date|date_fast:"l" }}{{ weeks.1.3.date|date_fast:"l" }}{{ weeks.1.4.date|date_fast:"l" }}{{ weeks.1.5.date|date_fast:"l" }}{{ weeks.1.6.date|date_fast:"l" }}
-

+ data-date="{{ day.date|date_fast:"SHORT_DATE_FORMAT" }}"> +