Repository: svthalia/concrexit

Issue:
API filters for sales shifts
### Is your feature request related to a problem? Please describe.
Thadmin should be able to query shifts in multiple ways. We should have filters for this

### Describe the solution you'd like
API filters

### Motivation

### Describe alternatives you've considered

### Additional context



Discussion:
https://github.com/svthalia/thadmin/issues/78 Is also relevant
It would be nice to know what filters exactly, because I have no clue what filters we'd need
Sorry should have elaborated on this:

- filter on whether a shift is locked or not
- filter on whether a shift is active or not
- filter on start/end of a shift


Resolving patch:
diff --git a/website/sales/api/v2/admin/views.py b/website/sales/api/v2/admin/views.py
--- a/website/sales/api/v2/admin/views.py
+++ b/website/sales/api/v2/admin/views.py
@@ -1,9 +1,10 @@
 from oauth2_provider.contrib.rest_framework import IsAuthenticatedOrTokenHasScope
-from rest_framework import filters, exceptions
+from rest_framework import filters as framework_filters, exceptions
 from rest_framework.exceptions import PermissionDenied
 from rest_framework.generics import get_object_or_404
 from rest_framework.permissions import DjangoModelPermissions
 
+from sales.api.v2 import filters
 from sales.api.v2.admin.permissions import IsManager
 from sales.api.v2.admin.serializers.order import OrderSerializer, OrderListSerializer
 from sales.api.v2.admin.serializers.shift import ShiftSerializer
@@ -24,8 +25,11 @@ class ShiftListView(AdminListAPIView):
     serializer_class = ShiftSerializer
     queryset = Shift.objects.all()
     filter_backends = (
-        filters.OrderingFilter,
-        filters.SearchFilter,
+        framework_filters.OrderingFilter,
+        framework_filters.SearchFilter,
+        filters.ShiftActiveFilter,
+        filters.ShiftLockedFilter,
+        filters.ShiftDateFilter,
     )
     ordering_fields = ("start", "end")
     permission_classes = [IsAuthenticatedOrTokenHasScope, DjangoModelPermissions]
diff --git a/website/sales/api/v2/filters.py b/website/sales/api/v2/filters.py
new file mode 100644
--- /dev/null
+++ b/website/sales/api/v2/filters.py
@@ -0,0 +1,84 @@
+from distutils.util import strtobool
+
+from django.utils import timezone
+from rest_framework import filters
+
+from utils.snippets import extract_date_range
+
+
+class ShiftActiveFilter(filters.BaseFilterBackend):
+    """Allows you to filter by active status."""
+
+    def filter_queryset(self, request, queryset, view):
+        active = request.query_params.get("active", None)
+
+        if active is not None:
+            queryset = queryset.filter(active=strtobool(active))
+
+        return queryset
+
+    def get_schema_operation_parameters(self, view):
+        return [
+            {
+                "name": "active",
+                "required": False,
+                "in": "query",
+                "description": "Filter by active status",
+                "schema": {"type": "boolean",},
+            }
+        ]
+
+
+class ShiftLockedFilter(filters.BaseFilterBackend):
+    """Allows you to filter by locked status."""
+
+    def filter_queryset(self, request, queryset, view):
+        locked = request.query_params.get("locked", None)
+
+        if locked is not None:
+            queryset = queryset.filter(locked=strtobool(locked))
+
+        return queryset
+
+    def get_schema_operation_parameters(self, view):
+        return [
+            {
+                "name": "locked",
+                "required": False,
+                "in": "query",
+                "description": "Filter by locked status",
+                "schema": {"type": "boolean",},
+            }
+        ]
+
+
+class ShiftDateFilter(filters.BaseFilterBackend):
+    """Allows you to filter by event start/end dates."""
+
+    def filter_queryset(self, request, queryset, view):
+        start, end = extract_date_range(request, allow_empty=True)
+
+        if start is not None:
+            queryset = queryset.filter(end__gte=start)
+        if end is not None:
+            queryset = queryset.filter(start__lte=end)
+
+        return queryset
+
+    def get_schema_operation_parameters(self, view):
+        return [
+            {
+                "name": "start",
+                "required": False,
+                "in": "query",
+                "description": "Filter shifts by ISO date, starting with this parameter (i.e. 2021-03-30T04:20:00) where `event.end >= value`",
+                "schema": {"type": "string",},
+            },
+            {
+                "name": "end",
+                "required": False,
+                "in": "query",
+                "description": "Filter shifts by ISO date, ending with this parameter (i.e. 2021-05-16T13:37:00) where `event.start <= value`",
+                "schema": {"type": "string",},
+            },
+        ]


Test patch: