From f0c7f630b2e7dfcf6a7bda913dbd21ebd6c4ec12 Mon Sep 17 00:00:00 2001
From: "J.-S. Caux" <J.S.Caux@uva.nl>
Date: Sun, 25 Oct 2020 10:53:18 +0100
Subject: [PATCH] Turn apimail.views into a package

---
 apimail/api/serializers/composed_message.py   |   5 +-
 apimail/api/views/__init__.py                 |  21 +++
 apimail/api/views/account.py                  |  32 ++++
 apimail/api/views/attachment.py               |  17 ++
 apimail/api/views/composed_message.py         |  78 ++++++++
 apimail/api/views/event.py                    |  23 +++
 .../api/{views.py => views/stored_message.py} | 166 +-----------------
 apimail/api/views/tag.py                      |  44 +++++
 apimail/models/account.py                     |   2 +
 apimail/models/composed_message.py            |   1 +
 10 files changed, 225 insertions(+), 164 deletions(-)
 create mode 100644 apimail/api/views/__init__.py
 create mode 100644 apimail/api/views/account.py
 create mode 100644 apimail/api/views/attachment.py
 create mode 100644 apimail/api/views/composed_message.py
 create mode 100644 apimail/api/views/event.py
 rename apimail/api/{views.py => views/stored_message.py} (53%)
 create mode 100644 apimail/api/views/tag.py

diff --git a/apimail/api/serializers/composed_message.py b/apimail/api/serializers/composed_message.py
index 705b46219..827ae4f05 100644
--- a/apimail/api/serializers/composed_message.py
+++ b/apimail/api/serializers/composed_message.py
@@ -9,10 +9,7 @@ from ...models import (
     ComposedMessage, ComposedMessageAPIResponse,
     AttachmentFile,
 )
-from ..serializers import (
-    ComposedMessageAPIResponseSerializer,
-    AttachmentFileSerializer
-)
+from ..serializers import AttachmentFileSerializer
 
 
 class ComposedMessageAPIResponseSerializer(serializers.ModelSerializer):
diff --git a/apimail/api/views/__init__.py b/apimail/api/views/__init__.py
new file mode 100644
index 000000000..e14b3a275
--- /dev/null
+++ b/apimail/api/views/__init__.py
@@ -0,0 +1,21 @@
+__copyright__ = "Copyright © Stichting SciPost (SciPost Foundation)"
+__license__ = "AGPL v3"
+
+
+from .account import EmailAccountListAPIView, UserEmailAccountAccessListAPIView
+
+from .attachment import AttachmentFileCreateAPIView
+
+from .event import EventListAPIView, EventRetrieveAPIView
+
+from .tag import UserTagCreateAPIView, UserTagDestroyAPIView, UserTagListAPIView
+
+from .composed_message import (
+    ComposedMessageCreateAPIView, ComposedMessageUpdateAPIView,
+    ComposedMessageDestroyAPIView, ComposedMessageListAPIView
+)
+
+from .stored_message import (
+    StoredMessageListAPIView, StoredMessageRetrieveAPIView,
+    StoredMessageUpdateReadAPIView, StoredMessageUpdateTagAPIView
+)
diff --git a/apimail/api/views/account.py b/apimail/api/views/account.py
new file mode 100644
index 000000000..1c91dbbf8
--- /dev/null
+++ b/apimail/api/views/account.py
@@ -0,0 +1,32 @@
+__copyright__ = "Copyright © Stichting SciPost (SciPost Foundation)"
+__license__ = "AGPL v3"
+
+from rest_framework.generics import ListAPIView
+from rest_framework.permissions import IsAdminUser, IsAuthenticated
+
+from ...models import EmailAccount
+
+from ..serializers import EmailAccountSerializer, EmailAccountAccessSerializer
+
+
+class EmailAccountListAPIView(ListAPIView):
+    permission_classes = (IsAdminUser,)
+    queryset = EmailAccount.objects.all()
+    serializer_class = EmailAccountSerializer
+
+
+class UserEmailAccountAccessListAPIView(ListAPIView):
+    """
+    ListAPIView returning request.user's email account accesses.
+    """
+
+    permission_classes = (IsAuthenticated,)
+    serializer_class = EmailAccountAccessSerializer
+
+    def get_queryset(self):
+        queryset = self.request.user.email_account_accesses.all()
+        if self.request.query_params.get('current', None) == 'true':
+            queryset = queryset.current()
+        if self.request.query_params.get('cansend', None) == 'true':
+            queryset = queryset.can_send()
+        return queryset
diff --git a/apimail/api/views/attachment.py b/apimail/api/views/attachment.py
new file mode 100644
index 000000000..95b70db84
--- /dev/null
+++ b/apimail/api/views/attachment.py
@@ -0,0 +1,17 @@
+__copyright__ = "Copyright © Stichting SciPost (SciPost Foundation)"
+__license__ = "AGPL v3"
+
+
+from rest_framework.generics import CreateAPIView
+from rest_framework.parsers import FormParser, MultiPartParser
+from rest_framework.permissions import IsAuthenticated
+
+from ...models import AttachmentFile
+from ..serializers import AttachmentFileSerializer
+
+
+class AttachmentFileCreateAPIView(CreateAPIView):
+    permission_classes = (IsAuthenticated,)
+    queryset = AttachmentFile.objects.all()
+    serializer_class = AttachmentFileSerializer
+    parser_classes = [FormParser, MultiPartParser,]
diff --git a/apimail/api/views/composed_message.py b/apimail/api/views/composed_message.py
new file mode 100644
index 000000000..3a4584c6f
--- /dev/null
+++ b/apimail/api/views/composed_message.py
@@ -0,0 +1,78 @@
+__copyright__ = "Copyright © Stichting SciPost (SciPost Foundation)"
+__license__ = "AGPL v3"
+
+
+from rest_framework.generics import (
+    CreateAPIView, ListAPIView, RetrieveAPIView, UpdateAPIView, DestroyAPIView
+)
+
+from rest_framework.permissions import IsAdminUser, IsAuthenticated
+from rest_framework.response import Response
+from rest_framework import status
+
+from ...models import ComposedMessage
+from ..serializers import ComposedMessageSerializer
+from ...permissions import CanHandleComposedMessage
+
+
+class ComposedMessageCreateAPIView(CreateAPIView):
+    permission_classes = (IsAuthenticated,)
+    queryset = ComposedMessage.objects.all()
+    serializer_class = ComposedMessageSerializer
+    lookup_field = 'uuid'
+
+    def create(self, request, *args, **kwargs):
+        # Override rest_framework.mixins.CreateModelMixin.create in order
+        # to include request.user in data, and attachment_uuids for serializer.create
+        data = request.data
+        data['author'] = request.user.id
+        serializer = self.get_serializer(data=data)
+        serializer.is_valid(raise_exception=True)
+        # Attachment uuids passed as extra args to be popped in serializer.create
+        serializer.save(attachment_uuids=request.data['attachment_uuids'])
+        headers = self.get_success_headers(serializer.data)
+        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
+
+
+class ComposedMessageUpdateAPIView(UpdateAPIView):
+    permission_classes = (IsAuthenticated, CanHandleComposedMessage,)
+    queryset = ComposedMessage.objects.all()
+    serializer_class = ComposedMessageSerializer
+    lookup_field = 'uuid'
+
+    def update(self, request, *args, **kwargs):
+        # Override rest_framework.mixins.CreateModelMixin.update in order
+        # to include request.user in data, and attachment_uuids for serializer.update
+        partial = kwargs.pop('partial', False)
+        instance = self.get_object()
+        serializer = self.get_serializer(instance, data=request.data, partial=partial)
+        serializer.is_valid(raise_exception=True)
+        serializer.save(attachment_uuids=request.data['attachment_uuids'])
+
+        if getattr(instance, '_prefetched_objects_cache', None):
+            # If 'prefetch_related' has been applied to a queryset, we need to
+            # forcibly invalidate the prefetch cache on the instance.
+            instance._prefetched_objects_cache = {}
+
+        return Response(serializer.data)
+
+
+class ComposedMessageDestroyAPIView(DestroyAPIView):
+    permission_classes = (IsAuthenticated, CanHandleComposedMessage,)
+    serializer_class = ComposedMessageSerializer
+    lookup_field = 'uuid'
+
+    def get_queryset(self):
+        return ComposedMessage.objects.filter_for_user(self.request.user)
+
+
+class ComposedMessageListAPIView(ListAPIView):
+    permission_classes = (IsAuthenticated,)
+    serializer_class = ComposedMessageSerializer
+    lookup_field = 'uuid'
+
+    def get_queryset(self):
+        queryset = ComposedMessage.objects.filter_for_user(self.request.user)
+        if self.request.query_params.get('status', None) == 'draft':
+            queryset = queryset.filter(status=ComposedMessage.STATUS_DRAFT)
+        return queryset
diff --git a/apimail/api/views/event.py b/apimail/api/views/event.py
new file mode 100644
index 000000000..cd62ead41
--- /dev/null
+++ b/apimail/api/views/event.py
@@ -0,0 +1,23 @@
+__copyright__ = "Copyright © Stichting SciPost (SciPost Foundation)"
+__license__ = "AGPL v3"
+
+
+from rest_framework.generics import ListAPIView, RetrieveAPIView
+from rest_framework.permissions import IsAdminUser
+
+from ...models import Event
+from ..serializers import EventSerializer
+
+
+class EventListAPIView(ListAPIView):
+    permission_classes = (IsAdminUser,)
+    queryset = Event.objects.all()
+    serializer_class = EventSerializer
+    lookup_field = 'uuid'
+
+
+class EventRetrieveAPIView(RetrieveAPIView):
+    permission_classes = (IsAdminUser,)
+    queryset = Event.objects.all()
+    serializer_class = EventSerializer
+    lookup_field = 'uuid'
diff --git a/apimail/api/views.py b/apimail/api/views/stored_message.py
similarity index 53%
rename from apimail/api/views.py
rename to apimail/api/views/stored_message.py
index 05fc3b93a..ffb01b783 100644
--- a/apimail/api/views.py
+++ b/apimail/api/views/stored_message.py
@@ -8,139 +8,16 @@ from django.db.models import Q
 from django.shortcuts import get_object_or_404
 from django.utils import timezone
 
-from rest_framework.generics import (
-    CreateAPIView, DestroyAPIView, ListAPIView,
-    RetrieveAPIView, UpdateAPIView)
-from rest_framework.parsers import FormParser, MultiPartParser
-from rest_framework.permissions import IsAdminUser, IsAuthenticated
+from rest_framework.generics import ListAPIView, RetrieveAPIView, UpdateAPIView
+from rest_framework.permissions import IsAuthenticated
 from rest_framework.response import Response
-from rest_framework import filters, status
+from rest_framework import filters
 
-from ..models import (
-    EmailAccount,
-    AttachmentFile,
-    ComposedMessage,
-    Event,
-    StoredMessage, UserTag)
 
-from ..permissions import (
-    CanHandleComposedMessage,
-    CanHandleStoredMessage, CanReadStoredMessage)
+from ...models import StoredMessage, UserTag
+from ..serializers import StoredMessageSerializer
 
-from .serializers import (
-    EmailAccountSerializer, EmailAccountAccessSerializer,
-    AttachmentFileSerializer,
-    ComposedMessageSerializer,
-    EventSerializer,
-    StoredMessageSerializer,
-    UserTagSerializer)
-
-
-class EmailAccountListAPIView(ListAPIView):
-    permission_classes = (IsAdminUser,)
-    queryset = EmailAccount.objects.all()
-    serializer_class = EmailAccountSerializer
-
-
-class UserEmailAccountAccessListAPIView(ListAPIView):
-    """
-    ListAPIView returning request.user's email account accesses.
-    """
-
-    permission_classes = (IsAuthenticated,)
-    serializer_class = EmailAccountAccessSerializer
-
-    def get_queryset(self):
-        queryset = self.request.user.email_account_accesses.all()
-        if self.request.query_params.get('current', None) == 'true':
-            queryset = queryset.current()
-        if self.request.query_params.get('cansend', None) == 'true':
-            queryset = queryset.can_send()
-        return queryset
-
-
-class AttachmentFileCreateAPIView(CreateAPIView):
-    permission_classes = (IsAuthenticated,)
-    queryset = AttachmentFile.objects.all()
-    serializer_class = AttachmentFileSerializer
-    parser_classes = [FormParser, MultiPartParser,]
-
-
-class ComposedMessageCreateAPIView(CreateAPIView):
-    permission_classes = (IsAuthenticated,)
-    queryset = ComposedMessage.objects.all()
-    serializer_class = ComposedMessageSerializer
-    lookup_field = 'uuid'
-
-    def create(self, request, *args, **kwargs):
-        # Override rest_framework.mixins.CreateModelMixin.create in order
-        # to include request.user in data, and attachment_uuids for serializer.create
-        data = request.data
-        data['author'] = request.user.id
-        serializer = self.get_serializer(data=data)
-        serializer.is_valid(raise_exception=True)
-        # Attachment uuids passed as extra args to be popped in serializer.create
-        serializer.save(attachment_uuids=request.data['attachment_uuids'])
-        headers = self.get_success_headers(serializer.data)
-        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
-
-
-class ComposedMessageUpdateAPIView(UpdateAPIView):
-    permission_classes = (IsAuthenticated, CanHandleComposedMessage,)
-    queryset = ComposedMessage.objects.all()
-    serializer_class = ComposedMessageSerializer
-    lookup_field = 'uuid'
-
-    def update(self, request, *args, **kwargs):
-        # Override rest_framework.mixins.CreateModelMixin.update in order
-        # to include request.user in data, and attachment_uuids for serializer.update
-        partial = kwargs.pop('partial', False)
-        instance = self.get_object()
-        serializer = self.get_serializer(instance, data=request.data, partial=partial)
-        serializer.is_valid(raise_exception=True)
-        serializer.save(attachment_uuids=request.data['attachment_uuids'])
-
-        if getattr(instance, '_prefetched_objects_cache', None):
-            # If 'prefetch_related' has been applied to a queryset, we need to
-            # forcibly invalidate the prefetch cache on the instance.
-            instance._prefetched_objects_cache = {}
-
-        return Response(serializer.data)
-
-
-class ComposedMessageDestroyAPIView(DestroyAPIView):
-    permission_classes = (IsAuthenticated, CanHandleComposedMessage,)
-    serializer_class = ComposedMessageSerializer
-    lookup_field = 'uuid'
-
-    def get_queryset(self):
-        return ComposedMessage.objects.filter_for_user(self.request.user)
-
-
-class ComposedMessageListAPIView(ListAPIView):
-    permission_classes = (IsAuthenticated,)
-    serializer_class = ComposedMessageSerializer
-    lookup_field = 'uuid'
-
-    def get_queryset(self):
-        queryset = ComposedMessage.objects.filter_for_user(self.request.user)
-        if self.request.query_params.get('status', None) == 'draft':
-            queryset = queryset.filter(status=ComposedMessage.STATUS_DRAFT)
-        return queryset
-
-
-class EventListAPIView(ListAPIView):
-    permission_classes = (IsAdminUser,)
-    queryset = Event.objects.all()
-    serializer_class = EventSerializer
-    lookup_field = 'uuid'
-
-
-class EventRetrieveAPIView(RetrieveAPIView):
-    permission_classes = (IsAdminUser,)
-    queryset = Event.objects.all()
-    serializer_class = EventSerializer
-    lookup_field = 'uuid'
+from ...permissions import CanReadStoredMessage
 
 
 class StoredMessageFilterBackend(filters.BaseFilterBackend):
@@ -275,37 +152,6 @@ class StoredMessageUpdateReadAPIView(UpdateAPIView):
         return Response()
 
 
-class UserTagCreateAPIView(CreateAPIView):
-    permission_classes = (IsAuthenticated,)
-    queryset = UserTag.objects.all()
-    serializer_class = UserTagSerializer
-
-    def create(self, request, *args, **kwargs):
-        data = request.data
-        data['user'] = request.user.id
-        serializer = self.get_serializer(data=data)
-        serializer.is_valid(raise_exception=True)
-        self.perform_create(serializer)
-        headers = self.get_success_headers(serializer.data)
-        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
-
-
-class UserTagDestroyAPIView(DestroyAPIView):
-    permission_classes = (IsAuthenticated,)
-    serializer_class = UserTagSerializer
-
-    def get_queryset(self):
-        return UserTag.objects.filter(user=self.request.user)
-
-
-class UserTagListAPIView(ListAPIView):
-    permission_classes = (IsAuthenticated,)
-    serializer_class = UserTagSerializer
-
-    def get_queryset(self):
-        return self.request.user.email_tags.all()
-
-
 class StoredMessageUpdateTagAPIView(UpdateAPIView):
     """
     Adds or removes a user tag on a StoredMessage.
diff --git a/apimail/api/views/tag.py b/apimail/api/views/tag.py
new file mode 100644
index 000000000..772ab4395
--- /dev/null
+++ b/apimail/api/views/tag.py
@@ -0,0 +1,44 @@
+__copyright__ = "Copyright © Stichting SciPost (SciPost Foundation)"
+__license__ = "AGPL v3"
+
+
+from rest_framework.generics import (
+    CreateAPIView, DestroyAPIView, ListAPIView,
+)
+from rest_framework.permissions import IsAuthenticated
+from rest_framework.response import Response
+from rest_framework import status
+
+from ...models import UserTag
+from ..serializers import UserTagSerializer
+
+
+class UserTagCreateAPIView(CreateAPIView):
+    permission_classes = (IsAuthenticated,)
+    queryset = UserTag.objects.all()
+    serializer_class = UserTagSerializer
+
+    def create(self, request, *args, **kwargs):
+        data = request.data
+        data['user'] = request.user.id
+        serializer = self.get_serializer(data=data)
+        serializer.is_valid(raise_exception=True)
+        self.perform_create(serializer)
+        headers = self.get_success_headers(serializer.data)
+        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
+
+
+class UserTagDestroyAPIView(DestroyAPIView):
+    permission_classes = (IsAuthenticated,)
+    serializer_class = UserTagSerializer
+
+    def get_queryset(self):
+        return UserTag.objects.filter(user=self.request.user)
+
+
+class UserTagListAPIView(ListAPIView):
+    permission_classes = (IsAuthenticated,)
+    serializer_class = UserTagSerializer
+
+    def get_queryset(self):
+        return self.request.user.email_tags.all()
diff --git a/apimail/models/account.py b/apimail/models/account.py
index fb59da3d1..cb2fb9e66 100644
--- a/apimail/models/account.py
+++ b/apimail/models/account.py
@@ -2,6 +2,8 @@ _copyright__ = "Copyright © Stichting SciPost (SciPost Foundation)"
 __license__ = "AGPL v3"
 
 
+import pytz
+
 from django.conf import settings
 from django.core.exceptions import ValidationError
 from django.db import models
diff --git a/apimail/models/composed_message.py b/apimail/models/composed_message.py
index 654f911d2..68b123702 100644
--- a/apimail/models/composed_message.py
+++ b/apimail/models/composed_message.py
@@ -2,6 +2,7 @@ __copyright__ = "Copyright © Stichting SciPost (SciPost Foundation)"
 __license__ = "AGPL v3"
 
 
+import pytz
 import uuid as uuid_lib
 
 from django.conf import settings
-- 
GitLab