Allow to filter event list in organizer view by meta data

This commit is contained in:
Raphael Michel
2019-10-21 19:03:51 +02:00
parent b219faafaa
commit 5e4a16bd44
4 changed files with 102 additions and 19 deletions

View File

@@ -10,8 +10,8 @@ from django.utils.translation import pgettext_lazy, ugettext_lazy as _
from pretix.base.forms.widgets import DatePickerWidget from pretix.base.forms.widgets import DatePickerWidget
from pretix.base.models import ( from pretix.base.models import (
Checkin, Event, Invoice, Item, Order, OrderPayment, OrderPosition, Checkin, Event, EventMetaValue, Invoice, Item, Order, OrderPayment,
OrderRefund, Organizer, Question, QuestionAnswer, SubEvent, OrderPosition, OrderRefund, Organizer, Question, QuestionAnswer, SubEvent,
) )
from pretix.base.signals import register_payment_providers from pretix.base.signals import register_payment_providers
from pretix.control.forms.widgets import Select2 from pretix.control.forms.widgets import Select2
@@ -573,13 +573,21 @@ class EventFilterForm(FilterForm):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
request = kwargs.pop('request') request = kwargs.pop('request')
self.organizer = kwargs.pop('organizer', None)
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
if request.user.has_active_staff_session(request.session.session_key): if self.organizer:
self.fields['organizer'].queryset = Organizer.objects.all() 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: else:
self.fields['organizer'].queryset = Organizer.objects.filter( if request.user.has_active_staff_session(request.session.session_key):
pk__in=request.user.teams.values_list('organizer', flat=True) 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): def filter_qs(self, qs):
fdata = self.cleaned_data fdata = self.cleaned_data
@@ -628,6 +636,26 @@ class EventFilterForm(FilterForm):
Q(name__icontains=i18ncomp(query)) | Q(slug__icontains=query) 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'): if fdata.get('ordering'):
qs = qs.order_by(self.get_order_by()) qs = qs.order_by(self.get_order_by())

View File

@@ -89,7 +89,7 @@
<br><small>{{ e.slug }}</small> <br><small>{{ e.slug }}</small>
{% for k, v in e.meta_data.items %} {% for k, v in e.meta_data.items %}
{% if v %} {% if v %}
<span class="text-muted">&middot; {{ k }}: {{ v }}</span> <small class="text-muted">&middot; {{ k }}: {{ v }}</small>
{% endif %} {% endif %}
{% endfor %} {% endfor %}
</td> </td>

View File

@@ -7,17 +7,46 @@
</h1> </h1>
{% if "can_create_events" in request.orgapermset %} {% if "can_create_events" in request.orgapermset %}
<p> <p>
<a href="{% url "control:events.add" %}?organizer={{ request.organizer.slug }}" class="btn btn-default"> <a href="{% url "control:events.add" %}?organizer={{ request.organizer.slug }}" class="btn btn-primary">
<span class="fa fa-plus"></span> <span class="fa fa-plus"></span>
{% trans "Create a new event" %} {% trans "Create a new event" %}
</a> </a>
</p> </p>
{% endif %} {% endif %}
{% if events|length == 0 %} {% if events|length == 0 and not filter_form.filtered %}
<p> <p>
<em>{% trans "You currently do not have access to any events." %}</em> <em>{% trans "You currently do not have access to any events." %}</em>
</p> </p>
{% else %} {% else %}
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">{% trans "Filter" %}</h3>
</div>
<div class="panel-body">
<form class="" action="" method="get">
<div class="row filter-form">
<div class="col-md-3 col-sm-6 col-xs-12">
{% bootstrap_field filter_form.query layout='inline' %}
</div>
<div class="col-md-3 col-sm-6 col-xs-12">
{% bootstrap_field filter_form.status layout='inline' %}
</div>
{% for mf in meta_fields %}
<div class="col-md-3 col-sm-6 col-xs-12">
{% bootstrap_field mf layout='inline' %}
</div>
{% endfor %}
</div>
<br>
<div class="text-right">
<button class="btn btn-primary" type="submit">
<span class="fa fa-filter"></span>
<span class="hidden-md">{% trans "Filter" %}</span>
</button>
</div>
</form>
</div>
</div>
<table class="table table-condensed table-hover"> <table class="table table-condensed table-hover">
<thead> <thead>
<tr> <tr>
@@ -37,6 +66,12 @@
<td> <td>
<strong><a <strong><a
href="{% url "control:event.index" organizer=e.organizer.slug event=e.slug %}">{{ e.name }}</a></strong> href="{% url "control:event.index" organizer=e.organizer.slug event=e.slug %}">{{ e.name }}</a></strong>
<br><small>{{ e.slug }}</small>
{% for k, v in e.meta_data.items %}
{% if v %}
<small class="text-muted">&middot; {{ k }}: {{ v }}</small>
{% endif %}
{% endfor %}
</td> </td>
<td> <td>
{% if e.has_subevents %} {% if e.has_subevents %}
@@ -62,9 +97,9 @@
<td> <td>
{% if not e.live %} {% if not e.live %}
<span class="label label-danger">{% trans "Shop disabled" %}</span> <span class="label label-danger">{% trans "Shop disabled" %}</span>
{% elif e.presale_has_ended %} {% elif e.presale_has_ended %}
<span class="label label-warning">{% trans "Presale over" %}</span> <span class="label label-warning">{% trans "Presale over" %}</span>
{% elif not e.presale_is_running %} {% elif not e.presale_is_running %}
<span class="label label-warning">{% trans "Presale not started" %}</span> <span class="label label-warning">{% trans "Presale not started" %}</span>
{% else %} {% else %}
<span class="label label-success">{% trans "On sale" %}</span> <span class="label label-success">{% trans "On sale" %}</span>
@@ -72,13 +107,13 @@
</td> </td>
<td class="text-right flip"> <td class="text-right flip">
<a href="{% url "control:event.index" organizer=e.organizer.slug event=e.slug %}" <a href="{% url "control:event.index" organizer=e.organizer.slug event=e.slug %}"
class="btn btn-sm btn-default" title="{% trans "Open event dashboard" %}" class="btn btn-sm btn-default" title="{% trans "Open event dashboard" %}"
data-toggle="tooltip"> data-toggle="tooltip">
<span class="fa fa-eye"></span> <span class="fa fa-eye"></span>
</a> </a>
{% if "can_create_events" in request.orgapermset %} {% if "can_create_events" in request.orgapermset %}
<a href="{% url "control:events.add" %}?clone={{ e.pk }}" class="btn btn-sm btn-default" <a href="{% url "control:events.add" %}?clone={{ e.pk }}" class="btn btn-sm btn-default"
title="{% trans "Clone event" %}" data-toggle="tooltip"> title="{% trans "Clone event" %}" data-toggle="tooltip">
<span class="fa fa-copy"></span> <span class="fa fa-copy"></span>
</a> </a>
{% endif %} {% endif %}

View File

@@ -7,7 +7,9 @@ from django.contrib import messages
from django.core.exceptions import PermissionDenied, ValidationError from django.core.exceptions import PermissionDenied, ValidationError
from django.core.files import File from django.core.files import File
from django.db import transaction 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.db.models.functions import Coalesce, Greatest
from django.forms import inlineformset_factory from django.forms import inlineformset_factory
from django.http import JsonResponse from django.http import JsonResponse
@@ -25,10 +27,12 @@ from pretix.base.auth import get_auth_backends
from pretix.base.models import ( from pretix.base.models import (
Device, GiftCard, Organizer, Team, TeamInvite, User, 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.models.organizer import TeamAPIToken
from pretix.base.services.mail import SendMailException, mail 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 ( from pretix.control.forms.organizer import (
DeviceForm, EventMetaPropertyForm, GiftCardCreateForm, OrganizerDeleteForm, DeviceForm, EventMetaPropertyForm, GiftCardCreateForm, OrganizerDeleteForm,
OrganizerForm, OrganizerSettingsForm, OrganizerUpdateForm, TeamForm, OrganizerForm, OrganizerSettingsForm, OrganizerUpdateForm, TeamForm,
@@ -105,7 +109,13 @@ class OrganizerDetail(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin
def get_queryset(self): def get_queryset(self):
qs = self.request.user.get_events_with_any_permission(self.request).select_related('organizer').prefetch_related( 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') ).filter(organizer=self.request.organizer).order_by('-date_from')
qs = qs.annotate( qs = qs.annotate(
min_from=Min('subevents__date_from'), min_from=Min('subevents__date_from'),
@@ -116,10 +126,20 @@ class OrganizerDetail(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin
order_from=Coalesce('min_from', 'date_from'), order_from=Coalesce('min_from', 'date_from'),
order_to=Coalesce('max_fromto', 'max_to', 'max_from', 'date_to', '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 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): def get_context_data(self, **kwargs):
ctx = super().get_context_data(**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 return ctx