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" %}
+
+
+
{% trans "Connect a device" %}
@@ -31,11 +56,20 @@
- | {% trans "Device ID" %} |
- {% trans "Name" %} |
+ {% trans "Device ID" %}
+
+ |
+
+ {% trans "Name" %}
+
+ |
+
{% trans "Hardware model" %} |
{% trans "Software" %} |
- {% trans "Setup date" %} |
+ {% trans "Setup date" %}
+
+ |
+
{% trans "Events" %} |
|
@@ -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 }}
{{ 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):
|