From 891b71d6c12eacb7712e297ee9eca94e700f51c5 Mon Sep 17 00:00:00 2001
From: Lucas Brandstaetter <lucas@brandstaetter.tech>
Date: Thu, 28 Nov 2024 02:01:10 +0100
Subject: [PATCH] Update assembly team views

- Use FilteredListView and AlphabeticalPaginatorView for assembly team
  views
  - Add multi-tag filtering to assembly list view
  - Migrate old filters to new system
---
 src/backoffice/views/assemblyteam.py | 58 ++++++++++++++--------------
 1 file changed, 28 insertions(+), 30 deletions(-)

diff --git a/src/backoffice/views/assemblyteam.py b/src/backoffice/views/assemblyteam.py
index 84bcd9020..b679ddb7c 100644
--- a/src/backoffice/views/assemblyteam.py
+++ b/src/backoffice/views/assemblyteam.py
@@ -14,12 +14,13 @@ from django.urls import reverse
 from django.utils.html import format_html
 from django.utils.translation import gettext
 from django.utils.translation import gettext_lazy as _
-from django.views.generic import DetailView, ListView, View
+from django.views.generic import DetailView, View
 
 from core.models import ActivityLogChange, ActivityLogEntry, Room
 from core.models.assemblies import Assembly, AssemblyMember
 from core.models.conference import ConferenceExportCache
 from core.models.users import UserCommunicationChannel
+from core.views.list_views import AlphabeticalPaginatorView, FilteredListView
 
 from backoffice.templatetags.c3assemblies import get_language_item
 
@@ -38,13 +39,13 @@ class AssemblyTeamMixin(ConferenceLoginRequiredMixin):
     status_field = 'state_assembly'
 
     MODES = {
-        'all': (Q(), _('nav_assemblies_all')),
-        'accepted': (Q(state_assembly__in=Assembly.PUBLIC_STATES), _('nav_assemblies_accepted')),
-        'pending': (Q(state_assembly__in=[Assembly.State.REGISTERED]), _('nav_assemblies_pending')),
-        'planned': (Q(state_assembly__in=[Assembly.State.PLANNED]), _('nav_assemblies_planned')),
-        'rejected': (Q(state_assembly__in=[Assembly.State.REJECTED]), _('nav_assemblies_rejected')),
-        'hidden': (Q(state_assembly__in=[Assembly.State.HIDDEN]), _('nav_assemblies_hidden')),
-        'not_selected': (Q(state_assembly__in=[Assembly.State.NONE]), _('nav_assemblies_not_selected')),
+        'all': (('QMode', Q()), _('nav_assemblies_all')),
+        'accepted': (('QMode', Q(state_assembly__in=Assembly.PUBLIC_STATES)), _('nav_assemblies_accepted')),
+        'pending': (('QMode', Q(state_assembly__in=[Assembly.State.REGISTERED])), _('nav_assemblies_pending')),
+        'planned': (('QMode', Q(state_assembly__in=[Assembly.State.PLANNED])), _('nav_assemblies_planned')),
+        'rejected': (('QMode', Q(state_assembly__in=[Assembly.State.REJECTED])), _('nav_assemblies_rejected')),
+        'hidden': (('QMode', Q(state_assembly__in=[Assembly.State.HIDDEN])), _('nav_assemblies_hidden')),
+        'not_selected': (('QMode', Q(state_assembly__in=[Assembly.State.NONE])), _('nav_assemblies_not_selected')),
     }
 
     def get_context_data(self, **kwargs):
@@ -75,7 +76,7 @@ class AssemblyTeamMixin(ConferenceLoginRequiredMixin):
                 {
                     'mode': m,
                     'caption': t,
-                    'count': self.conference.assemblies.filter(q).count(),
+                    'count': self.conference.assemblies.filter(q[1]).count(),
                     'link': reverse(self.base_view_name) + '?mode=' + m,
                 }
             )
@@ -116,32 +117,39 @@ class AssemblyTeamMixin(ConferenceLoginRequiredMixin):
         return context
 
 
-class AssembliesListMixin(AssemblyTeamMixin):
+class AssembliesListMixin(AssemblyTeamMixin, FilteredListView):
     default_assemblies_mode = 'all'
+    filters = {
+        'tag': ('tags__tag__slug', 'list'),
+    }
 
     def get_queryset(self, **kwargs):
         # get the mode
         mode = (self.request.POST if self.request.method == 'POST' else self.request.GET).get('mode', self.default_assemblies_mode)
+        query_filters = {}
 
-        qs = Assembly.objects.associated_with_user(conference=self.conference, user=self.request.user, staff_can_see=True)
         if mode == 'not_selected':
             pass
         elif self.active_page == 'assemblies':
-            qs = qs.exclude(state_assembly=Assembly.State.NONE)
+            query_filters['!state_assembly'] = Assembly.State.NONE
         elif self.active_page == 'channels':
-            qs = qs.exclude(state_channel=Assembly.State.NONE)
+            query_filters['!state_channel'] = Assembly.State.NONE
         else:
             logging.warning('AssembliesListMixin: unexpected active_page="%s"', self.active_page)
-            qs = Assembly.objects.none()
+            return Assembly.objects.none()
 
         if mode in self.MODES:
-            qs = qs.filter(self.MODES[mode][0])
+            query_filters[self.MODES[mode][0][0]] = self.MODES[mode][0][1]
         else:
             messages.warning(self.request, f'unknown mode "{mode}", using "all"')
             mode = 'all'
+            query_filters[self.MODES[mode][0][0]] = self.MODES[mode][0][1]
         self.assemblies_mode = mode
 
-        # done
+        qs = super().get_queryset(
+            Assembly.objects.associated_with_user(conference=self.conference, user=self.request.user, staff_can_see=True), query_filters=query_filters
+        )
+
         return qs
 
 
@@ -200,13 +208,13 @@ class AssemblyView(SingleAssemblyTeamMixin, DetailView):
         return redirect('backoffice:assemblyteam-detail', self.assembly.id)
 
 
-class AssembliesView(AssembliesListMixin, ListView):
+class AssembliesView(AssembliesListMixin, AlphabeticalPaginatorView):
     template_name = 'backoffice/assembly_list.html'
 
     def get_context_data(self, **kwargs):
         context = super().get_context_data(**kwargs)
 
-        context['filter_tag'] = self.selected_tag
+        # context['filter_tag'] = self.selected_tag
 
         context['mode'] = self.assemblies_mode
         context['mode_display'] = self.MODES[self.assemblies_mode][1]
@@ -222,19 +230,8 @@ class AssembliesView(AssembliesListMixin, ListView):
 
         return context
 
-    def get_queryset(self, *args, **kwargs):
-        qs = super().get_queryset(*args, **kwargs)
-
-        # tag filtering
-        self.selected_tag = (self.request.POST if self.request.method == 'POST' else self.request.GET).get('tag')
-        if self.selected_tag is not None:
-            qs = qs.filter(tags__tag__slug=self.selected_tag, tags__tag__is_public=True)
-
-        # return the assembled QuerySet
-        return qs
-
 
-class AssembliesListsView(AssembliesListMixin, View):
+class AssembliesListsView(AssembliesListMixin):
     DEFAULT_DELIMITER_CHAR = ';'
     DEFAULT_QUOTE_CHAR = '"'
     DELIMITER_CHARS = ',;|\t=*/'
@@ -364,6 +361,7 @@ class AssembliesListsView(AssembliesListMixin, View):
 
     def get(self, *args, **kwargs):
         qs, variant, variant_name, variant_fields = self.prepare(*args, **kwargs)
+        self.object_list = qs
         if qs is None:
             assert isinstance(variant, HttpResponse)
             return variant
-- 
GitLab