From 26a62ea30b239a8b0fb56a738e3f5ab793604fa0 Mon Sep 17 00:00:00 2001 From: Qwann Date: Tue, 25 Jul 2017 18:42:20 +0200 Subject: [PATCH 1/4] WIP Activity serializer --- api/event/serializers.py | 41 ++++++++++++++++++++++++++++++++++++++-- api/test_event.py | 22 +++++++++++++++++++++ api/test_mixins.py | 14 +++++++++++++- api/urls.py | 6 ++++-- 4 files changed, 78 insertions(+), 5 deletions(-) diff --git a/api/event/serializers.py b/api/event/serializers.py index e898d8d..01adbc3 100644 --- a/api/event/serializers.py +++ b/api/event/serializers.py @@ -1,6 +1,12 @@ +from django.contrib.auth import get_user_model from django.shortcuts import get_object_or_404 + from rest_framework import serializers -from event.models import Event, ActivityTag, Place, ActivityTemplate + +from event.models import Event, ActivityTag, Place, ActivityTemplate, Activity +from api.users.serializers import UserSerializer + +User = get_user_model() # Classes utilitaires @@ -91,5 +97,36 @@ class ActivityTemplateSerializer(serializers.ModelSerializer): return activity_template +# TODO rajouter des permissions class ActivitySerializer(serializers.ModelSerializer): - pass + event = EventSerializer(read_only=True) + parent = serializers.PrimaryKeyRelatedField(queryset=User.objects.all()) + en_perm = UserSerializer(read_only=True) + tags = ActivityTagSerializer(many=True) + + class Meta: + model = ActivityTemplate + fields = ('id', 'title', 'event', 'parent', 'is_public', 'has_perm', + 'min_perm', 'max_perm', 'description', 'remarks', 'tags',) + + def update(self, instance, validated_data): + tags_data = validated_data.pop('tags') + validated_data.pop('event_pk') + event = instance.event + [setattr(instance, key, value) + for key, value in validated_data.items()] + instance.save() + tags = [ActivityTag.objects.get_or_create(event=event, **tag_data)[0] + for tag_data in tags_data] + instance.tags = tags + return instance + + def create(self, validated_data): + tags_data = validated_data.pop('tags') + event_pk = validated_data.pop('event_pk') + event = event_pk and get_object_or_404(Event, id=event_pk) or None + activity = Activity.objects.create(event=event, **validated_data) + tags = [ActivityTag.objects.get_or_create(event=event, **tag_data)[0] + for tag_data in tags_data] + activity.tags = tags + return activity diff --git a/api/test_event.py b/api/test_event.py index 6c2b118..92c8b0b 100644 --- a/api/test_event.py +++ b/api/test_event.py @@ -44,6 +44,28 @@ class ActivityTemplateTest(EventBasedModelMixin, APITestCase): self.assertEqual(instance.tags.count(), 2) +class ActivityTest(EventBasedModelMixin, APITestCase): + model = ActivityTemplate + base_name = 'event-activity' + initial_count = 1 + # Creation + data_creation = 'act1_data' + # Update/Delete + instance_name = 'act1' + field_tested = 'title' + serializer = ActivityTemplateSerializer + + def test_create_extra(self): + self.assertEqual(self.model.objects.get(id=1).tags.count(), 1) + + def pre_update_extra(self, data): + data['tags'].append(self.tag2_data) + return data + + def post_update_extra(self, instance): + self.assertEqual(instance.tags.count(), 2) + + class EventSpecficTagTest(EventSpecificMixin, APITestCase): model = ActivityTag root_base_name = 'activitytag' diff --git a/api/test_mixins.py b/api/test_mixins.py index 094676d..1b19100 100644 --- a/api/test_mixins.py +++ b/api/test_mixins.py @@ -7,7 +7,7 @@ from django.urls import reverse from rest_framework.test import APIRequestFactory from rest_framework import status -from event.models import Event, Place, ActivityTag, ActivityTemplate +from event.models import Event, Place, ActivityTag, ActivityTemplate, Activity User = get_user_model() @@ -41,6 +41,8 @@ class DataBaseMixin(object): self.tag1 = ActivityTag.objects.create(**self.tag1_data) self.act_temp1_data = {'title': "act temp1", 'is_public': True, + 'beginning': timezone.now(), + 'ending': timezone.now()+timedelta(hours=2), 'remarks': "test remark", 'event': self.event1} self.act_temp2_data = {'title': "act temp2", 'is_public': False, 'remarks': "test remark", @@ -48,6 +50,16 @@ class DataBaseMixin(object): self.act_temp1 = ActivityTemplate.objects.create(**self.act_temp1_data) self.act_temp1.tags.add(self.tag1) + # Actitvity + self.act1_data = {'title': "act1", 'is_public': True, + 'remarks': "test remark", 'event': self.event1} + self.act2_data = {'title': "act2", 'is_public': False, + "beginning": "2017-07-18T18:05:00Z", + "ending": "2017-07-19T18:05:00Z", + 'remarks': "test remark", 'tags': [self.tag2_data, ]} + self.act1 = Activity.objects.create(**self.act1_data) + self.act1.tags.add(self.tag1) + class EventBasedModelMixin(DataBaseMixin): """ diff --git a/api/urls.py b/api/urls.py index 0409143..24da79a 100644 --- a/api/urls.py +++ b/api/urls.py @@ -1,7 +1,7 @@ from django.conf.urls import url, include from rest_framework_nested.routers import SimpleRouter, NestedSimpleRouter from api.event.views import EventViewSet, PlaceViewSet, ActivityTagViewSet,\ - ActivityTemplateViewSet + ActivityTemplateViewSet, ActivityViewSet # Create a router and register our viewsets with it. router = SimpleRouter() @@ -12,9 +12,11 @@ router.register(r'activitytag', ActivityTagViewSet, 'activitytag') # Register nested router and register someviewsets vith it event_router = NestedSimpleRouter(router, r'event', lookup='event') event_router.register(r'place', PlaceViewSet, base_name='event-place') -event_router.register(r'tag', ActivityTagViewSet, base_name='event-activitytag') +event_router.register(r'tag', ActivityTagViewSet, + base_name='event-activitytag') event_router.register(r'activitytemplate', ActivityTemplateViewSet, base_name='event-activitytemplate') +event_router.register(r'activity', ActivityViewSet, base_name='event-activity') # The API URLs are now determined automatically by the router. From 6bb176be013531c209ead811def6931b1d585c24 Mon Sep 17 00:00:00 2001 From: Qwann Date: Wed, 26 Jul 2017 19:27:28 +0200 Subject: [PATCH 2/4] Activity serialized --- api/event/serializers.py | 7 ++++--- api/test_event.py | 19 +++++++++---------- api/test_mixins.py | 39 ++++++++++++++++++++++++--------------- 3 files changed, 37 insertions(+), 28 deletions(-) diff --git a/api/event/serializers.py b/api/event/serializers.py index 8abc556..4f8ca16 100644 --- a/api/event/serializers.py +++ b/api/event/serializers.py @@ -120,14 +120,15 @@ class ActivityTemplateSerializer(serializers.ModelSerializer): # TODO rajouter des permissions class ActivitySerializer(serializers.ModelSerializer): event = EventSerializer(read_only=True) - parent = serializers.PrimaryKeyRelatedField(queryset=User.objects.all()) + parent = serializers.PrimaryKeyRelatedField(queryset=ActivityTemplate.objects.all(), allow_null=True) en_perm = UserSerializer(read_only=True) tags = ActivityTagSerializer(many=True) class Meta: - model = ActivityTemplate + model = Activity fields = ('id', 'title', 'event', 'parent', 'is_public', 'has_perm', - 'min_perm', 'max_perm', 'description', 'remarks', 'tags',) + 'min_perm', 'max_perm', 'en_perm', 'description', 'remarks', + 'tags', 'beginning', 'end', ) def update(self, instance, validated_data): tags_data = validated_data.pop('tags') diff --git a/api/test_event.py b/api/test_event.py index 1796608..6a8e352 100644 --- a/api/test_event.py +++ b/api/test_event.py @@ -4,14 +4,15 @@ from rest_framework.test import APITestCase from event.models import Event, Place, ActivityTag, ActivityTemplate -from api.event.serializers import ActivityTemplateSerializer, EventSerializer +from api.event.serializers import ActivityTemplateSerializer,\ + EventSerializer, Activity from api.test_mixins import EventBasedModelTestMixin, EventSpecificTestMixin,\ ModelTestMixin User = get_user_model() -class EventTest(ModelTestMixin, APITestCase): +class EventTests(ModelTestMixin, APITestCase): model = Event base_name = 'event' tested_fields = {'title': "I'm a test", } @@ -22,10 +23,9 @@ class EventTest(ModelTestMixin, APITestCase): serializer = EventSerializer -class ActivityTemplateTest(EventBasedModelTestMixin, APITestCase): +class ActivityTemplateTests(EventBasedModelTestMixin, APITestCase): model = ActivityTemplate base_name = 'event-activitytemplate' - initial_count = 1 # Creation data_creation = 'act_temp2_data' # Update/Delete @@ -44,12 +44,11 @@ class ActivityTemplateTest(EventBasedModelTestMixin, APITestCase): self.assertEqual(instance.tags.count(), 2) -class ActivityTest(EventBasedModelTestMixin, APITestCase): - model = ActivityTemplate +class ActivityTests(EventBasedModelTestMixin, APITestCase): + model = Activity base_name = 'event-activity' - initial_count = 1 # Creation - data_creation = 'act1_data' + data_creation = 'act2_data' # Update/Delete instance_name = 'act1' field_tested = 'title' @@ -66,13 +65,13 @@ class ActivityTest(EventBasedModelTestMixin, APITestCase): self.assertEqual(instance.tags.count(), 2) -class EventSpecficTagTest(EventSpecificTestMixin, APITestCase): +class EventSpecficTagTests(EventSpecificTestMixin, APITestCase): model = ActivityTag root_base_name = 'activitytag' event_base_name = 'event-activitytag' -class EventSpecficPlaceTest(EventSpecificTestMixin, APITestCase): +class EventSpecficPlaceTests(EventSpecificTestMixin, APITestCase): model = Place root_base_name = 'place' event_base_name = 'event-place' diff --git a/api/test_mixins.py b/api/test_mixins.py index 39ef4d4..c94a5a2 100644 --- a/api/test_mixins.py +++ b/api/test_mixins.py @@ -13,7 +13,7 @@ from event.models import Event, Place, ActivityTag, ActivityTemplate, Activity User = get_user_model() -class DataBaseMixin(object): +class DataBaseMixin(): """ provides a datatabse for API tests """ @@ -44,8 +44,6 @@ class DataBaseMixin(object): self.tag1 = ActivityTag.objects.create(**self.tag1_data) self.act_temp1_data = {'title': "act temp1", 'is_public': True, - 'beginning': timezone.now(), - 'ending': timezone.now()+timedelta(hours=2), 'remarks': "test remark", 'event': self.event1} self.act_temp2_data = {'title': "act temp2", 'is_public': False, 'remarks': "test remark", @@ -55,11 +53,14 @@ class DataBaseMixin(object): # Actitvity self.act1_data = {'title': "act1", 'is_public': True, + 'beginning': timezone.now(), + 'end': timezone.now()+timedelta(hours=2), 'remarks': "test remark", 'event': self.event1} self.act2_data = {'title': "act2", 'is_public': False, "beginning": "2017-07-18T18:05:00Z", - "ending": "2017-07-19T18:05:00Z", - 'remarks': "test remark", 'tags': [self.tag2_data, ]} + "end": "2017-07-19T18:05:00Z", + 'parent': None, + 'remarks': "test remark", 'tags': []} self.act1 = Activity.objects.create(**self.act1_data) self.act1.tags.add(self.tag1) @@ -95,6 +96,7 @@ class EventBasedModelTestMixin(DataBaseMixin): ensure a permited user can create a new %model object using API """ data = getattr(self, self.data_creation) + initial_count = self.model.objects.count() event_id = self.event1.id url = reverse('{base_name}-list'.format(base_name=self.base_name), @@ -102,9 +104,10 @@ class EventBasedModelTestMixin(DataBaseMixin): self.client.force_authenticate(user=self.user1) response = self.client.post(url, data, format='json') - self.assertEqual(response.status_code, status.HTTP_201_CREATED) - self.assertEqual(self.model.objects.count(), self.initial_count + 1) - self.assertEqual(self.model.objects.get(id=self.initial_count+1).event, + self.assertEqual(response.status_code, status.HTTP_201_CREATED, + msg=response.content) + self.assertEqual(self.model.objects.count(), initial_count + 1) + self.assertEqual(self.model.objects.get(id=initial_count+1).event, self.event1) self.user_create_extra() @@ -132,7 +135,8 @@ class EventBasedModelTestMixin(DataBaseMixin): self.client.force_authenticate(user=self.user1) response = self.client.patch(url, data, format='json') - self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.status_code, status.HTTP_200_OK, + msg=response.content) instance.refresh_from_db() self.assertEqual(getattr(instance, self.field_tested), newvalue) @@ -144,6 +148,7 @@ class EventBasedModelTestMixin(DataBaseMixin): ensure a permited user can delete a new %model object using API """ instance = getattr(self, self.instance_name) + initial_count = self.model.objects.count() instance_id = instance.id event_id = self.event1.id @@ -153,14 +158,15 @@ class EventBasedModelTestMixin(DataBaseMixin): self.client.force_authenticate(user=self.user1) response = self.client.delete(url) - self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) - self.assertEqual(self.model.objects.count(), self.initial_count - 1) + self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT, + msg=response.content) + self.assertEqual(self.model.objects.count(), initial_count - 1) # TODO rajouter la gestion des permissions dans le Mixin # TODO rajouter un test pour s'assurer que les personnes non # connectées ne peuvent pas create/update/delete -class EventSpecificTestMixin(object): +class EventSpecificTestMixin(): """ Tests is the EventSpecifics querysets are rendered correctly using the API @@ -246,7 +252,8 @@ class ModelTestMixin(DataBaseMixin): self.client.force_authenticate(user=self.user1) response = self.client.post(url, data) - self.assertEqual(response.status_code, status.HTTP_201_CREATED) + self.assertEqual(response.status_code, status.HTTP_201_CREATED, + msg=response.content) self.assertEqual(self.model.objects.count(), initial_count + 1) instance = self.model.objects.get(id=initial_count+1) @@ -271,7 +278,8 @@ class ModelTestMixin(DataBaseMixin): self.client.force_authenticate(user=self.user1) response = self.client.put(url, data, format='json') - self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.status_code, status.HTTP_200_OK, + msg=response.content) instance.refresh_from_db() for field, value in self.tested_fields.items(): @@ -290,5 +298,6 @@ class ModelTestMixin(DataBaseMixin): self.client.force_authenticate(user=self.user1) response = self.client.delete(url) - self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) + self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT, + msg=response.content) self.assertEqual(self.model.objects.count(), initial_count - 1) From 8d29049989d7ff2967980b11bc7135507d415799 Mon Sep 17 00:00:00 2001 From: Qwann Date: Wed, 26 Jul 2017 19:58:10 +0200 Subject: [PATCH 3/4] useless --- api/__init__.py | 1 + api/apps.py | 6 ++++++ communication/__init__.py | 1 + communication/apps.py | 2 ++ equipment/__init__.py | 1 + evenementiel/settings/common.py | 8 ++++---- event/__init__.py | 1 + users/__init__.py | 1 + users/migrations/__init__.py | 0 9 files changed, 17 insertions(+), 4 deletions(-) create mode 100644 api/__init__.py create mode 100644 api/apps.py create mode 100644 equipment/__init__.py create mode 100644 users/migrations/__init__.py diff --git a/api/__init__.py b/api/__init__.py new file mode 100644 index 0000000..08884cb --- /dev/null +++ b/api/__init__.py @@ -0,0 +1 @@ +default_app_config = 'api.apps.APIConfig' diff --git a/api/apps.py b/api/apps.py new file mode 100644 index 0000000..413e0bf --- /dev/null +++ b/api/apps.py @@ -0,0 +1,6 @@ +from django.utils.translation import ugettext_lazy as _ +from django.apps import AppConfig + +class APIConfig(AppConfig): + name = 'api' + verbose_name = _("API") diff --git a/communication/__init__.py b/communication/__init__.py index e69de29..d4488d0 100644 --- a/communication/__init__.py +++ b/communication/__init__.py @@ -0,0 +1 @@ +default_app_config = 'communication.apps.CommunicationConfig' diff --git a/communication/apps.py b/communication/apps.py index e6c488d..4b962ac 100644 --- a/communication/apps.py +++ b/communication/apps.py @@ -1,4 +1,6 @@ +from django.utils.translation import ugettext_lazy as _ from django.apps import AppConfig class CommunicationConfig(AppConfig): name = 'communication' + verbose_name = _("communication") diff --git a/equipment/__init__.py b/equipment/__init__.py new file mode 100644 index 0000000..0a0003b --- /dev/null +++ b/equipment/__init__.py @@ -0,0 +1 @@ +default_app_config = 'equipment.apps.EquipmentConfig' diff --git a/evenementiel/settings/common.py b/evenementiel/settings/common.py index 8baa801..2ef622b 100644 --- a/evenementiel/settings/common.py +++ b/evenementiel/settings/common.py @@ -44,10 +44,6 @@ BASE_DIR = os.path.dirname( INSTALLED_APPS = [ - 'communication.apps.CommunicationConfig', - 'equipment.apps.EquipmentConfig', - 'event.apps.EventConfig', - 'users.apps.UsersConfig', 'shared.apps.SharedConfig', 'django.contrib.admin', 'django.contrib.auth', @@ -60,6 +56,10 @@ INSTALLED_APPS = [ 'bootstrapform', 'widget_tweaks', 'api', + 'communication', + 'equipment', + 'event', + 'users', ] MIDDLEWARE_CLASSES = [ diff --git a/event/__init__.py b/event/__init__.py index e69de29..55ece8a 100644 --- a/event/__init__.py +++ b/event/__init__.py @@ -0,0 +1 @@ +default_app_config = 'event.apps.EventConfig' diff --git a/users/__init__.py b/users/__init__.py index e69de29..208620c 100644 --- a/users/__init__.py +++ b/users/__init__.py @@ -0,0 +1 @@ +default_app_config = 'users.apps.UsersConfig' diff --git a/users/migrations/__init__.py b/users/migrations/__init__.py new file mode 100644 index 0000000..e69de29 From 8568199de48c02e715ca2e1a6c5e2d4d58f2d166 Mon Sep 17 00:00:00 2001 From: Qwann Date: Wed, 26 Jul 2017 20:20:47 +0200 Subject: [PATCH 4/4] useless(2): ordering --- api/event/serializers.py | 3 ++- api/event/views.py | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/api/event/serializers.py b/api/event/serializers.py index 4f8ca16..13ab7fd 100644 --- a/api/event/serializers.py +++ b/api/event/serializers.py @@ -120,7 +120,8 @@ class ActivityTemplateSerializer(serializers.ModelSerializer): # TODO rajouter des permissions class ActivitySerializer(serializers.ModelSerializer): event = EventSerializer(read_only=True) - parent = serializers.PrimaryKeyRelatedField(queryset=ActivityTemplate.objects.all(), allow_null=True) + parent = serializers.PrimaryKeyRelatedField( + queryset=ActivityTemplate.objects.all(), allow_null=True) en_perm = UserSerializer(read_only=True) tags = ActivityTagSerializer(many=True) diff --git a/api/event/views.py b/api/event/views.py index b360583..316bc59 100644 --- a/api/event/views.py +++ b/api/event/views.py @@ -2,6 +2,7 @@ from django.contrib.auth import get_user_model from django.db.models import Q from rest_framework.viewsets import ModelViewSet +from rest_framework.filters import OrderingFilter from api.event.serializers import EventSerializer, PlaceSerializer,\ ActivityTagSerializer, ActivityTemplateSerializer, ActivitySerializer @@ -51,6 +52,11 @@ class EventViewSet(ModelViewSet): queryset = Event.objects.all() serializer_class = EventSerializer + filter_backends = (OrderingFilter,) + ordering_fields = ('title', 'creation_date', 'beginning_date', + 'ending_date', ) + ordering = ('beginning_date', ) + def perform_create(self, serializer): serializer.save(created_by=self.request.user) @@ -59,11 +65,19 @@ class PlaceViewSet(EventSpecificViewSet): queryset = Place.objects.all() serializer_class = PlaceSerializer + filter_backends = (OrderingFilter,) + ordering_fields = ('name', ) + ordering = ('name', ) + class ActivityTagViewSet(EventSpecificViewSet): queryset = ActivityTag.objects.all() serializer_class = ActivityTagSerializer + filter_backends = (OrderingFilter,) + ordering_fields = ('name', ) + ordering = ('name', ) + class ActivityTemplateViewSet(ModelViewSet): """ @@ -82,6 +96,10 @@ class ActivityTemplateViewSet(ModelViewSet): event_pk = self.kwargs.get('event_pk') serializer.save(event_pk=event_pk) + filter_backends = (OrderingFilter,) + ordering_fields = ('title', ) + ordering = ('title', ) + class ActivityViewSet(ModelViewSet): """ @@ -99,3 +117,7 @@ class ActivityViewSet(ModelViewSet): def perform_update(self, serializer): event_pk = self.kwargs.get('event_pk') serializer.save(event_pk=event_pk) + + filter_backends = (OrderingFilter,) + ordering_fields = ('title', ) + ordering = ('title', )