Skip to content
Snippets Groups Projects
Commit 052e1543 authored by florimondmanca's avatar florimondmanca
Browse files

add more endpoints on project edition and participation resources

parent 2967a702
Branches
No related tags found
No related merge requests found
...@@ -4,7 +4,7 @@ import factory ...@@ -4,7 +4,7 @@ import factory
import factory.django import factory.django
from users.factory import UserFactory from users.factory import UserFactory
from .models import Project, Edition, Participation from .models import Project, Edition, Participation, EditionForm
class ProjectFactory(factory.DjangoModelFactory): class ProjectFactory(factory.DjangoModelFactory):
...@@ -37,6 +37,14 @@ class EditionFactory(factory.DjangoModelFactory): ...@@ -37,6 +37,14 @@ class EditionFactory(factory.DjangoModelFactory):
return project and project or ProjectFactory.create() return project and project or ProjectFactory.create()
class EditionFormFactory(factory.DjangoModelFactory):
class Meta: # noqa
model = EditionForm
deadline = factory.Faker('future_date')
class ParticipationFactory(factory.DjangoModelFactory): class ParticipationFactory(factory.DjangoModelFactory):
"""Participation object factory.""" """Participation object factory."""
......
...@@ -4,12 +4,12 @@ from django.db import transaction ...@@ -4,12 +4,12 @@ from django.db import transaction
from rest_framework import serializers from rest_framework import serializers
from core.fields import MarkdownField from core.fields import MarkdownField
from dynamicforms.serializers import FormDetailSerializer, FormEntrySerializer
from profiles.serializers import TutorSerializer
from users.fields import UserField from users.fields import UserField
from users.serializers import UserSerializer from users.serializers import UserSerializer
from profiles.serializers import TutorSerializer
from dynamicforms.serializers import FormDetailSerializer, FormEntrySerializer
from .models import Edition, Participation, Project, EditionForm from .models import Edition, EditionForm, Participation, Project
class ProjectSerializer(serializers.HyperlinkedModelSerializer): class ProjectSerializer(serializers.HyperlinkedModelSerializer):
...@@ -25,53 +25,20 @@ class ProjectSerializer(serializers.HyperlinkedModelSerializer): ...@@ -25,53 +25,20 @@ class ProjectSerializer(serializers.HyperlinkedModelSerializer):
} }
class ParticipationSerializer(serializers.ModelSerializer):
"""Serializer for project edition participations."""
user = UserField(
label='Utilisateur',
help_text='Identifier for the user who participates.')
edition = serializers.PrimaryKeyRelatedField(
queryset=Edition.objects.all(),
label='Édition',
help_text='Identifier for the associated edition.')
entry = FormEntrySerializer(write_only=True)
def create(self, validated_data: dict) -> Participation:
"""Explicitly create as entry is a nested serializer."""
with transaction.atomic():
entry_data = validated_data['entry']
entry = FormEntrySerializer().create(entry_data)
participation = Participation.objects.create(
user=validated_data['user'],
edition=validated_data['edition'],
state=Participation.STATE_PENDING,
entry=entry,
)
return participation
class Meta: # noqa
model = Participation
fields = ('id', 'submitted', 'user', 'edition', 'state', 'entry',)
extra_kwargs = {
'state': {
'label': 'State of the participation.'
}
}
class EditionFormSerializer(serializers.ModelSerializer): class EditionFormSerializer(serializers.ModelSerializer):
"""Serializer for edition form objects.""" """Serializer for edition form objects."""
edition = serializers.PrimaryKeyRelatedField(read_only=True) edition = serializers.PrimaryKeyRelatedField(read_only=True)
title = serializers.SerializerMethodField()
def get_title(self, obj) -> str:
"""Return the form's title if form is set."""
form = getattr(obj, 'form', None)
return form and str(form) or None
class Meta: # noqa class Meta: # noqa
model = EditionForm model = EditionForm
fields = ('id', 'edition', 'deadline') fields = ('id', 'title', 'edition', 'deadline')
class EditionFormDetailSerializer(EditionFormSerializer): class EditionFormDetailSerializer(EditionFormSerializer):
...@@ -92,6 +59,7 @@ class EditionListSerializer(serializers.HyperlinkedModelSerializer): ...@@ -92,6 +59,7 @@ class EditionListSerializer(serializers.HyperlinkedModelSerializer):
organizers = serializers.SerializerMethodField() organizers = serializers.SerializerMethodField()
participations = serializers.SerializerMethodField() participations = serializers.SerializerMethodField()
edition_form = EditionFormSerializer() edition_form = EditionFormSerializer()
participates = serializers.SerializerMethodField()
def get_organizers(self, obj: Edition) -> int: def get_organizers(self, obj: Edition) -> int:
"""Return the number of organizers.""" """Return the number of organizers."""
...@@ -101,15 +69,71 @@ class EditionListSerializer(serializers.HyperlinkedModelSerializer): ...@@ -101,15 +69,71 @@ class EditionListSerializer(serializers.HyperlinkedModelSerializer):
"""Return the number of participations.""" """Return the number of participations."""
return obj.participations.count() return obj.participations.count()
def get_participates(self, obj: Edition) -> bool:
"""Return whether the current user participates in the edition."""
request = self.context['request']
if not request.user:
return False
return request.user.pk in obj.participations.values_list('user__pk')
class Meta: # noqa class Meta: # noqa
model = Edition model = Edition
fields = ('id', 'url', 'name', 'year', 'project', 'description', fields = ('id', 'url', 'name', 'year', 'project', 'description',
'organizers', 'participations', 'edition_form',) 'organizers', 'participations', 'edition_form',
'participates',)
extra_kwargs = { extra_kwargs = {
'url': {'view_name': 'api:edition-detail'}, 'url': {'view_name': 'api:edition-detail'},
} }
class ParticipationSerializer(serializers.ModelSerializer):
"""Serializer for project edition participations."""
user = UserField(
label='Utilisateur',
help_text='Identifier for the user who participates.')
edition_id = serializers.PrimaryKeyRelatedField(
source='edition',
queryset=Edition.objects.all(),
label='Édition',
help_text='Identifier for the associated edition.')
edition_form_title = serializers.SerializerMethodField()
entry = FormEntrySerializer(write_only=True)
def get_edition_form_title(self, obj: Participation) -> str:
form = getattr(obj.edition, 'edition_form', None)
return form and str(form) or None
def create(self, validated_data: dict) -> Participation:
"""Explicitly create as entry is a nested serializer."""
with transaction.atomic():
entry_data = validated_data['entry']
entry = FormEntrySerializer().create(entry_data)
participation = Participation.objects.create(
user=validated_data['user'],
edition=validated_data['edition'],
state=Participation.STATE_PENDING,
entry=entry,
)
return participation
class Meta: # noqa
model = Participation
fields = ('id', 'submitted', 'user',
'edition_id', 'edition_form_title',
'state', 'entry',)
extra_kwargs = {
'state': {
'label': 'State of the participation.'
}
}
class EditionDetailSerializer(EditionListSerializer): class EditionDetailSerializer(EditionListSerializer):
"""Detail serializer for Edition objects.""" """Detail serializer for Edition objects."""
......
"""Projects views.""" """Projects views."""
from django.utils.timezone import now
from rest_framework import mixins, permissions, viewsets from rest_framework import mixins, permissions, viewsets
from rest_framework.response import Response
from rest_framework.decorators import action
from django_filters.rest_framework.backends import DjangoFilterBackend
from django_filters import rest_framework as filters from django_filters import rest_framework as filters
...@@ -214,16 +218,50 @@ class EditionViewSet(viewsets.ReadOnlyModelViewSet): ...@@ -214,16 +218,50 @@ class EditionViewSet(viewsets.ReadOnlyModelViewSet):
} }
""" """
queryset = Edition.objects.all() queryset = Edition.objects.all().prefetch_related(
'participations', 'organizers'
)
permission_classes = (permissions.IsAuthenticated,) permission_classes = (permissions.IsAuthenticated,)
filter_backends = (filters.DjangoFilterBackend,) filter_backends = (filters.DjangoFilterBackend,)
filter_fields = ('project', 'year',) filter_fields = ('project', 'year',)
def get_serializer_class(self): def get_serializer_class(self):
if self.action == 'list': if self.action == 'retrieve':
return EditionListSerializer
elif self.action == 'retrieve':
return EditionDetailSerializer return EditionDetailSerializer
return EditionListSerializer
@action(methods=['get'], detail=False)
def open_registrations(self, request, **kwargs):
"""Return a list of the editions with open registrations.
These are the editions that have an edition form set and whose
deadline is a future date.
### Example response
[
{
"id": 1,
"url": "http://localhost:8000/api/editions/1/",
"name": "",
"year": 2018,
"project": 1,
"description": "",
"organizers": 0,
"participations": 3,
"edition_form": {
"id": 1,
"edition": 1,
"deadline": "2018-06-30"
}
}
]
"""
queryset = self.get_queryset().filter(
edition_form__isnull=False,
edition_form__deadline__gte=now().date())
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
class ParticipationViewSet(mixins.CreateModelMixin, class ParticipationViewSet(mixins.CreateModelMixin,
...@@ -251,7 +289,8 @@ class ParticipationViewSet(mixins.CreateModelMixin, ...@@ -251,7 +289,8 @@ class ParticipationViewSet(mixins.CreateModelMixin,
"date_of_birth": null, "date_of_birth": null,
"url": "http://localhost:8000/api/users/3/" "url": "http://localhost:8000/api/users/3/"
}, },
"edition": 1, "edition_id": 1,
"edition_form_title": "Inscriptions à Oser la Prépa 2018"
"state": "valid" "state": "valid"
}, },
] ]
...@@ -276,7 +315,8 @@ class ParticipationViewSet(mixins.CreateModelMixin, ...@@ -276,7 +315,8 @@ class ParticipationViewSet(mixins.CreateModelMixin,
"date_of_birth": null, "date_of_birth": null,
"url": "http://localhost:8000/api/users/3/" "url": "http://localhost:8000/api/users/3/"
}, },
"edition": 1, "edition_id": 1,
"edition_form_title": "Inscriptions à Oser la Prépa 2018"
"state": "valid" "state": "valid"
} }
""" """
...@@ -284,3 +324,5 @@ class ParticipationViewSet(mixins.CreateModelMixin, ...@@ -284,3 +324,5 @@ class ParticipationViewSet(mixins.CreateModelMixin,
queryset = Participation.objects.all() queryset = Participation.objects.all()
serializer_class = ParticipationSerializer serializer_class = ParticipationSerializer
permission_classes = (permissions.IsAuthenticated,) permission_classes = (permissions.IsAuthenticated,)
filter_backends = (DjangoFilterBackend,)
filter_fields = ('user', 'state',)
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
from rest_framework import status from rest_framework import status
from tests.utils import SimpleAPITestCase, logged_in from tests.utils import SimpleAPITestCase, logged_in
from projects.factory import EditionFactory from projects.factory import EditionFactory, EditionFormFactory
class EditionEndpointsTest(SimpleAPITestCase): class EditionEndpointsTest(SimpleAPITestCase):
...@@ -13,7 +13,7 @@ class EditionEndpointsTest(SimpleAPITestCase): ...@@ -13,7 +13,7 @@ class EditionEndpointsTest(SimpleAPITestCase):
read_expected_fields = {'id', 'url', 'name', 'year', 'project', read_expected_fields = {'id', 'url', 'name', 'year', 'project',
'description', 'organizers', 'participations', 'description', 'organizers', 'participations',
'edition_form',} 'edition_form', 'participates'}
def setUp(self): def setUp(self):
self.factory.create_batch(3) self.factory.create_batch(3)
...@@ -51,3 +51,12 @@ class EditionEndpointsTest(SimpleAPITestCase): ...@@ -51,3 +51,12 @@ class EditionEndpointsTest(SimpleAPITestCase):
self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.status_code, status.HTTP_200_OK)
fields = set(response.data) fields = set(response.data)
self.assertSetEqual(fields, self.read_expected_fields) self.assertSetEqual(fields, self.read_expected_fields)
@logged_in
def test_list_open_registrations(self):
edition = self.factory.create()
EditionFormFactory.create(edition=edition)
url = '/api/editions/open_registrations/'
response = self.client.get(url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data), 1)
...@@ -13,7 +13,7 @@ class ParticipationReadTest(SimpleAPITestCase): ...@@ -13,7 +13,7 @@ class ParticipationReadTest(SimpleAPITestCase):
factory = ParticipationFactory factory = ParticipationFactory
read_expected_fields = {'id', 'user', 'edition', read_expected_fields = {'id', 'user', 'edition_id', 'edition_form_title',
'state', 'submitted'} 'state', 'submitted'}
def setUp(self): def setUp(self):
...@@ -66,7 +66,7 @@ class ParticipationCreateTest(SimpleAPITestCase): ...@@ -66,7 +66,7 @@ class ParticipationCreateTest(SimpleAPITestCase):
} }
payload = { payload = {
'user': user.pk, 'user': user.pk,
'edition': edition.pk, 'edition_id': edition.pk,
'entry': entry, 'entry': entry,
} }
return self.client.post('/api/project-participations/', return self.client.post('/api/project-participations/',
...@@ -80,5 +80,7 @@ class ParticipationCreateTest(SimpleAPITestCase): ...@@ -80,5 +80,7 @@ class ParticipationCreateTest(SimpleAPITestCase):
def test_returns_expected_fields(self): def test_returns_expected_fields(self):
response = self.perform_create() response = self.perform_create()
self.assertEqual(response.status_code, status.HTTP_201_CREATED) self.assertEqual(response.status_code, status.HTTP_201_CREATED)
expected = {'id', 'user', 'edition', 'state', 'submitted'} expected = {
'id', 'user', 'edition_id', 'edition_form_title',
'state', 'submitted'}
self.assertSetEqual(expected, set(response.data)) self.assertSetEqual(expected, set(response.data))
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment