viewsets.py 10.9 KB
Newer Older
1
2
import logging

3
from django.conf import settings
4
from rest_framework.permissions import BasePermission
5
from rest_framework.response import Response
6
from rest_framework.viewsets import ViewSet
7
8

from backend_app.checks import check_viewsets
9
from backend_app.models.abstract.base import BaseModelViewSet
10
from backend_app.models.abstract.essentialModule import EssentialModuleViewSet
11
12
13
14
15
from backend_app.models.campus import CampusViewSet, MainCampusViewSet
from backend_app.models.city import CityViewSet
from backend_app.models.country import CountryViewSet
from backend_app.models.countryDri import CountryDriViewSet
from backend_app.models.countryScholarship import CountryScholarshipViewSet
16
from backend_app.models.course import Course
17
from backend_app.models.courseFeedback import CourseFeedback
18
from backend_app.models.currency import CurrencyViewSet
19
from backend_app.models.exchange import Exchange, UnivMajorMinorsViewSet
20
from backend_app.models.exchangeFeedback import ExchangeFeedbackViewSet
21
from backend_app.models.file_picture import FileViewSet, PictureViewSet
22
23
from backend_app.models.for_testing.moderation import ForTestingModerationViewSet
from backend_app.models.for_testing.versioning import ForTestingVersioningViewSet
Segolene Brisemeur's avatar
Segolene Brisemeur committed
24
from backend_app.models.language import LanguageViewSet
25
from backend_app.models.offer import OfferViewSet
26
from backend_app.models.partner import Partner
27
28
29
30
from backend_app.models.pendingModeration import (
    PendingModerationViewSet,
    PendingModerationObjViewSet,
)
31
32
33
34
from backend_app.models.recommendationList import (
    RecommendationListViewSet,
    RecommendationList,
)
35
36
37
38
from backend_app.models.taggedItems import (
    CountryTaggedItemViewSet,
    UniversityTaggedItemViewSet,
)
39
40
41
42
43
44
45
from backend_app.models.university import UniversityViewSet
from backend_app.models.universityDri import UniversityDriViewSet
from backend_app.models.universityInfo import UniversityInfoViewSet
from backend_app.models.universityScholarship import UniversityScholarshipViewSet
from backend_app.models.universitySemestersDates import UniversitySemestersDatesViewSet
from backend_app.models.userData import UserDataViewSet
from backend_app.models.version import VersionViewSet
46
47
48
49
50
51
from backend_app.permissions.app_permissions import ReadOnly, IsStaff, NoDelete, NoPost
from backend_app.serializers import (
    CourseFeedbackSerializer,
    ExchangeSerializerSimple,
    CourseSerializer,
)
52
from backend_app.settings.defaults import OBJ_MODERATION_PERMISSIONS
53
from base_app.models import UserViewset, User, SiteInformationViewSet
54
from external_data.management.commands.utils import UtcData
55
from external_data.models import ExternalDataUpdateInfo
56

57

58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
class CourseViewSet(BaseModelViewSet):
    queryset = Course.objects.all().select_related(
        "exchange",
        "exchange__feedbacks",
        "exchange__feedbacks__moderated_by",
        "exchange__feedbacks__updated_by",
        "course_feedback",
        "course_feedback__moderated_by",
        "course_feedback__updated_by",
    )
    serializer_class = CourseSerializer
    permission_classes = (ReadOnly,)
    end_point_route = "courses"
    filterset_fields = ("exchange",)
    required_filterset_fields = ("exchange",)


class CourseFeedbackPermission(BasePermission):
    """
    Permission that checks that the requester is the student concern by the exchange / course.
    """

    def has_object_permission(self, request, view, obj):
        return request.user.pk == obj.course.exchange.student.pk


84
class CourseFeedbackViewSet(EssentialModuleViewSet):
85
86
87
    permission_classes = (
        NoDelete & NoPost & (ReadOnly | IsStaff | CourseFeedbackPermission),
    )
88
    queryset = CourseFeedback.objects.filter(course__unlinked=False).select_related(
89
90
        "course", "updated_by", "moderated_by"
    )
91

92
93
    serializer_class = CourseFeedbackSerializer
    end_point_route = "courseFeedbacks"
94
95
96
97
98
99
100
    filterset_fields = ("course__exchange",)
    required_filterset_fields = ("course__exchange",)


class ExchangeViewSet(BaseModelViewSet):
    permission_classes = (ReadOnly,)
    queryset = (
101
        Exchange.objects.filter(unlinked=False)
102
103
104
105
106
107
108
        .select_related("student")
        .prefetch_related("exchange_courses", "exchange_courses__course_feedback")
    )
    serializer_class = ExchangeSerializerSimple
    end_point_route = "exchanges"
    filterset_fields = ("student",)
    required_filterset_fields = ("student",)
109
110


111
ALL_API_VIEWSETS = [
112
    SiteInformationViewSet,
113
    UserViewset,
114
115
116
117
118
119
120
    CampusViewSet,
    MainCampusViewSet,
    CityViewSet,
    CountryViewSet,
    CountryDriViewSet,
    CountryScholarshipViewSet,
    CountryTaggedItemViewSet,
121
    CourseViewSet,
122
    CourseFeedbackViewSet,
123
124
    CurrencyViewSet,
    OfferViewSet,
Segolene Brisemeur's avatar
Segolene Brisemeur committed
125
    LanguageViewSet,
126
127
    PendingModerationViewSet,
    PendingModerationObjViewSet,
128
129
    FileViewSet,
    PictureViewSet,
130
    ExchangeViewSet,
131
    ExchangeFeedbackViewSet,
132
    UnivMajorMinorsViewSet,
133
    RecommendationListViewSet,
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
    UniversityViewSet,
    UniversityDriViewSet,
    UniversityInfoViewSet,
    UniversityScholarshipViewSet,
    UniversitySemestersDatesViewSet,
    UniversityTaggedItemViewSet,
    UserDataViewSet,
    VersionViewSet,
]

if settings.TESTING:
    # We only register viewsets in a testing environment
    ALL_API_VIEWSETS += [ForTestingModerationViewSet, ForTestingVersioningViewSet]


149
class AppModerationStatusViewSet(ViewSet):
150
151
152
153
154
155
156
157
158
    """
    Viewset to know what is the app moderation status
    """

    # Since AppModerationStatusViewSet doesn't inherit from BaseModelViewSet
    # We need to link here the correct permissions
    permission_classes = (ReadOnly,)
    end_point_route = "serverModerationStatus"

159
    def list(self, request):
160
161
162
163
164
165
166
167
        return Response(
            {
                "activated": settings.MODERATION_ACTIVATED,
                "moderator_level": OBJ_MODERATION_PERMISSIONS["moderator"],
            }
        )


168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
class LatestUpdateExternalDataViewSet(ViewSet):
    """
    Viewset to fetch the latest update dates of the external data
    """

    # Since AppModerationStatusViewSet doesn't inherit from BaseModelViewSet
    # We need to link here the correct permissions
    permission_classes = (ReadOnly,)
    end_point_route = "externalDataUpdateInfo"

    def list(self, request):
        objects = (
            ExternalDataUpdateInfo.objects.all()
            .order_by("source", "-timestamp")
            .distinct("source")
        )

        return Response(
            list(
                map(
                    lambda obj: dict(timestamp=obj.timestamp, source=obj.source),
                    objects,
                )
            )
        )


195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
class UnlinkedUtcPartners(ViewSet):
    """
    Viewset to fetch the latest list of utc partners that are not linked to a univeristy
    """

    permission_classes = (ReadOnly,)
    end_point_route = "unlinkedUtcPartners"

    def list(self, request):
        partners = Partner.objects.filter(university=None)

        return Response(list(map(lambda partner: partner.univ_name, partners)))


class UpdateStudentExchangesViewSet(ViewSet):
    """
    Viewset to be able to ban and un-ban users from the site
    """

    end_point_route = "updateStudentExchanges"
    permission_classes = (ReadOnly,)

    def list(self, request, **kwargs):
        user = request.user
        UtcData().update_one_student(user.username)
        return Response()


223
class LogFrontendErrorsViewSet(ViewSet):
224
225
226
227
228
229
230
    """
    Viewset to handle the logging of errors coming from the frontend.
    """

    permission_classes = tuple()
    end_point_route = "frontendErrors"

231
    def create(self, request):
232
233
234
235
236
237
238
239
240
        logger = logging.getLogger("frontend")
        data = request.data
        if "componentStack" in data.keys():
            logger.error(request.data["componentStack"])
        else:
            logger.error(request.data)
        return Response(status=201)


241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
class BannedUserViewSet(ViewSet):
    """
    Viewset to be able to ban and un-ban users from the site
    """

    end_point_route = "banned_users"
    permission_classes = (IsStaff,)

    def list(self, request, **kwargs):
        return Response(
            [
                dict(user_id=user.pk, user_login=user.username)
                for user in User.objects.filter(is_banned=True)
            ]
        )

    def update(self, request, pk=None):
        if pk is None:
            return Response(status=403)

        user = User.objects.get(pk=pk)
        if user.is_staff:
            # Prevent ban of admin users
            return Response(status=403)
        user.is_banned = True
        user.save()
        return Response(status=200)

    def delete(self, request, pk=None):
        if pk is None:
            return Response(status=403)

        user = User.objects.get(pk=pk)
        user.is_banned = False
        user.save()
        return Response(status=200)


279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
class DeleteUserViewSet(ViewSet):
    """
    Viewset to handle account deletion
    """

    end_point_route = "emptyUserAccount"
    permission_classes = tuple()

    def create(self, request):
        """
        Line up the user from the request for deletion
        """
        user = request.user
        user.delete_next_time = True
        user.save()
        return Response(status=201)

    def update(self, request):
        # Here only to have the correct routes automatically generated, not to be used.
        return Response(status=403)

    def delete(self, request, pk="osef"):  # don't delete this unused argument!
        """
        Un-Line up the user from the request for deletion
        """
        user = request.user
        user.delete_next_time = False
        user.save()
        return Response(status=200)


310
311
312
313
314
315
316
317
318
319
320
class RecommendationListChangeFollowerViewSet(ViewSet):
    """
    Viewset to be able to add or delete followers on
    a recommendation list
    """

    # Since RecommendationListChangeFollowerViewSet doesn't inherit from BaseModelViewSet
    # We need to link here the correct permissions
    end_point_route = "recommendationListChangeFollower"
    permission_classes = tuple()

321
    def update(self, request, pk=None):
322
323
324
325
326
        if pk is None:
            return Response(status=403)

        recommendation = RecommendationList.objects.get(pk=pk)
        if recommendation.is_public:
327
            recommendation.followers.add(request.user)
328
329
330
331
332
333
334
335
336
            recommendation.save()
            return Response(status=200)
        else:
            return Response(status=403)

    def delete(self, request, pk=None):
        if pk is None:
            return Response(status=403)
        # can delete folower even if list not public
337
        recommendation = RecommendationList.objects.get(pk=pk)
338
        recommendation.followers.remove(request.user)
339
340
341
342
        recommendation.save()
        return Response(status=200)


343
344
ALL_API_VIEW_VIEWSETS = [
    AppModerationStatusViewSet,
345
    LatestUpdateExternalDataViewSet,
346
347
    UnlinkedUtcPartners,
    UpdateStudentExchangesViewSet,
348
349
    LogFrontendErrorsViewSet,
    BannedUserViewSet,
350
    RecommendationListChangeFollowerViewSet,
351
    DeleteUserViewSet,
352
]
353
354
355
356

ALL_VIEWSETS = ALL_API_VIEWSETS + ALL_API_VIEW_VIEWSETS

check_viewsets(ALL_VIEWSETS)