Skip to content

Commit 6f43eea

Browse files
committed
Implement API views from ddd
1 parent 4bdc7e9 commit 6f43eea

File tree

10 files changed

+937
-98
lines changed

10 files changed

+937
-98
lines changed

api/generator.py

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# ##############################################################################
2+
#
3+
# OSIS stands for Open Student Information System. It's an application
4+
# designed to manage the core business of higher education institutions,
5+
# such as universities, faculties, institutes and professional schools.
6+
# The core business involves the administration of students, teachers,
7+
# courses, programs and so on.
8+
#
9+
# Copyright (C) 2015-2021 Université catholique de Louvain (http://www.uclouvain.be)
10+
#
11+
# This program is free software: you can redistribute it and/or modify
12+
# it under the terms of the GNU General Public License as published by
13+
# the Free Software Foundation, either version 3 of the License, or
14+
# (at your option) any later version.
15+
#
16+
# This program is distributed in the hope that it will be useful,
17+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
18+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19+
# GNU General Public License for more details.
20+
#
21+
# A copy of this license - GNU General Public License - is available
22+
# at the root of the source code of this program. If not,
23+
# see http://www.gnu.org/licenses/.
24+
#
25+
# ##############################################################################
26+
from rest_framework.schemas.openapi import SchemaGenerator
27+
28+
29+
class AdmissionSchemaGenerator(SchemaGenerator):
30+
def get_schema(self, *args, **kwargs):
31+
schema = super().get_schema(*args, **kwargs)
32+
schema["info"]["title"] = "Admission API"
33+
schema["info"]["description"] = "This API delivers data for the Admission project."
34+
schema["info"]["contact"] = {
35+
"name": "UCLouvain - OSIS",
36+
"url": "https://github.com/uclouvain/osis"
37+
}
38+
schema["servers"] = [
39+
{
40+
"url": "https://{environment}.osis.uclouvain.be/api/v1/admission/",
41+
"variables": {
42+
"environment": {
43+
"default": "dev",
44+
"enum": [
45+
"dev",
46+
"qa",
47+
"test"
48+
]
49+
}
50+
}
51+
},
52+
{
53+
"url": "https://osis.uclouvain.be/api/v1/admission/",
54+
"description": "Production server"
55+
}
56+
]
57+
schema["security"] = [{"Token": []}]
58+
schema['components']["securitySchemes"] = {
59+
"Token": {
60+
"type": "apiKey",
61+
"in": "header",
62+
"name": "Authorization",
63+
"description": "Enter your token in the format **Token <token>**"
64+
}
65+
}
66+
return schema

api/url_v1.py

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# ##############################################################################
2+
#
3+
# OSIS stands for Open Student Information System. It's an application
4+
# designed to manage the core business of higher education institutions,
5+
# such as universities, faculties, institutes and professional schools.
6+
# The core business involves the administration of students, teachers,
7+
# courses, programs and so on.
8+
#
9+
# Copyright (C) 2015-2021 Université catholique de Louvain (http://www.uclouvain.be)
10+
#
11+
# This program is free software: you can redistribute it and/or modify
12+
# it under the terms of the GNU General Public License as published by
13+
# the Free Software Foundation, either version 3 of the License, or
14+
# (at your option) any later version.
15+
#
16+
# This program is distributed in the hope that it will be useful,
17+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
18+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19+
# GNU General Public License for more details.
20+
#
21+
# A copy of this license - GNU General Public License - is available
22+
# at the root of the source code of this program. If not,
23+
# see http://www.gnu.org/licenses/.
24+
#
25+
# ##############################################################################
26+
from django.urls import path
27+
28+
from admission.api import views
29+
30+
app_name = "admission_api_v1"
31+
urlpatterns = [
32+
path('propositions', views.PropositionListViewSet.as_view()),
33+
path('propositions/<uuid:uuid>', views.PropositionViewSet.as_view()),
34+
path('autocomplete/sector', views.AutocompleteSectorViewSet.as_view()),
35+
path('autocomplete/sector/<str:sigle>/doctorates', views.AutocompleteDoctoratViewSet.as_view()),
36+
]

api/views/__init__.py

+6-2
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,12 @@
2424
#
2525
# ##############################################################################
2626

27-
from .doctorate import DoctorateAdmissionViewSet
27+
from admission.api.views.doctorate import *
28+
from admission.api.views.autocomplete import *
2829

2930
__all__ = [
30-
"DoctorateAdmissionViewSet",
31+
"PropositionViewSet",
32+
"PropositionListViewSet",
33+
"AutocompleteDoctoratViewSet",
34+
"AutocompleteSectorViewSet",
3135
]

api/views/autocomplete.py

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# ##############################################################################
2+
#
3+
# OSIS stands for Open Student Information System. It's an application
4+
# designed to manage the core business of higher education institutions,
5+
# such as universities, faculties, institutes and professional schools.
6+
# The core business involves the administration of students, teachers,
7+
# courses, programs and so on.
8+
#
9+
# Copyright (C) 2015-2021 Université catholique de Louvain (http://www.uclouvain.be)
10+
#
11+
# This program is free software: you can redistribute it and/or modify
12+
# it under the terms of the GNU General Public License as published by
13+
# the Free Software Foundation, either version 3 of the License, or
14+
# (at your option) any later version.
15+
#
16+
# This program is distributed in the hope that it will be useful,
17+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
18+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19+
# GNU General Public License for more details.
20+
#
21+
# A copy of this license - GNU General Public License - is available
22+
# at the root of the source code of this program. If not,
23+
# see http://www.gnu.org/licenses/.
24+
#
25+
# ##############################################################################
26+
from django.db.models import F
27+
from rest_framework.generics import ListAPIView
28+
from rest_framework.response import Response
29+
30+
from admission.contrib import serializers
31+
from admission.contrib.models import EntityProxy
32+
from base.models.enums.entity_type import SECTOR
33+
from ddd.logic.admission.preparation.projet_doctoral.commands import SearchDoctoratCommand
34+
from infrastructure.messages_bus import message_bus_instance
35+
36+
37+
class AutocompleteSectorViewSet(ListAPIView):
38+
pagination_class = None
39+
filter_backends = None
40+
serializer_class = serializers.SectorDTOSerializer
41+
42+
def list(self, request, **kwargs):
43+
# TODO revert to command once it's in the shared kernel
44+
qs = EntityProxy.objects.with_acronym().with_title().with_type().filter(type=SECTOR).annotate(
45+
sigle=F('acronym'),
46+
intitule_fr=F('title'),
47+
intitule_en=F('title'),
48+
).values('sigle', 'intitule_fr', 'intitule_en')
49+
serializer = serializers.SectorDTOSerializer(instance=qs, many=True)
50+
return Response(serializer.data)
51+
52+
53+
class AutocompleteDoctoratViewSet(ListAPIView):
54+
pagination_class = None
55+
filter_backends = None
56+
serializer_class = serializers.DoctoratDTOSerializer
57+
58+
def list(self, request, **kwargs):
59+
doctorat_list = message_bus_instance.invoke(
60+
SearchDoctoratCommand(sigle_secteur_entite_gestion=kwargs.get('sigle'))
61+
)
62+
serializer = serializers.DoctoratDTOSerializer(instance=doctorat_list, many=True)
63+
return Response(serializer.data)

api/views/doctorate.py

+109-21
Original file line numberDiff line numberDiff line change
@@ -23,33 +23,121 @@
2323
# see http://www.gnu.org/licenses/.
2424
#
2525
# ##############################################################################
26-
27-
from rest_framework import viewsets, status
28-
from rest_framework.authentication import SessionAuthentication
26+
from rest_framework import mixins, status
27+
from rest_framework.generics import GenericAPIView, ListCreateAPIView
2928
from rest_framework.response import Response
29+
from rest_framework.schemas.openapi import AutoSchema
30+
from rest_framework.serializers import Serializer
3031

31-
from admission.contrib.models import DoctorateAdmission
32-
from admission.contrib.serializers import (
33-
DoctorateAdmissionReadSerializer, DoctorateAdmissionWriteSerializer
32+
from admission.contrib import serializers
33+
from ddd.logic.admission.preparation.projet_doctoral.commands import (
34+
CompleterPropositionCommand, GetPropositionCommand,
35+
InitierPropositionCommand,
36+
SearchPropositionsCommand,
3437
)
38+
from infrastructure.messages_bus import message_bus_instance
39+
40+
41+
class DetailedAutoSchema(AutoSchema):
42+
def get_request_body(self, path, method):
43+
if method not in ('PUT', 'PATCH', 'POST'):
44+
return {}
45+
46+
self.request_media_types = self.map_parsers(path, method)
47+
48+
serializer = self.get_serializer(path, method, for_response=False)
49+
50+
if not isinstance(serializer, Serializer):
51+
item_schema = {}
52+
else:
53+
item_schema = self._get_reference(serializer)
54+
55+
return {
56+
'content': {
57+
ct: {'schema': item_schema}
58+
for ct in self.request_media_types
59+
}
60+
}
61+
62+
def get_components(self, path, method):
63+
if method.lower() == 'delete':
64+
return {}
65+
66+
components = {}
67+
for with_response in [True, False]:
68+
serializer = self.get_serializer(path, method, for_response=with_response)
69+
if not isinstance(serializer, Serializer):
70+
return {}
71+
component_name = self.get_component_name(serializer)
72+
content = self.map_serializer(serializer)
73+
components[component_name] = content
74+
75+
return components
76+
77+
def get_serializer(self, path, method, for_response=True):
78+
raise NotImplementedError
79+
3580

81+
class PropositionListSchema(DetailedAutoSchema):
82+
def get_operation_id_base(self, path, method, action):
83+
return '_proposition' if method == 'POST' else '_propositions'
3684

37-
class DoctorateAdmissionViewSet(viewsets.ModelViewSet):
38-
queryset = DoctorateAdmission.objects.all()
39-
authentication_classes = [SessionAuthentication, ]
40-
lookup_field = "uuid"
85+
def get_serializer(self, path, method, for_response=True):
86+
if method == 'POST':
87+
if for_response:
88+
return serializers.PropositionIdentityDTOSerializer()
89+
return serializers.InitierPropositionCommandSerializer()
90+
return serializers.PropositionSearchDTOSerializer()
4191

42-
def get_serializer_class(self):
43-
if self.action in ["create", "update", "partial_update"]:
44-
return DoctorateAdmissionWriteSerializer
45-
return DoctorateAdmissionReadSerializer
4692

47-
def create(self, request, *args, **kwargs):
48-
serializer = self.get_serializer(data=request.data)
93+
class PropositionListViewSet(ListCreateAPIView):
94+
schema = PropositionListSchema()
95+
pagination_class = None
96+
filter_backends = None
97+
98+
def list(self, request, **kwargs):
99+
proposition_list = message_bus_instance.invoke(
100+
SearchPropositionsCommand(matricule_candidat=request.user.person.global_id)
101+
)
102+
serializer = serializers.PropositionSearchDTOSerializer(instance=proposition_list, many=True)
103+
return Response(serializer.data)
104+
105+
def create(self, request, **kwargs):
106+
serializer = serializers.InitierPropositionCommandSerializer(data=request.data)
49107
serializer.is_valid(raise_exception=True)
50-
self.perform_create(serializer)
51-
serializer = DoctorateAdmissionReadSerializer(instance=serializer.instance)
52-
headers = self.get_success_headers(serializer.data)
53-
return Response(
54-
serializer.data, status=status.HTTP_201_CREATED, headers=headers
108+
result = message_bus_instance.invoke(InitierPropositionCommand(**serializer.data))
109+
serializer = serializers.PropositionIdentityDTOSerializer(instance=result)
110+
return Response(serializer.data, status=status.HTTP_201_CREATED)
111+
112+
113+
class PropositionSchema(DetailedAutoSchema):
114+
def get_operation_id_base(self, path, method, action):
115+
return '_proposition'
116+
117+
def get_serializer(self, path, method, for_response=True):
118+
if method == 'PUT':
119+
if for_response:
120+
return serializers.PropositionIdentityDTOSerializer()
121+
return serializers.CompleterPropositionCommandSerializer()
122+
return serializers.PropositionDTOSerializer()
123+
124+
125+
class PropositionViewSet(mixins.RetrieveModelMixin, mixins.UpdateModelMixin, GenericAPIView):
126+
schema = PropositionSchema()
127+
pagination_class = None
128+
filter_backends = None
129+
130+
def get(self, request, *args, **kwargs):
131+
# TODO call osis_role perm for this object
132+
proposition = message_bus_instance.invoke(
133+
GetPropositionCommand(uuid_proposition=kwargs.get('uuid'))
55134
)
135+
serializer = serializers.PropositionDTOSerializer(instance=proposition)
136+
return Response(serializer.data)
137+
138+
def put(self, request, *args, **kwargs):
139+
serializer = serializers.CompleterPropositionCommandSerializer(data=request.data)
140+
serializer.is_valid(raise_exception=True)
141+
result = message_bus_instance.invoke(CompleterPropositionCommand(**serializer.data))
142+
serializer = serializers.PropositionIdentityDTOSerializer(instance=result)
143+
return Response(serializer.data, status=status.HTTP_200_OK)

contrib/serializers/__init__.py

+2-8
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,5 @@
2424
#
2525
# ##############################################################################
2626

27-
from .doctorate import (
28-
DoctorateAdmissionReadSerializer, DoctorateAdmissionWriteSerializer
29-
)
30-
31-
__all__ = [
32-
"DoctorateAdmissionReadSerializer",
33-
"DoctorateAdmissionWriteSerializer",
34-
]
27+
from .doctorate import *
28+
# from .person import *

0 commit comments

Comments
 (0)