mirror of
https://github.com/pretix/pretix.git
synced 2026-05-05 15:14:04 +00:00
Refactor quota calculation (#1668)
This commit is contained in:
@@ -59,7 +59,7 @@
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
{% for item in q.items.all %}
|
||||
{% for item in cached_items%}
|
||||
{% if not item.has_variations %}
|
||||
<li><a href="{% url "control:event.item" organizer=request.event.organizer.slug event=request.event.slug item=item.id %}">{{ item }}</a></li>
|
||||
{% endif %}
|
||||
@@ -74,7 +74,7 @@
|
||||
<td>{{ q.subevent.name }} – {{ q.subevent.get_date_range_display }}</td>
|
||||
{% endif %}
|
||||
<td>{% if q.size == None %}Unlimited{% else %}{{ q.size }}{% endif %}</td>
|
||||
<td>{% include "pretixcontrol/items/fragment_quota_availability.html" with availability=q.availability closed=q.closed %}</td>
|
||||
<td>{% include "pretixcontrol/items/fragment_quota_availability.html" with availability=q.cached_avail closed=q.closed %}</td>
|
||||
<td class="text-right flip">
|
||||
<a href="{% url "control:event.items.quotas.edit" organizer=request.event.organizer.slug event=request.event.slug quota=q.id %}" class="btn btn-default btn-sm"><i class="fa fa-edit"></i></a>
|
||||
<a href="{% url "control:event.items.quotas.add" organizer=request.event.organizer.slug event=request.event.slug %}?copy_from={{ q.id }}"
|
||||
|
||||
@@ -25,6 +25,7 @@ from pretix.base.models import (
|
||||
Item, ItemVariation, Order, OrderPosition, OrderRefund, RequiredAction,
|
||||
SubEvent, Voucher, WaitingListEntry,
|
||||
)
|
||||
from pretix.base.services.quotas import QuotaAvailability
|
||||
from pretix.base.timeline import timeline_for_event
|
||||
from pretix.control.forms.event import CommentForm
|
||||
from pretix.control.signals import (
|
||||
@@ -199,10 +200,20 @@ def waitinglist_widgets(sender, subevent=None, lazy=False, **kwargs):
|
||||
@receiver(signal=event_dashboard_widgets)
|
||||
def quota_widgets(sender, subevent=None, lazy=False, **kwargs):
|
||||
widgets = []
|
||||
quotas = sender.quotas.filter(subevent=subevent)
|
||||
|
||||
for q in sender.quotas.filter(subevent=subevent):
|
||||
quotas_to_compute = [
|
||||
q for q in quotas
|
||||
if not q.cache_is_hot(now() + timedelta(seconds=5))
|
||||
]
|
||||
qa = QuotaAvailability()
|
||||
if quotas_to_compute:
|
||||
qa.queue(*quotas_to_compute)
|
||||
qa.compute()
|
||||
|
||||
for q in quotas:
|
||||
if not lazy:
|
||||
status, left = q.availability(allow_cache=True)
|
||||
status, left = qa.results[q] if q in qa.results else q.availability(allow_cache=True)
|
||||
widgets.append({
|
||||
'content': None if lazy else NUM_WIDGET.format(
|
||||
num='{}/{}'.format(left, q.size) if q.size is not None else '\u221e',
|
||||
|
||||
@@ -31,6 +31,7 @@ from pretix.base.models import (
|
||||
)
|
||||
from pretix.base.models.event import SubEvent
|
||||
from pretix.base.models.items import ItemAddOn, ItemBundle, ItemMetaValue
|
||||
from pretix.base.services.quotas import QuotaAvailability
|
||||
from pretix.base.services.tickets import invalidate_cache
|
||||
from pretix.base.signals import quota_availability
|
||||
from pretix.control.forms.item import (
|
||||
@@ -643,9 +644,7 @@ class QuotaList(PaginationMixin, ListView):
|
||||
template_name = 'pretixcontrol/items/quotas.html'
|
||||
|
||||
def get_queryset(self):
|
||||
qs = Quota.objects.filter(
|
||||
event=self.request.event
|
||||
).prefetch_related(
|
||||
qs = self.request.event.quotas.prefetch_related(
|
||||
Prefetch(
|
||||
"items",
|
||||
queryset=Item.objects.annotate(
|
||||
@@ -654,13 +653,28 @@ class QuotaList(PaginationMixin, ListView):
|
||||
to_attr="cached_items"
|
||||
),
|
||||
"variations",
|
||||
"variations__item"
|
||||
"variations__item",
|
||||
Prefetch(
|
||||
"subevent",
|
||||
queryset=self.request.event.subevents.all()
|
||||
)
|
||||
)
|
||||
if self.request.GET.get("subevent", "") != "":
|
||||
s = self.request.GET.get("subevent", "")
|
||||
qs = qs.filter(subevent_id=s)
|
||||
return qs
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
ctx = super().get_context_data()
|
||||
|
||||
qa = QuotaAvailability()
|
||||
qa.queue(*ctx['quotas'])
|
||||
qa.compute()
|
||||
for quota in ctx['quotas']:
|
||||
qa.cached_avail = qa.results[quota]
|
||||
|
||||
return ctx
|
||||
|
||||
|
||||
class QuotaCreate(EventPermissionRequiredMixin, CreateView):
|
||||
model = Quota
|
||||
@@ -719,28 +733,30 @@ class QuotaView(ChartContainingView, DetailView):
|
||||
def get_context_data(self, *args, **kwargs):
|
||||
ctx = super().get_context_data()
|
||||
|
||||
avail = self.object.availability()
|
||||
ctx['avail'] = avail
|
||||
qa = QuotaAvailability(full_results=True)
|
||||
qa.queue(self.object)
|
||||
qa.compute()
|
||||
ctx['avail'] = qa.results[self.object]
|
||||
|
||||
data = [
|
||||
{
|
||||
'label': gettext('Paid orders'),
|
||||
'value': self.object.count_paid_orders(),
|
||||
'value': qa.count_paid_orders[self.object],
|
||||
'sum': True,
|
||||
},
|
||||
{
|
||||
'label': gettext('Pending orders'),
|
||||
'value': self.object.count_pending_orders(),
|
||||
'value': qa.count_pending_orders[self.object],
|
||||
'sum': True,
|
||||
},
|
||||
{
|
||||
'label': gettext('Vouchers and waiting list reservations'),
|
||||
'value': self.object.count_blocking_vouchers(),
|
||||
'value': qa.count_vouchers[self.object],
|
||||
'sum': True,
|
||||
},
|
||||
{
|
||||
'label': gettext('Current user\'s carts'),
|
||||
'value': self.object.count_in_cart(),
|
||||
'value': qa.count_cart[self.object],
|
||||
'sum': True,
|
||||
},
|
||||
]
|
||||
@@ -756,14 +772,14 @@ class QuotaView(ChartContainingView, DetailView):
|
||||
})
|
||||
data.append({
|
||||
'label': gettext('Waiting list (pending)'),
|
||||
'value': self.object.count_waiting_list_pending(),
|
||||
'value': qa.count_waitinglist[self.object],
|
||||
'sum': False,
|
||||
})
|
||||
|
||||
if self.object.size is not None:
|
||||
data.append({
|
||||
'label': gettext('Currently for sale'),
|
||||
'value': avail[1],
|
||||
'value': ctx['avail'][1],
|
||||
'sum': False,
|
||||
'strong': True
|
||||
})
|
||||
@@ -786,7 +802,17 @@ class QuotaView(ChartContainingView, DetailView):
|
||||
ctx['has_ignore_vouchers'] = Voucher.objects.filter(
|
||||
Q(allow_ignore_quota=True) &
|
||||
Q(Q(valid_until__isnull=True) | Q(valid_until__gte=now())) &
|
||||
Q(Q(self.object._position_lookup) | Q(quota=self.object)) &
|
||||
Q(
|
||||
(
|
||||
( # Orders for items which do not have any variations
|
||||
Q(variation__isnull=True) &
|
||||
Q(item_id__in=Quota.items.through.objects.filter(quota_id=self.object.pk).values_list('item_id', flat=True))
|
||||
) | ( # Orders for items which do have any variations
|
||||
Q(variation__in=Quota.variations.through.objects.filter(quota_id=self.object.pk).values_list(
|
||||
'itemvariation_id', flat=True))
|
||||
)
|
||||
) | Q(quota=self.object)
|
||||
) &
|
||||
Q(redeemed__lt=F('max_usages'))
|
||||
).exists()
|
||||
if self.object.closed:
|
||||
|
||||
@@ -18,6 +18,7 @@ from i18nfield.strings import LazyI18nString
|
||||
from pretix.base.forms import SafeSessionWizardView
|
||||
from pretix.base.i18n import language
|
||||
from pretix.base.models import Event, EventMetaValue, Organizer, Quota, Team
|
||||
from pretix.base.services.quotas import QuotaAvailability
|
||||
from pretix.control.forms.event import (
|
||||
EventWizardBasicsForm, EventWizardCopyForm, EventWizardFoundationForm,
|
||||
)
|
||||
@@ -82,19 +83,27 @@ class EventList(PaginationMixin, ListView):
|
||||
self.filter_form[k] for k in self.filter_form.fields if k.startswith('meta_')
|
||||
]
|
||||
|
||||
quotas = []
|
||||
for s in ctx['events']:
|
||||
s.first_quotas = s.first_quotas[:4]
|
||||
for q in s.first_quotas:
|
||||
q.cached_avail = (
|
||||
(q.cached_availability_state, q.cached_availability_number)
|
||||
if q.cached_availability_time is not None
|
||||
else q.availability(allow_cache=True)
|
||||
quotas += list(s.first_quotas)
|
||||
|
||||
qa = QuotaAvailability(early_out=False)
|
||||
for q in quotas:
|
||||
if q.cached_availability_time is None or q.cached_availability_paid_orders is None:
|
||||
qa.queue(q)
|
||||
qa.compute()
|
||||
|
||||
for q in quotas:
|
||||
q.cached_avail = (
|
||||
qa.results[q] if q in qa.results
|
||||
else (q.cached_availability_state, q.cached_availability_number)
|
||||
)
|
||||
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
|
||||
)
|
||||
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
|
||||
|
||||
@@ -24,6 +24,7 @@ from pretix.base.models.items import (
|
||||
ItemVariation, Quota, SubEventItem, SubEventItemVariation,
|
||||
)
|
||||
from pretix.base.reldate import RelativeDate, RelativeDateWrapper
|
||||
from pretix.base.services.quotas import QuotaAvailability
|
||||
from pretix.control.forms.checkin import CheckinListForm
|
||||
from pretix.control.forms.filter import SubEventFilterForm
|
||||
from pretix.control.forms.item import QuotaForm
|
||||
@@ -68,19 +69,28 @@ class SubEventList(EventPermissionRequiredMixin, PaginationMixin, ListView):
|
||||
def get_context_data(self, **kwargs):
|
||||
ctx = super().get_context_data(**kwargs)
|
||||
ctx['filter_form'] = self.filter_form
|
||||
|
||||
quotas = []
|
||||
for s in ctx['subevents']:
|
||||
s.first_quotas = s.first_quotas[:4]
|
||||
for q in s.first_quotas:
|
||||
q.cached_avail = (
|
||||
(q.cached_availability_state, q.cached_availability_number)
|
||||
if q.cached_availability_time is not None
|
||||
else q.availability(allow_cache=True)
|
||||
quotas += list(s.first_quotas)
|
||||
|
||||
qa = QuotaAvailability(early_out=False)
|
||||
for q in quotas:
|
||||
if q.cached_availability_time is None or q.cached_availability_paid_orders is None:
|
||||
qa.queue(q)
|
||||
qa.compute()
|
||||
|
||||
for q in quotas:
|
||||
q.cached_avail = (
|
||||
qa.results[q] if q in qa.results
|
||||
else (q.cached_availability_state, q.cached_availability_number)
|
||||
)
|
||||
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
|
||||
)
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user