From 10e106ba83728463a16e2d42fb7e3356d8a59211 Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Tue, 7 Feb 2017 22:06:03 -0400 Subject: [PATCH] Improve tag serializer adding bulk document tagging on creation and editing. Improve and add additiona tag API tests. --- mayan/apps/tags/api_views.py | 67 +++++++++++++++------------- mayan/apps/tags/serializers.py | 48 ++++++++++++++++++-- mayan/apps/tags/tests/test_api.py | 73 +++++++++++++++++++++---------- 3 files changed, 131 insertions(+), 57 deletions(-) diff --git a/mayan/apps/tags/api_views.py b/mayan/apps/tags/api_views.py index f24e267ef3..87c37c49e1 100644 --- a/mayan/apps/tags/api_views.py +++ b/mayan/apps/tags/api_views.py @@ -21,11 +21,39 @@ from .permissions import ( permission_tag_remove, permission_tag_view ) from .serializers import ( - DocumentTagSerializer, NewDocumentTagSerializer, NewTagSerializer, - TagSerializer + DocumentTagSerializer, NewDocumentTagSerializer, TagSerializer, + WritableTagSerializer ) +class APITagListView(generics.ListCreateAPIView): + filter_backends = (MayanObjectPermissionsFilter,) + mayan_object_permissions = {'GET': (permission_tag_view,)} + mayan_view_permissions = {'POST': (permission_tag_create,)} + permission_classes = (MayanPermission,) + queryset = Tag.objects.all() + + def get_serializer_class(self): + if self.request.method == 'GET': + return TagSerializer + elif self.request.method == 'POST': + return WritableTagSerializer + + def get(self, *args, **kwargs): + """ + Returns a list of all the tags. + """ + + return super(APITagListView, self).get(*args, **kwargs) + + def post(self, *args, **kwargs): + """ + Create a new tag. + """ + + return super(APITagListView, self).post(*args, **kwargs) + + class APITagView(generics.RetrieveUpdateDestroyAPIView): filter_backends = (MayanObjectPermissionsFilter,) mayan_object_permissions = { @@ -35,7 +63,6 @@ class APITagView(generics.RetrieveUpdateDestroyAPIView): 'PUT': (permission_tag_edit,) } queryset = Tag.objects.all() - serializer_class = TagSerializer def delete(self, *args, **kwargs): """ @@ -51,6 +78,12 @@ class APITagView(generics.RetrieveUpdateDestroyAPIView): return super(APITagView, self).get(*args, **kwargs) + def get_serializer_class(self): + if self.request.method == 'GET': + return TagSerializer + else: + return WritableTagSerializer + def patch(self, *args, **kwargs): """ Edit the selected tag. @@ -66,34 +99,6 @@ class APITagView(generics.RetrieveUpdateDestroyAPIView): return super(APITagView, self).put(*args, **kwargs) -class APITagListView(generics.ListCreateAPIView): - filter_backends = (MayanObjectPermissionsFilter,) - mayan_object_permissions = {'GET': (permission_tag_view,)} - mayan_view_permissions = {'POST': (permission_tag_create,)} - permission_classes = (MayanPermission,) - queryset = Tag.objects.all() - - def get_serializer_class(self): - if self.request.method == 'GET': - return TagSerializer - elif self.request.method == 'POST': - return NewTagSerializer - - def get(self, *args, **kwargs): - """ - Returns a list of all the tags. - """ - - return super(APITagListView, self).get(*args, **kwargs) - - def post(self, *args, **kwargs): - """ - Create a new tag. - """ - - return super(APITagListView, self).post(*args, **kwargs) - - class APITagDocumentListView(generics.ListAPIView): """ Returns a list of all the documents tagged by a particular tag. diff --git a/mayan/apps/tags/serializers.py b/mayan/apps/tags/serializers.py index 3f49cf2e42..2d78f5e9bb 100644 --- a/mayan/apps/tags/serializers.py +++ b/mayan/apps/tags/serializers.py @@ -8,6 +8,7 @@ from rest_framework.exceptions import ValidationError from rest_framework.reverse import reverse from acls.models import AccessControlList +from documents.models import Document from permissions import Permission from .models import Tag @@ -15,7 +16,7 @@ from .permissions import permission_tag_attach class TagSerializer(serializers.HyperlinkedModelSerializer): - documents = serializers.HyperlinkedIdentityField( + documents_url = serializers.HyperlinkedIdentityField( view_name='rest_api:tag-document-list' ) documents_count = serializers.SerializerMethodField() @@ -25,7 +26,7 @@ class TagSerializer(serializers.HyperlinkedModelSerializer): 'url': {'view_name': 'rest_api:tag-detail'}, } fields = ( - 'color', 'documents', 'documents_count', 'id', 'label', 'url' + 'color', 'documents_count', 'documents_url', 'id', 'label', 'url' ) model = Tag @@ -33,13 +34,52 @@ class TagSerializer(serializers.HyperlinkedModelSerializer): return instance.documents.count() -class NewTagSerializer(serializers.ModelSerializer): +class WritableTagSerializer(serializers.ModelSerializer): + documents_pk_list = serializers.CharField( + help_text=_( + 'Comma separated list of document primary keys to which this tag ' + 'will be attached.' + ), required=False + ) + class Meta: fields = ( - 'color', 'label', 'id' + 'color', 'documents_pk_list', 'id', 'label', ) model = Tag + def _add_documents(self, documents_pk_list, instance): + instance.documents.add( + *Document.objects.filter(pk__in=documents_pk_list.split(',')) + ) + + def create(self, validated_data): + documents_pk_list = validated_data.pop('documents_pk_list', '') + + instance = super(WritableTagSerializer, self).create(validated_data) + + if documents_pk_list: + self._add_documents( + documents_pk_list=documents_pk_list, instance=instance + ) + + return instance + + def update(self, instance, validated_data): + documents_pk_list = validated_data.pop('documents_pk_list', '') + + instance = super(WritableTagSerializer, self).update( + instance, validated_data + ) + + if documents_pk_list: + instance.documents.clear() + self._add_documents( + documents_pk_list=documents_pk_list, instance=instance + ) + + return instance + class NewDocumentTagSerializer(serializers.Serializer): tag = serializers.IntegerField( diff --git a/mayan/apps/tags/tests/test_api.py b/mayan/apps/tags/tests/test_api.py index d714a5a14f..072ab962f4 100644 --- a/mayan/apps/tags/tests/test_api.py +++ b/mayan/apps/tags/tests/test_api.py @@ -20,6 +20,7 @@ from .literals import ( ) +@override_settings(OCR_AUTO_OCR=False) class TagAPITestCase(APITestCase): """ Test the tag API endpoints @@ -37,6 +38,20 @@ class TagAPITestCase(APITestCase): def tearDown(self): self.admin_user.delete() + if hasattr(self, 'document_type'): + self.document_type.delete() + + def _document_create(self): + self.document_type = DocumentType.objects.create( + label=TEST_DOCUMENT_TYPE + ) + + with open(TEST_SMALL_DOCUMENT_PATH) as file_object: + document = self.document_type.new_document( + file_object=file_object, + ) + + return document def test_tag_create(self): response = self.client.post( @@ -46,7 +61,22 @@ class TagAPITestCase(APITestCase): ) tag = Tag.objects.first() + self.assertEqual(response.data['id'], tag.pk) + self.assertEqual(response.data['label'], TEST_TAG_LABEL) + self.assertEqual(response.data['color'], TEST_TAG_COLOR) + self.assertEqual(Tag.objects.count(), 1) + self.assertEqual(tag.label, TEST_TAG_LABEL) + self.assertEqual(tag.color, TEST_TAG_COLOR) + + def test_tag_create_with_documents(self): + response = self.client.post( + reverse('rest_api:tag-list'), { + 'label': TEST_TAG_LABEL, 'color': TEST_TAG_COLOR + } + ) + + tag = Tag.objects.first() self.assertEqual(response.data['id'], tag.pk) self.assertEqual(response.data['label'], TEST_TAG_LABEL) self.assertEqual(response.data['color'], TEST_TAG_COLOR) @@ -62,7 +92,23 @@ class TagAPITestCase(APITestCase): self.assertEqual(Tag.objects.count(), 0) - def test_tag_edit(self): + def test_tag_edit_via_patch(self): + tag = Tag.objects.create(color=TEST_TAG_COLOR, label=TEST_TAG_LABEL) + + self.client.patch( + reverse('rest_api:tag-detail', args=(tag.pk,)), + { + 'label': TEST_TAG_LABEL_EDITED, + 'color': TEST_TAG_COLOR_EDITED + } + ) + + tag = Tag.objects.first() + + self.assertEqual(tag.label, TEST_TAG_LABEL_EDITED) + self.assertEqual(tag.color, TEST_TAG_COLOR_EDITED) + + def test_tag_edit_via_put(self): tag = Tag.objects.create(color=TEST_TAG_COLOR, label=TEST_TAG_LABEL) self.client.put( @@ -78,18 +124,10 @@ class TagAPITestCase(APITestCase): self.assertEqual(tag.label, TEST_TAG_LABEL_EDITED) self.assertEqual(tag.color, TEST_TAG_COLOR_EDITED) - @override_settings(OCR_AUTO_OCR=False) - def test_tag_add_document(self): + def test_tag_document_add(self): tag = Tag.objects.create(color=TEST_TAG_COLOR, label=TEST_TAG_LABEL) - document_type = DocumentType.objects.create( - label=TEST_DOCUMENT_TYPE - ) - - with open(TEST_SMALL_DOCUMENT_PATH) as file_object: - document = document_type.new_document( - file_object=file_object, - ) + document = self._document_create() self.client.post( reverse('rest_api:document-tag-list', args=(document.pk,)), @@ -98,19 +136,10 @@ class TagAPITestCase(APITestCase): self.assertEqual(tag.documents.count(), 1) - @override_settings(OCR_AUTO_OCR=False) - def test_tag_remove_document(self): + def test_tag_document_remove(self): tag = Tag.objects.create(color=TEST_TAG_COLOR, label=TEST_TAG_LABEL) - document_type = DocumentType.objects.create( - label=TEST_DOCUMENT_TYPE - ) - - with open(TEST_SMALL_DOCUMENT_PATH) as file_object: - document = document_type.new_document( - file_object=file_object, - ) - + document = self._document_create() tag.documents.add(document) self.client.delete(