From 5e4a16bd44d3f47901ec95396f2a459ab4e906ba Mon Sep 17 00:00:00 2001 From: Raphael Michel Date: Mon, 21 Oct 2019 19:03:51 +0200 Subject: [PATCH] Allow to filter event list in organizer view by meta data --- src/pretix/control/forms/filter.py | 42 +++++++++++++--- .../templates/pretixcontrol/events/index.html | 2 +- .../pretixcontrol/organizers/detail.html | 49 ++++++++++++++++--- src/pretix/control/views/organizer.py | 28 +++++++++-- 4 files changed, 102 insertions(+), 19 deletions(-) diff --git a/src/pretix/control/forms/filter.py b/src/pretix/control/forms/filter.py index 3f103f23cc..4e3651d0bb 100644 --- a/src/pretix/control/forms/filter.py +++ b/src/pretix/control/forms/filter.py @@ -10,8 +10,8 @@ from django.utils.translation import pgettext_lazy, ugettext_lazy as _ from pretix.base.forms.widgets import DatePickerWidget from pretix.base.models import ( - Checkin, Event, Invoice, Item, Order, OrderPayment, OrderPosition, - OrderRefund, Organizer, Question, QuestionAnswer, SubEvent, + Checkin, Event, EventMetaValue, Invoice, Item, Order, OrderPayment, + OrderPosition, OrderRefund, Organizer, Question, QuestionAnswer, SubEvent, ) from pretix.base.signals import register_payment_providers from pretix.control.forms.widgets import Select2 @@ -573,13 +573,21 @@ class EventFilterForm(FilterForm): def __init__(self, *args, **kwargs): request = kwargs.pop('request') + self.organizer = kwargs.pop('organizer', None) super().__init__(*args, **kwargs) - if request.user.has_active_staff_session(request.session.session_key): - self.fields['organizer'].queryset = Organizer.objects.all() + if self.organizer: + del self.fields['organizer'] + for p in self.organizer.meta_properties.all(): + self.fields['meta_{}'.format(p.name)] = forms.CharField( + label=p.name, required=False + ) else: - self.fields['organizer'].queryset = Organizer.objects.filter( - pk__in=request.user.teams.values_list('organizer', flat=True) - ) + if request.user.has_active_staff_session(request.session.session_key): + self.fields['organizer'].queryset = Organizer.objects.all() + else: + self.fields['organizer'].queryset = Organizer.objects.filter( + pk__in=request.user.teams.values_list('organizer', flat=True) + ) def filter_qs(self, qs): fdata = self.cleaned_data @@ -628,6 +636,26 @@ class EventFilterForm(FilterForm): Q(name__icontains=i18ncomp(query)) | Q(slug__icontains=query) ) + if self.organizer: + for i, p in enumerate(self.organizer.meta_properties.all()): + d = fdata.get('meta_{}'.format(p.name)) + if d: + emv_with_value = EventMetaValue.objects.filter( + event=OuterRef('pk'), + property__name=p.name, + value=d + ) + emv_with_any_value = EventMetaValue.objects.filter( + event=OuterRef('pk'), + property__name=p.name, + ) + qs = qs.annotate(**{'attr_{}'.format(i): Exists(emv_with_value)}) + filters = Q(**{'attr_{}'.format(i): True}) + if p.default == d: + qs = qs.annotate(**{'attr_{}_any'.format(i): Exists(emv_with_any_value)}) + filters |= Q(**{'attr_{}_any'.format(i): False}) + qs = qs.filter(filters) + if fdata.get('ordering'): qs = qs.order_by(self.get_order_by()) diff --git a/src/pretix/control/templates/pretixcontrol/events/index.html b/src/pretix/control/templates/pretixcontrol/events/index.html index e2d078dde5..d94d28228a 100644 --- a/src/pretix/control/templates/pretixcontrol/events/index.html +++ b/src/pretix/control/templates/pretixcontrol/events/index.html @@ -89,7 +89,7 @@
{{ e.slug }} {% for k, v in e.meta_data.items %} {% if v %} - · {{ k }}: {{ v }} + · {{ k }}: {{ v }} {% endif %} {% endfor %} diff --git a/src/pretix/control/templates/pretixcontrol/organizers/detail.html b/src/pretix/control/templates/pretixcontrol/organizers/detail.html index 2fad61474f..0846f87d4b 100644 --- a/src/pretix/control/templates/pretixcontrol/organizers/detail.html +++ b/src/pretix/control/templates/pretixcontrol/organizers/detail.html @@ -7,17 +7,46 @@ {% if "can_create_events" in request.orgapermset %}

- + {% trans "Create a new event" %}

{% endif %} - {% if events|length == 0 %} + {% if events|length == 0 and not filter_form.filtered %}

{% trans "You currently do not have access to any events." %}

{% else %} +
+
+

{% trans "Filter" %}

+
+
+
+
+
+ {% bootstrap_field filter_form.query layout='inline' %} +
+
+ {% bootstrap_field filter_form.status layout='inline' %} +
+ {% for mf in meta_fields %} +
+ {% bootstrap_field mf layout='inline' %} +
+ {% endfor %} +
+
+
+ +
+
+
+
@@ -37,6 +66,12 @@
{{ e.name }} +
{{ e.slug }} + {% for k, v in e.meta_data.items %} + {% if v %} + · {{ k }}: {{ v }} + {% endif %} + {% endfor %}
{% if e.has_subevents %} @@ -62,9 +97,9 @@ {% if not e.live %} {% trans "Shop disabled" %} - {% elif e.presale_has_ended %} + {% elif e.presale_has_ended %} {% trans "Presale over" %} - {% elif not e.presale_is_running %} + {% elif not e.presale_is_running %} {% trans "Presale not started" %} {% else %} {% trans "On sale" %} @@ -72,13 +107,13 @@ + class="btn btn-sm btn-default" title="{% trans "Open event dashboard" %}" + data-toggle="tooltip"> {% if "can_create_events" in request.orgapermset %} + title="{% trans "Clone event" %}" data-toggle="tooltip"> {% endif %} diff --git a/src/pretix/control/views/organizer.py b/src/pretix/control/views/organizer.py index 3d9a5a0b07..cf250d9bed 100644 --- a/src/pretix/control/views/organizer.py +++ b/src/pretix/control/views/organizer.py @@ -7,7 +7,9 @@ from django.contrib import messages from django.core.exceptions import PermissionDenied, ValidationError from django.core.files import File from django.db import transaction -from django.db.models import Count, DecimalField, Max, Min, ProtectedError, Sum +from django.db.models import ( + Count, DecimalField, Max, Min, Prefetch, ProtectedError, Sum, +) from django.db.models.functions import Coalesce, Greatest from django.forms import inlineformset_factory from django.http import JsonResponse @@ -25,10 +27,12 @@ from pretix.base.auth import get_auth_backends from pretix.base.models import ( Device, GiftCard, Organizer, Team, TeamInvite, User, ) -from pretix.base.models.event import Event, EventMetaProperty +from pretix.base.models.event import Event, EventMetaProperty, EventMetaValue from pretix.base.models.organizer import TeamAPIToken from pretix.base.services.mail import SendMailException, mail -from pretix.control.forms.filter import GiftCardFilterForm, OrganizerFilterForm +from pretix.control.forms.filter import ( + EventFilterForm, GiftCardFilterForm, OrganizerFilterForm, +) from pretix.control.forms.organizer import ( DeviceForm, EventMetaPropertyForm, GiftCardCreateForm, OrganizerDeleteForm, OrganizerForm, OrganizerSettingsForm, OrganizerUpdateForm, TeamForm, @@ -105,7 +109,13 @@ class OrganizerDetail(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin def get_queryset(self): qs = self.request.user.get_events_with_any_permission(self.request).select_related('organizer').prefetch_related( - '_settings_objects', 'organizer___settings_objects' + 'organizer', '_settings_objects', 'organizer___settings_objects', + 'organizer__meta_properties', + Prefetch( + 'meta_values', + EventMetaValue.objects.select_related('property'), + to_attr='meta_values_cached' + ) ).filter(organizer=self.request.organizer).order_by('-date_from') qs = qs.annotate( min_from=Min('subevents__date_from'), @@ -116,10 +126,20 @@ class OrganizerDetail(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin order_from=Coalesce('min_from', 'date_from'), order_to=Coalesce('max_fromto', 'max_to', 'max_from', 'date_to', 'date_from'), ) + if self.filter_form.is_valid(): + qs = self.filter_form.filter_qs(qs) return qs + @cached_property + def filter_form(self): + return EventFilterForm(data=self.request.GET, request=self.request, organizer=self.organizer) + def get_context_data(self, **kwargs): ctx = super().get_context_data(**kwargs) + ctx['filter_form'] = self.filter_form + ctx['meta_fields'] = [ + self.filter_form['meta_{}'.format(p.name)] for p in self.organizer.meta_properties.all() + ] return ctx