From b6078d52729fa88dea9d9d69c30da5ef9243cdee Mon Sep 17 00:00:00 2001 From: Raphael Michel Date: Mon, 19 Jul 2021 13:57:17 +0200 Subject: [PATCH] Allow to filter and sort the list of devices --- src/pretix/control/forms/filter.py | 67 ++++++++++++++++++- .../pretixcontrol/organizers/devices.html | 48 +++++++++++-- src/pretix/control/views/organizer.py | 18 ++++- 3 files changed, 124 insertions(+), 9 deletions(-) diff --git a/src/pretix/control/forms/filter.py b/src/pretix/control/forms/filter.py index 10ff9fa884..2ea1d9567f 100644 --- a/src/pretix/control/forms/filter.py +++ b/src/pretix/control/forms/filter.py @@ -42,7 +42,7 @@ from django.conf import settings from django.db.models import ( Count, Exists, F, Max, Model, OrderBy, OuterRef, Q, QuerySet, ) -from django.db.models.functions import Coalesce, ExtractWeekDay +from django.db.models.functions import Coalesce, ExtractWeekDay, Upper from django.urls import reverse, reverse_lazy from django.utils.formats import date_format, localize from django.utils.functional import cached_property @@ -1950,3 +1950,68 @@ class CheckinFilterForm(FilterForm): qs = qs.filter(datetime__lte=fdata.get('datetime_until')) return qs + + +class DeviceFilterForm(FilterForm): + orders = { + 'name': Upper('name'), + 'device_id': 'device_id', + 'initialized': F('initialized').asc(nulls_last=True), + '-initialized': F('initialized').desc(nulls_first=True), + } + query = forms.CharField( + label=_('Search query'), + widget=forms.TextInput(attrs={ + 'placeholder': _('Search query'), + 'autofocus': 'autofocus' + }), + required=False + ) + gate = forms.ModelChoiceField( + queryset=Gate.objects.none(), + label=_('Gate'), + empty_label=_('All gates'), + required=False, + ) + software_brand = forms.ChoiceField( + label=_('Software'), + choices=[ + ('', _('All')), + ], + required=False, + ) + + def __init__(self, *args, **kwargs): + request = kwargs.pop('request') + super().__init__(*args, **kwargs) + self.fields['gate'].queryset = request.organizer.gates.all() + self.fields['software_brand'].choices = [ + ('', _('All')), + ] + [ + (f['software_brand'], f['software_brand']) for f in + request.organizer.devices.order_by().values('software_brand').annotate(c=Count('*')) + if f['software_brand'] + ] + + def filter_qs(self, qs): + fdata = self.cleaned_data + + if fdata.get('query'): + query = fdata.get('query') + qs = qs.filter( + Q(name__icontains=query) + | Q(unique_serial__icontains=query) + | Q(hardware_brand__icontains=query) + | Q(hardware_model__icontains=query) + | Q(software_brand__icontains=query) + ) + + if fdata.get('gate'): + qs = qs.filter(gate=fdata['gate']) + + if fdata.get('ordering'): + qs = qs.order_by(self.get_order_by()) + else: + qs = qs.order_by('-device_id') + + return qs diff --git a/src/pretix/control/templates/pretixcontrol/organizers/devices.html b/src/pretix/control/templates/pretixcontrol/organizers/devices.html index f9d07f9ff8..bf94a73985 100644 --- a/src/pretix/control/templates/pretixcontrol/organizers/devices.html +++ b/src/pretix/control/templates/pretixcontrol/organizers/devices.html @@ -1,6 +1,7 @@ {% extends "pretixcontrol/organizers/base.html" %} {% load i18n %} {% load bootstrap3 %} +{% load urlreplace %} {% block inner %}

{% trans "Connected devices" %} @@ -11,7 +12,7 @@ your account. {% endblocktrans %}

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

{% blocktrans trimmed %} @@ -23,6 +24,30 @@ class="btn btn-primary btn-lg"> {% trans "Connect a device" %}

{% else %} +
+
+

{% trans "Filter" %}

+
+
+
+
+ {% bootstrap_field filter_form.query %} +
+
+ {% bootstrap_field filter_form.gate %} +
+
+ {% bootstrap_field filter_form.software_brand %} +
+
+
+ +
+
+

{% trans "Connect a device" %} @@ -31,11 +56,20 @@ - - + + + + - + + @@ -50,8 +84,12 @@ {% if d.revoked %}{% endif %} {{ d.name }} {% if d.revoked %}{% endif %} + {% if d.gate %} +
+ {{ d.gate.name }} + {% endif %}
- {{ d.unique_serial }} + {{ d.unique_serial }}
{% trans "Device ID" %}{% trans "Name" %}{% trans "Device ID" %} + + {% trans "Name" %} + + {% trans "Hardware model" %} {% trans "Software" %}{% trans "Setup date" %}{% trans "Setup date" %} + + {% trans "Events" %}
{{ d.hardware_brand|default_if_none:"" }} {{ d.hardware_model|default_if_none:"" }} diff --git a/src/pretix/control/views/organizer.py b/src/pretix/control/views/organizer.py index 151bc35980..c98b535f91 100644 --- a/src/pretix/control/views/organizer.py +++ b/src/pretix/control/views/organizer.py @@ -84,7 +84,7 @@ from pretix.base.signals import register_multievent_data_exporters from pretix.base.templatetags.rich_text import markdown_compile_email from pretix.base.views.tasks import AsyncAction from pretix.control.forms.filter import ( - CustomerFilterForm, EventFilterForm, GiftCardFilterForm, + CustomerFilterForm, DeviceFilterForm, EventFilterForm, GiftCardFilterForm, OrganizerFilterForm, TeamFilterForm, ) from pretix.control.forms.orders import ExporterForm @@ -824,9 +824,21 @@ class DeviceListView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, context_object_name = 'devices' def get_queryset(self): - return self.request.organizer.devices.prefetch_related( - 'limit_events' + qs = self.request.organizer.devices.prefetch_related( + 'limit_events', 'gate', ).order_by('revoked', '-device_id') + if self.filter_form.is_valid(): + qs = self.filter_form.filter_qs(qs) + return qs + + def get_context_data(self, **kwargs): + ctx = super().get_context_data(**kwargs) + ctx['filter_form'] = self.filter_form + return ctx + + @cached_property + def filter_form(self): + return DeviceFilterForm(data=self.request.GET, request=self.request) class DeviceCreateView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, CreateView):