diff --git a/src/pretix/base/migrations/0081_quota_cached_availability_paid_orders.py b/src/pretix/base/migrations/0081_quota_cached_availability_paid_orders.py new file mode 100644 index 0000000000..2f6c031274 --- /dev/null +++ b/src/pretix/base/migrations/0081_quota_cached_availability_paid_orders.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.5 on 2017-10-18 09:06 +from __future__ import unicode_literals + +from django.db import migrations, models + + +def clear_quota_caches(app, schema_editor): + Quota = app.get_model('pretixbase', 'Quota') + Quota.objects.all().update(cached_availability_time=None) + + +class Migration(migrations.Migration): + + dependencies = [ + ('pretixbase', '0080_auto_20171016_1553'), + ] + + operations = [ + migrations.AddField( + model_name='quota', + name='cached_availability_paid_orders', + field=models.PositiveIntegerField(blank=True, null=True), + ), + migrations.RunPython( + clear_quota_caches, migrations.RunPython.noop + ) + ] diff --git a/src/pretix/base/models/items.py b/src/pretix/base/models/items.py index 345dfa82ec..3ec1d1d4d5 100644 --- a/src/pretix/base/models/items.py +++ b/src/pretix/base/models/items.py @@ -727,6 +727,7 @@ class Quota(LoggedModel): ) cached_availability_state = models.PositiveIntegerField(null=True, blank=True) cached_availability_number = models.PositiveIntegerField(null=True, blank=True) + cached_availability_paid_orders = models.PositiveIntegerField(null=True, blank=True) cached_availability_time = models.DateTimeField(null=True, blank=True) class Meta: @@ -783,8 +784,13 @@ class Quota(LoggedModel): self.cached_availability_state = res[0] self.cached_availability_number = res[1] self.cached_availability_time = now_dt + if self.size is None: + self.cached_availability_paid_orders = self.count_pending_orders() self.save( - update_fields=['cached_availability_state', 'cached_availability_number', 'cached_availability_time'], + update_fields=[ + 'cached_availability_state', 'cached_availability_number', 'cached_availability_time', + 'cached_availability_paid_orders' + ], clear_cache=False ) @@ -799,8 +805,9 @@ class Quota(LoggedModel): if size_left is None: return Quota.AVAILABILITY_OK, None - # TODO: Test for interference with old versions of Item-Quota-relations, etc. - size_left -= self.count_paid_orders() + paid_orders = self.count_paid_orders() + self.cached_availability_paid_orders = paid_orders + size_left -= paid_orders if size_left <= 0: return Quota.AVAILABILITY_GONE, 0 diff --git a/src/pretix/base/services/quotas.py b/src/pretix/base/services/quotas.py index eda775fc6f..6c90058ecb 100644 --- a/src/pretix/base/services/quotas.py +++ b/src/pretix/base/services/quotas.py @@ -10,11 +10,11 @@ from ..signals import periodic_task @receiver(signal=periodic_task) def build_all_quota_caches(sender, **kwargs): - refresh_quota_cashes.apply_async() + refresh_quota_caches.apply_async() @app.task -def refresh_quota_cashes(): +def refresh_quota_caches(): last_activity = LogEntry.objects.filter( event=OuterRef('event_id'), ).order_by().values('event').annotate( diff --git a/src/pretix/control/forms/filter.py b/src/pretix/control/forms/filter.py index a997ba8a19..c6660dc2c4 100644 --- a/src/pretix/control/forms/filter.py +++ b/src/pretix/control/forms/filter.py @@ -260,7 +260,7 @@ class EventFilterForm(FilterForm): 'date_from': 'order_from', 'date_to': 'order_to', 'live': 'live', - 'sum_quota_available': 'sum_quota_available' + 'sum_tickets_paid': 'sum_tickets_paid' } status = forms.ChoiceField( label=_('Status'), diff --git a/src/pretix/control/templates/pretixcontrol/events/index.html b/src/pretix/control/templates/pretixcontrol/events/index.html index 0b1923c3e1..1ce4a8c91f 100644 --- a/src/pretix/control/templates/pretixcontrol/events/index.html +++ b/src/pretix/control/templates/pretixcontrol/events/index.html @@ -68,9 +68,9 @@ - {% trans "Quota available" %} - - + {% trans "Paid tickets per quota" %} + + {% trans "Status" %} @@ -97,7 +97,7 @@ {% trans "Series" %} {% endif %} {% if e.settings.show_date_to and e.date_to %} -
– + –
{% if e.has_subevents %} {{ e.max_fromto|default_if_none:e.max_from|default_if_none:e.max_to|default_if_none:""|date:"SHORT_DATETIME_FORMAT" }} {% else %} @@ -107,7 +107,7 @@ {% for q in e.first_quotas|slice:":3" %} - {% include "pretixcontrol/fragment_quota_box.html" with quota=q %} + {% include "pretixcontrol/fragment_quota_box_paid.html" with quota=q %} {% endfor %} {% if e.first_quotas|length > 3 %} {% blocktrans with date=q.cached_availability_time|date:"SHORT_DATETIME_FORMAT" %}Numbers as of {{ date }}{% endblocktrans %}"> {% if q.size|default_if_none:"NONE" == "NONE" %}
diff --git a/src/pretix/control/templates/pretixcontrol/fragment_quota_box_paid.html b/src/pretix/control/templates/pretixcontrol/fragment_quota_box_paid.html new file mode 100644 index 0000000000..03809d6561 --- /dev/null +++ b/src/pretix/control/templates/pretixcontrol/fragment_quota_box_paid.html @@ -0,0 +1,16 @@ +{% load i18n %} +
{% blocktrans with date=q.cached_availability_time|date:"SHORT_DATETIME_FORMAT" %}Numbers as of {{ date }}{% endblocktrans %}{% if q.cached_avail.1 is not None %}
{% blocktrans with num=q.cached_avail.1 %}Currently available: {{ num }}{% endblocktrans %}{% endif %}"> + {% if q.size|default_if_none:"NONE" == "NONE" %} +
+
+ {% else %} +
+
+
+
+ {% endif %} +
+ {{ q.cached_availability_paid_orders|default_if_none:"?" }} / {{ q.size|default_if_none:"∞" }} +
+
diff --git a/src/pretix/control/templates/pretixcontrol/subevents/index.html b/src/pretix/control/templates/pretixcontrol/subevents/index.html index f99b1f1c0c..18b6cc1c2a 100644 --- a/src/pretix/control/templates/pretixcontrol/subevents/index.html +++ b/src/pretix/control/templates/pretixcontrol/subevents/index.html @@ -52,9 +52,9 @@
- {% trans "Quota available" %} - - + {% trans "Paid tickets per quota" %} + + {% trans "Status" %} @@ -74,7 +74,7 @@ {{ s.get_date_from_display }} {% for q in s.first_quotas|slice:":3" %} - {% include "pretixcontrol/fragment_quota_box.html" with quota=q %} + {% include "pretixcontrol/fragment_quota_box_paid.html" with quota=q %} {% endfor %} {% if s.first_quotas|length > 3 %} 0 else 0 - q.inv_percent = 100 - q.percent + if q.size is not None: + q.percent_paid = min( + 100, + round(q.cached_availability_paid_orders / q.size * 100) if q.size > 0 else 100 + ) return ctx @cached_property diff --git a/src/pretix/control/views/subevents.py b/src/pretix/control/views/subevents.py index 60652240f6..a4a2265ae2 100644 --- a/src/pretix/control/views/subevents.py +++ b/src/pretix/control/views/subevents.py @@ -31,16 +31,16 @@ class SubEventList(EventPermissionRequiredMixin, ListView): permission = 'can_change_settings' def get_queryset(self): - sum_quota_available = Quota.objects.filter( + sum_tickets_paid = Quota.objects.filter( subevent=OuterRef('pk') ).order_by().values('subevent').annotate( - s=Sum('cached_availability_number') + s=Sum('cached_availability_paid_orders') ).values( 's' ) qs = self.request.event.subevents.annotate( - sum_quota_available=Subquery(sum_quota_available, output_field=IntegerField()) + sum_tickets_paid=Subquery(sum_tickets_paid, output_field=IntegerField()) ).prefetch_related( Prefetch('quotas', queryset=Quota.objects.annotate(s=Coalesce(F('size'), 0)).order_by('-s'), @@ -61,9 +61,11 @@ class SubEventList(EventPermissionRequiredMixin, ListView): if q.cached_availability_time is not None else q.availability(allow_cache=True) ) - if q.cached_avail[1] is not None: - q.percent = round(q.cached_avail[1] / q.size * 100) if q.size > 0 else 0 - q.inv_percent = 100 - q.percent + if q.size is not None: + q.percent_paid = min( + 100, + round(q.cached_availability_paid_orders / q.size * 100) if q.size > 0 else 100 + ) return ctx @cached_property diff --git a/src/pretix/static/pretixcontrol/scss/main.scss b/src/pretix/static/pretixcontrol/scss/main.scss index b120f4b300..d84446cc7c 100644 --- a/src/pretix/static/pretixcontrol/scss/main.scss +++ b/src/pretix/static/pretixcontrol/scss/main.scss @@ -418,7 +418,7 @@ body.loading #wrapper { display: block; text-align: center; } - .progress-bar-success { + &.availability .progress-bar-success { background: lighten($brand-success, 20%); } }