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/api/event/serializers.py b/api/event/serializers.py index cd7ba7a..13ab7fd 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() # Event Serializer @@ -111,5 +117,38 @@ 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=ActivityTemplate.objects.all(), allow_null=True) + en_perm = UserSerializer(read_only=True) + tags = ActivityTagSerializer(many=True) + + class Meta: + model = Activity + fields = ('id', 'title', 'event', 'parent', 'is_public', 'has_perm', + 'min_perm', 'max_perm', 'en_perm', 'description', 'remarks', + 'tags', 'beginning', 'end', ) + + 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/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', ) diff --git a/api/test_event.py b/api/test_event.py index 62d77d5..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,13 +44,34 @@ class ActivityTemplateTest(EventBasedModelTestMixin, APITestCase): self.assertEqual(instance.tags.count(), 2) -class EventSpecficTagTest(EventSpecificTestMixin, APITestCase): +class ActivityTests(EventBasedModelTestMixin, APITestCase): + model = Activity + base_name = 'event-activity' + # Creation + data_creation = 'act2_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 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 9f524d7..c94a5a2 100644 --- a/api/test_mixins.py +++ b/api/test_mixins.py @@ -7,13 +7,13 @@ 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() -class DataBaseMixin(object): +class DataBaseMixin(): """ provides a datatabse for API tests """ @@ -51,6 +51,19 @@ 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, + '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", + "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) + class EventBasedModelTestMixin(DataBaseMixin): """ @@ -83,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), @@ -90,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() @@ -120,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) @@ -132,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 @@ -141,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 @@ -234,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) @@ -259,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(): @@ -278,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) 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. 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