Instead of applying a list of __in filters at once, iterate over the list and apply them individually. Only then will the tickets be filtered to match them all as intended.
159 lines
4.7 KiB
Python
159 lines
4.7 KiB
Python
# -*- encoding: utf-8 -*-
|
|
|
|
import logging
|
|
from typing import Any
|
|
|
|
from django.db.models import Q
|
|
from django.http import HttpRequest
|
|
from django.core.cache import cache
|
|
from django.views.decorators.cache import cache_page
|
|
from django.utils.decorators import method_decorator
|
|
from django.core.exceptions import ValidationError
|
|
from rest_framework.views import APIView
|
|
from rest_framework.response import Response
|
|
from rest_framework import status, permissions
|
|
from rest_framework.pagination import PageNumberPagination
|
|
|
|
from apps.home.models import Ticket, TicketPriority, TicketTag
|
|
from apps.authentication.models import Department
|
|
from .serializers import (
|
|
TicketSerializer, TicketTagSerializer, TicketPrioritySerializer,
|
|
UserSerializer
|
|
)
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
def respond_error(error: Exception, status: Any) -> Response:
|
|
logging.error(error)
|
|
return Response({
|
|
"detail": str(error),
|
|
"error": error.__class__.__name__
|
|
}, status=status)
|
|
|
|
|
|
class TicketPaginiation(PageNumberPagination):
|
|
page_size = 25
|
|
page_size_query_param = "page_size"
|
|
max_page_size = 100
|
|
|
|
|
|
class TicketListApiView(APIView):
|
|
permission_classes = [permissions.IsAuthenticated]
|
|
pagination_class = TicketPaginiation
|
|
|
|
ALLOWED_FILTERS = (
|
|
"uuid__in", "priority__in", "tags__in", "author__department__in", "search"
|
|
)
|
|
MATCHER_MAP = {
|
|
"contains": lambda k, v: {k: v[0]},
|
|
"in": lambda k, v: {k: v}
|
|
}
|
|
|
|
# @method_decorator(cache_page(60 * 5))
|
|
def get(self, request: HttpRequest) -> Response:
|
|
"""Returns a response containing tickets matching the given filters."""
|
|
|
|
try:
|
|
return self._get_tickets(request)
|
|
|
|
except KeyError as error:
|
|
return respond_error(error, status.HTTP_400_BAD_REQUEST)
|
|
|
|
except ValidationError as error:
|
|
return respond_error(error, status.HTTP_400_BAD_REQUEST)
|
|
|
|
def post(self, request):
|
|
"""Create a new ticket"""
|
|
|
|
serializer = TicketSerializer(data={
|
|
|
|
})
|
|
if not serializer.is_valid():
|
|
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
|
|
|
serializer.save()
|
|
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
|
|
|
def _get_tickets(self, request: HttpRequest) -> Response:
|
|
"""Returns a response containing tickets matching the given filters.
|
|
|
|
Parameters
|
|
----------
|
|
request : HttpRequest
|
|
The request containing the filters.
|
|
|
|
Returns
|
|
-------
|
|
Response
|
|
A response containing the tickets data.
|
|
|
|
Raises
|
|
------
|
|
KeyError
|
|
If any given filter key isn't in `self.ALLOWED_FILTERS`.
|
|
ValidationError
|
|
If any ValidationErrors are raised by Django while applying filters.
|
|
"""
|
|
|
|
queryset = Ticket.objects.all()
|
|
|
|
for key, values in request.GET.lists():
|
|
key = key.removesuffix("[]")
|
|
|
|
if key not in self.ALLOWED_FILTERS:
|
|
raise KeyError(key)
|
|
|
|
if not key.endswith("__in"):
|
|
values = values[0]
|
|
|
|
if key == "search":
|
|
queryset = queryset.filter(
|
|
Q(**{"title__contains": values}) |
|
|
Q(**{"description__contains": values})
|
|
)
|
|
continue
|
|
|
|
if "all" in values:
|
|
continue
|
|
|
|
for value in values:
|
|
filter_kwargs = {key: [value]}
|
|
queryset = queryset.filter(Q(**filter_kwargs))
|
|
|
|
tickets = queryset.order_by("-create_timestamp")
|
|
serializer = TicketSerializer(tickets, many=True)
|
|
response_data = serializer.data
|
|
|
|
log.debug("Query successful showing %s results", tickets.count())
|
|
return Response(response_data, status=status.HTTP_200_OK)
|
|
|
|
|
|
class FilterCountApiView(APIView):
|
|
permission_classes = [permissions.IsAuthenticated]
|
|
|
|
@method_decorator(cache_page(60 * 5))
|
|
def get(self, request):
|
|
|
|
priorities = TicketPriority.objects.all()
|
|
tags = TicketTag.objects.all()
|
|
departments = Department.objects.all()
|
|
|
|
tickets = Ticket.objects.all()
|
|
|
|
data = {
|
|
"priority_counts": {},
|
|
"tag_counts": {},
|
|
"department_counts": {},
|
|
"ticket_count": tickets.count()
|
|
}
|
|
|
|
for priority in priorities:
|
|
data["priority_counts"][str(priority.uuid)] = tickets.filter(priority=priority).count()
|
|
|
|
for tag in tags:
|
|
data["tag_counts"][str(tag.uuid)] = tickets.filter(tags__in=[tag]).count()
|
|
|
|
for department in departments:
|
|
data["department_counts"][str(department.uuid)] = tickets.filter(author__department=department).count()
|
|
|
|
return Response(data, status=status.HTTP_200_OK) |