Blame view

flashcards/views.py 14.3 KB
776577266   Andrew Buss   websockets notifi...
1
  from random import sample
54bba1fea   Andrew Buss   enforce enrollmen...
2

776577266   Andrew Buss   websockets notifi...
3
  import django
72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
4
  from django.contrib import auth
29c433096   Andrew Buss   updated readme to...
5
  from django.shortcuts import get_object_or_404
ee4104aa2   Rohan Rangray   Added a Study vie...
6
  from flashcards.api import StandardResultsSetPagination, IsEnrolledInAssociatedSection, IsFlashcardReviewer
f66f9ca7d   Rohan Rangray   Fixed SwaggerUI p...
7
  from flashcards.models import Section, User, Flashcard, FlashcardHide, UserFlashcard, UserFlashcardQuiz
776577266   Andrew Buss   websockets notifi...
8
  from flashcards.notifications import notify_new_card
ce17f969f   Andrew Buss   Restructured api,...
9
  from flashcards.serializers import SectionSerializer, UserUpdateSerializer, RegistrationSerializer, UserSerializer, \
bca16d61f   Rohan Rangray   Added the patch m...
10
      PasswordResetSerializer, PasswordResetRequestSerializer, EmailPasswordSerializer, FlashcardSerializer, \
d818aecbc   Rohan Rangray   Merged.
11
12
      FlashcardUpdateSerializer, QuizRequestSerializer, QuizResponseSerializer, \
      QuizAnswerRequestSerializer, DeepSectionSerializer
3188404a6   Andrew Buss   feed tests works(?)
13
  from rest_framework.decorators import detail_route, permission_classes, api_view, list_route
2c22131d9   Andrew Buss   Added department ...
14
  from rest_framework.generics import ListAPIView, GenericAPIView
ee4104aa2   Rohan Rangray   Added a Study vie...
15
  from rest_framework.mixins import CreateModelMixin, RetrieveModelMixin, UpdateModelMixin
01ea09616   Andrew Buss   Don't be silly, y...
16
  from rest_framework.permissions import IsAuthenticated
72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
17
  from rest_framework.viewsets import ReadOnlyModelViewSet, GenericViewSet
ce17f969f   Andrew Buss   Restructured api,...
18
  from django.core.mail import send_mail
72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
19
  from django.contrib.auth import authenticate
ce17f969f   Andrew Buss   Restructured api,...
20
  from django.contrib.auth.tokens import default_token_generator
5d861cbfb   Rohan Rangray   Wrote tests for F...
21
  from rest_framework.status import HTTP_204_NO_CONTENT, HTTP_201_CREATED, HTTP_200_OK
ce17f969f   Andrew Buss   Restructured api,...
22
  from rest_framework.response import Response
72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
23
  from rest_framework.exceptions import AuthenticationFailed, NotAuthenticated, ValidationError, PermissionDenied
7aa4b42d3   Andrew Buss   Fixed registratio...
24
  from simple_email_confirmation import EmailAddress
2a72f1a8a   Andrew Buss   Development serve...
25

c4a8e6cef   Rohan Rangray   Added pagination ...
26

ce17f969f   Andrew Buss   Restructured api,...
27
  class SectionViewSet(ReadOnlyModelViewSet):
491577131   Andrew Buss   Class -> section....
28
      queryset = Section.objects.all()
bd04c9af5   Andrew Buss   Only retrieve lec...
29
      serializer_class = DeepSectionSerializer
c4a8e6cef   Rohan Rangray   Added pagination ...
30
      pagination_class = StandardResultsSetPagination
72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
31
      permission_classes = [IsAuthenticated]
a2d8c4229   Andrew Buss   added feed
32
      @detail_route(methods=['GET'])
57168cbc2   Rachel Lee   Stuff
33
34
35
36
37
      def flashcards(self, request, pk):
          """
          Gets flashcards for a section, excluding hidden cards.
          Returned in strictly chronological order (material date).
          """
54bba1fea   Andrew Buss   enforce enrollmen...
38
          flashcards = Flashcard.cards_visible_to(request.user).filter(section=self.get_object()).all()
c1f4d3dea   Andrew Buss   Fix section flash...
39
          return Response(FlashcardSerializer(flashcards, many=True).data)
57168cbc2   Rachel Lee   Stuff
40

ee4104aa2   Rohan Rangray   Added a Study vie...
41
      @detail_route(methods=['POST'])
72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
42
43
44
45
46
      def enroll(self, request, pk):
          """
          Add the current user to a specified section
          If the class has a whitelist, but the user is not on the whitelist, the request will fail.
          ---
2958a1827   Andrew Buss   cleaned up some d...
47
          view_mocker: flashcards.api.mock_no_params
72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
48
          """
b8dcac27e   Andrew Buss   friendlier error ...
49
50
51
52
53
54
          try:
              self.get_object().enroll(request.user)
          except django.core.exceptions.PermissionDenied as e:
              raise PermissionDenied(e)
          except django.core.exceptions.ValidationError as e:
              raise ValidationError(e)
72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
55
          return Response(status=HTTP_204_NO_CONTENT)
ee4104aa2   Rohan Rangray   Added a Study vie...
56
      @detail_route(methods=['POST'])
72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
57
58
59
60
61
      def drop(self, request, pk):
          """
          Remove the current user from a specified section
          If the user is not in the class, the request will fail.
          ---
2958a1827   Andrew Buss   cleaned up some d...
62
          view_mocker: flashcards.api.mock_no_params
72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
63
          """
54bba1fea   Andrew Buss   enforce enrollmen...
64
65
          try:
              self.get_object().drop(request.user)
2958a1827   Andrew Buss   cleaned up some d...
66
67
68
69
          except django.core.exceptions.PermissionDenied as e:
              raise PermissionDenied(e)
          except django.core.exceptions.ValidationError as e:
              raise ValidationError(e)
72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
70
          return Response(status=HTTP_204_NO_CONTENT)
491577131   Andrew Buss   Class -> section....
71

3188404a6   Andrew Buss   feed tests works(?)
72
      @list_route(methods=['GET'])
2c22131d9   Andrew Buss   Added department ...
73
      def search(self, request):
a2d8c4229   Andrew Buss   added feed
74
75
          """
          Returns a list of sections which match a user's query
2958a1827   Andrew Buss   cleaned up some d...
76
77
78
79
80
81
          ---
          parameters:
              - name: q
                description: space-separated list of terms
                required: true
                type: form
bd04c9af5   Andrew Buss   Only retrieve lec...
82
          response_serializer: SectionSerializer
a2d8c4229   Andrew Buss   added feed
83
          """
c66537ab3   Andrew Buss   nitpicky space fi...
84
          query = request.GET.get('q', None)
dc685f192   Andrew Buss   Section search wo...
85
          if not query: return Response('[]')
fe0d37016   Andrew Buss   changed some thin...
86
          qs = Section.search(query.split(' '))[:20]
33c5f1825   Andrew Buss   somewhat smarter ...
87
          data = SectionSerializer(qs, many=True).data
5a0ce27d1   Andrew Buss   cache rice
88
          return Response(data)
3709ee645   Laura Hawkins   working on deck view
89

a2d8c4229   Andrew Buss   added feed
90
      @detail_route(methods=['GET'])
6158afb2d   Laura Hawkins   my deckview is pe...
91
      def deck(self, request, pk):
3709ee645   Laura Hawkins   working on deck view
92
93
94
          """
          Gets the contents of a user's deck for a given section.
          """
fedcc8ded   Rohan Rangray   Wrote tests for F...
95
          qs = request.user.get_deck(self.get_object())
3709ee645   Laura Hawkins   working on deck view
96
97
          serializer = FlashcardSerializer(qs, many=True)
          return Response(serializer.data)
2c22131d9   Andrew Buss   Added department ...
98

ee4104aa2   Rohan Rangray   Added a Study vie...
99
      @detail_route(methods=['GET'], permission_classes=[IsAuthenticated])
2c52b3c5c   Chung Wang   ordered_deck unde...
100
101
102
103
104
      def ordered_deck(self, request, pk):
          """
          Get a chronological order by material_date of flashcards for a section.
          This excludes hidden card.
          """
41819dd7e   Rohan Rangray   Resolved merge co...
105
          qs = request.user.get_deck(self.get_object()).order_by('-material_date')
3709ee645   Laura Hawkins   working on deck view
106
107
          serializer = FlashcardSerializer(qs, many=True)
          return Response(serializer.data)
2c22131d9   Andrew Buss   Added department ...
108

a2d8c4229   Andrew Buss   added feed
109
110
111
112
113
114
115
116
      @detail_route(methods=['GET'])
      def feed(self, request, pk):
          """
          Gets the contents of a user's feed for a section.
          Exclude cards that are already in the user's deck
          """
          serializer = FlashcardSerializer(self.get_object().get_feed_for_user(request.user), many=True)
          return Response(serializer.data)
2a72f1a8a   Andrew Buss   Development serve...
117

72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
118
  class UserSectionListView(ListAPIView):
bd04c9af5   Andrew Buss   Only retrieve lec...
119
      serializer_class = DeepSectionSerializer
be7810aad   Laura Hawkins   working on adding...
120
      permission_classes = [IsAuthenticated]
2a72f1a8a   Andrew Buss   Development serve...
121

be7810aad   Laura Hawkins   working on adding...
122
123
      def get_queryset(self):
          return self.request.user.sections.all()
2a72f1a8a   Andrew Buss   Development serve...
124

be7810aad   Laura Hawkins   working on adding...
125
      def paginate_queryset(self, queryset): return None
18095ed46   Andrew Buss   Integrated django...
126

2a72f1a8a   Andrew Buss   Development serve...
127

72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
128
129
130
  class UserDetail(GenericAPIView):
      serializer_class = UserSerializer
      permission_classes = [IsAuthenticated]
ce17f969f   Andrew Buss   Restructured api,...
131
132
133
134
135
136
137
138
139
      def patch(self, request, format=None):
          """
          Updates the user's password, or verifies their email address
          ---
          request_serializer: UserUpdateSerializer
          response_serializer: UserSerializer
          """
          data = UserUpdateSerializer(data=request.data, context={'user': request.user})
          data.is_valid(raise_exception=True)
7aa4b42d3   Andrew Buss   Fixed registratio...
140
          data = data.validated_data
ce17f969f   Andrew Buss   Restructured api,...
141
142
143
144
  
          if 'new_password' in data:
              if not request.user.check_password(data['old_password']):
                  raise ValidationError('old_password is incorrect')
7aa4b42d3   Andrew Buss   Fixed registratio...
145
              request.user.set_password(data['new_password'])
ce17f969f   Andrew Buss   Restructured api,...
146
              request.user.save()
7aa4b42d3   Andrew Buss   Fixed registratio...
147
148
149
150
151
          if 'confirmation_key' in data:
              try:
                  request.user.confirm_email(data['confirmation_key'])
              except EmailAddress.DoesNotExist:
                  raise ValidationError('confirmation_key is invalid')
ce17f969f   Andrew Buss   Restructured api,...
152
153
154
155
156
157
158
159
160
  
          return Response(UserSerializer(request.user).data)
  
      def get(self, request, format=None):
          """
          Return data about the user
          ---
          response_serializer: UserSerializer
          """
72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
161
          serializer = UserSerializer(request.user, context={'request': request})
ce17f969f   Andrew Buss   Restructured api,...
162
          return Response(serializer.data)
ce17f969f   Andrew Buss   Restructured api,...
163
164
165
166
167
168
169
170
      def delete(self, request):
          """
          Irrevocably delete the user and their data
  
          Yes, really
          """
          request.user.delete()
          return Response(status=HTTP_204_NO_CONTENT)
2c22131d9   Andrew Buss   Added department ...
171

72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
172
173
174
175
176
177
178
179
180
181
  @api_view(['POST'])
  def register(request, format=None):
      """
      Register a new user
      ---
      request_serializer: EmailPasswordSerializer
      response_serializer: UserSerializer
      """
      data = RegistrationSerializer(data=request.data)
      data.is_valid(raise_exception=True)
ce17f969f   Andrew Buss   Restructured api,...
182

72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
183
184
185
      User.objects.create_user(**data.validated_data)
      user = authenticate(**data.validated_data)
      auth.login(request, user)
ce17f969f   Andrew Buss   Restructured api,...
186

72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
187
      return Response(UserSerializer(request.user).data, status=HTTP_201_CREATED)
2a72f1a8a   Andrew Buss   Development serve...
188

2c22131d9   Andrew Buss   Added department ...
189

72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
  @api_view(['POST'])
  def login(request):
      """
      Authenticates user and returns user data if valid.
      ---
      request_serializer: EmailPasswordSerializer
      response_serializer: UserSerializer
      """
  
      data = EmailPasswordSerializer(data=request.data)
      data.is_valid(raise_exception=True)
      user = authenticate(**data.validated_data)
  
      if user is None:
          raise AuthenticationFailed('Invalid email or password')
      if not user.is_active:
          raise NotAuthenticated('Account is disabled')
      auth.login(request, user)
      return Response(UserSerializer(request.user).data)
ce17f969f   Andrew Buss   Restructured api,...
209

72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
210
  @api_view(['POST'])
776577266   Andrew Buss   websockets notifi...
211
  @permission_classes((IsAuthenticated,))
72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
212
  def logout(request, format=None):
ce17f969f   Andrew Buss   Restructured api,...
213
      """
72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
214
      Logs the authenticated user out.
ce17f969f   Andrew Buss   Restructured api,...
215
      """
72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
216
217
      auth.logout(request)
      return Response(status=HTTP_204_NO_CONTENT)
ce17f969f   Andrew Buss   Restructured api,...
218

ce17f969f   Andrew Buss   Restructured api,...
219

72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
220
221
222
223
224
225
226
227
228
  @api_view(['POST'])
  def request_password_reset(request, format=None):
      """
      Send a password reset token/link to the provided email.
      ---
      request_serializer: PasswordResetRequestSerializer
      """
      data = PasswordResetRequestSerializer(data=request.data)
      data.is_valid(raise_exception=True)
8e66c8186   Andrew Buss   moved still more ...
229
      get_object_or_404(User, email=data['email'].value).request_password_reset()
72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
230
      return Response(status=HTTP_204_NO_CONTENT)
ce17f969f   Andrew Buss   Restructured api,...
231

2dc11d15d   Laura Hawkins   password and user...
232

72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
233
234
235
236
237
238
239
240
241
  @api_view(['POST'])
  def reset_password(request, format=None):
      """
      Updates user's password to new password if token is valid.
      ---
      request_serializer: PasswordResetSerializer
      """
      data = PasswordResetSerializer(data=request.data)
      data.is_valid(raise_exception=True)
2a9edd990   Chung Wang   Flashcard detail ...
242

72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
243
244
      user = User.objects.get(id=data['uid'].value)
      # Check token validity.
2a72f1a8a   Andrew Buss   Development serve...
245

72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
246
247
248
249
250
251
      if default_token_generator.check_token(user, data['token'].value):
          user.set_password(data['new_password'].value)
          user.save()
      else:
          raise ValidationError('Could not verify reset token')
      return Response(status=HTTP_204_NO_CONTENT)
2a72f1a8a   Andrew Buss   Development serve...
252

2a9edd990   Chung Wang   Flashcard detail ...
253

2958a1827   Andrew Buss   cleaned up some d...
254
  class FlashcardViewSet(GenericViewSet, CreateModelMixin, RetrieveModelMixin):
72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
255
256
      queryset = Flashcard.objects.all()
      serializer_class = FlashcardSerializer
54bba1fea   Andrew Buss   enforce enrollmen...
257
      permission_classes = [IsAuthenticated, IsEnrolledInAssociatedSection]
2a9edd990   Chung Wang   Flashcard detail ...
258

c2b6dc852   Rachel Lee   Wrote test for se...
259
260
      # Override create in CreateModelMixin
      def create(self, request, *args, **kwargs):
8964ffa26   Rohan Rangray   Corrected Flashca...
261
          serializer = FlashcardSerializer(data=request.data)
c2b6dc852   Rachel Lee   Wrote test for se...
262
          serializer.is_valid(raise_exception=True)
8964ffa26   Rohan Rangray   Corrected Flashca...
263
          data = serializer.validated_data
2958a1827   Andrew Buss   cleaned up some d...
264
265
          if not request.user.is_in_section(data['section']):
              raise PermissionDenied('The user is not enrolled in that section')
8964ffa26   Rohan Rangray   Corrected Flashca...
266
267
268
          data['author'] = request.user
          flashcard = Flashcard.objects.create(**data)
          self.perform_create(flashcard)
776577266   Andrew Buss   websockets notifi...
269
          notify_new_card(flashcard)
8964ffa26   Rohan Rangray   Corrected Flashca...
270
          headers = self.get_success_headers(data)
22a482a65   Andrew Buss   pull a card that ...
271
          request.user.pull(flashcard)
776577266   Andrew Buss   websockets notifi...
272
          response_data = FlashcardSerializer(flashcard).data
c66537ab3   Andrew Buss   nitpicky space fi...
273

776577266   Andrew Buss   websockets notifi...
274
          return Response(response_data, status=HTTP_201_CREATED, headers=headers)
c7885ab86   Chung Wang   hide flashcard an...
275

ee4104aa2   Rohan Rangray   Added a Study vie...
276
      @detail_route(methods=['POST'])
29c433096   Andrew Buss   updated readme to...
277
278
      def unhide(self, request, pk):
          """
2958a1827   Andrew Buss   cleaned up some d...
279
          Unhide the given card
29c433096   Andrew Buss   updated readme to...
280
          ---
2958a1827   Andrew Buss   cleaned up some d...
281
          view_mocker: flashcards.api.mock_no_params
29c433096   Andrew Buss   updated readme to...
282
          """
54bba1fea   Andrew Buss   enforce enrollmen...
283
          hide = get_object_or_404(FlashcardHide, user=request.user, flashcard=self.get_object())
29c433096   Andrew Buss   updated readme to...
284
          hide.delete()
c7885ab86   Chung Wang   hide flashcard an...
285
          return Response(status=HTTP_204_NO_CONTENT)
29c433096   Andrew Buss   updated readme to...
286

ee4104aa2   Rohan Rangray   Added a Study vie...
287
      @detail_route(methods=['POST'])
72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
288
289
      def report(self, request, pk):
          """
2958a1827   Andrew Buss   cleaned up some d...
290
          Hide the given card
72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
291
          ---
2958a1827   Andrew Buss   cleaned up some d...
292
          view_mocker: flashcards.api.mock_no_params
72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
293
          """
8e66c8186   Andrew Buss   moved still more ...
294
          self.get_object().report(request.user)
72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
295
          return Response(status=HTTP_204_NO_CONTENT)
fe6a4ff63   Rohan Rangray   Replaced Flashcar...
296

2958a1827   Andrew Buss   cleaned up some d...
297
      hide = report
a2d8c4229   Andrew Buss   added feed
298
      @detail_route(methods=['POST'])
be6cc9169   Rohan Rangray   Fixed a bug in th...
299
300
301
      def pull(self, request, pk):
          """
          Pull a card from the live feed into the user's deck.
2958a1827   Andrew Buss   cleaned up some d...
302
303
          ---
          view_mocker: flashcards.api.mock_no_params
be6cc9169   Rohan Rangray   Fixed a bug in th...
304
          """
7ac46127a   Laura Hawkins   working on notify...
305
306
  
          request.user.pull(self.get_object())
be6cc9169   Rohan Rangray   Fixed a bug in th...
307
          return Response(status=HTTP_204_NO_CONTENT)
bca16d61f   Rohan Rangray   Added the patch m...
308

17ecc595a   Laura Hawkins   trying to pull
309

b048e96a2   Chung Wang   unpull flashcard
310
311
312
313
      @detail_route(methods=['POST'])
      def unpull(self, request, pk):
          """
          Unpull a card from the user's deck
2958a1827   Andrew Buss   cleaned up some d...
314
315
          ---
          view_mocker: flashcards.api.mock_no_params
b048e96a2   Chung Wang   unpull flashcard
316
317
318
319
320
          """
          user = request.user
          flashcard = self.get_object()
          user.unpull(flashcard)
          return Response(status=HTTP_204_NO_CONTENT)
2958a1827   Andrew Buss   cleaned up some d...
321
      def partial_update(self, request, *args, **kwargs):
bca16d61f   Rohan Rangray   Added the patch m...
322
323
          """
          Edit settings related to a card for the user.
2958a1827   Andrew Buss   cleaned up some d...
324
325
          ---
          request_serializer: FlashcardUpdateSerializer
bca16d61f   Rohan Rangray   Added the patch m...
326
327
          """
          user = request.user
a2d8c4229   Andrew Buss   added feed
328
          flashcard = self.get_object()
bca16d61f   Rohan Rangray   Added the patch m...
329
330
331
          data = FlashcardUpdateSerializer(data=request.data)
          data.is_valid(raise_exception=True)
          new_flashcard = data.validated_data
cec534fd3   Andrew Buss   moved more view l...
332
          new_flashcard = flashcard.edit(user, new_flashcard)
ee4104aa2   Rohan Rangray   Added a Study vie...
333
          return Response(FlashcardSerializer(new_flashcard).data, status=HTTP_200_OK)
28a4bd2e7   Rohan Rangray   Refactored code i...
334

ee4104aa2   Rohan Rangray   Added a Study vie...
335
  class UserFlashcardQuizViewSet(GenericViewSet, CreateModelMixin, UpdateModelMixin):
ee4104aa2   Rohan Rangray   Added a Study vie...
336
      permission_classes = [IsAuthenticated, IsFlashcardReviewer]
f66f9ca7d   Rohan Rangray   Fixed SwaggerUI p...
337
338
339
340
341
342
      queryset = UserFlashcardQuiz.objects.all()
  
      def get_serializer_class(self):
          if self.request.method == 'POST':
              return QuizRequestSerializer
          return QuizAnswerRequestSerializer
ee4104aa2   Rohan Rangray   Added a Study vie...
343
344
345
346
347
348
349
  
      def create(self, request, *args, **kwargs):
          """
          Return a card based on the request params.
          :param request: A request object.
          :param format: Format of the request.
          :return: A response containing
f66f9ca7d   Rohan Rangray   Fixed SwaggerUI p...
350
351
          request_serializer: serializers.QuizRequestSerializer
          response_serializer: serializers.QuizResponseSerializer
ee4104aa2   Rohan Rangray   Added a Study vie...
352
          """
f66f9ca7d   Rohan Rangray   Fixed SwaggerUI p...
353
          serializer = QuizRequestSerializer(data=request.data)
ee4104aa2   Rohan Rangray   Added a Study vie...
354
          serializer.is_valid(raise_exception=True)
f66f9ca7d   Rohan Rangray   Fixed SwaggerUI p...
355
356
357
358
359
360
361
362
363
364
365
          data = serializer.validated_data
          user_flashcard_filter = UserFlashcard.objects.filter(
              user=request.user, flashcard__section__pk__in=data['sections'],
              flashcard__material_date__gte=data['material_date_begin'],
              flashcard__material_date__lte=data['material_date_end']
          )
  
          if not user_flashcard_filter.exists():
              raise ValidationError("No matching flashcard found in your decks")
  
          user_flashcard = user_flashcard_filter.order_by('?').first()
28a4bd2e7   Rohan Rangray   Refactored code i...
366
          mask = user_flashcard.get_mask().get_random_blank()
f66f9ca7d   Rohan Rangray   Fixed SwaggerUI p...
367
368
          user_flashcard_quiz = UserFlashcardQuiz(user_flashcard=user_flashcard,
                                                  blanked_word=user_flashcard.flashcard.text[slice(*mask)])
ee4104aa2   Rohan Rangray   Added a Study vie...
369
          user_flashcard_quiz.save()
f66f9ca7d   Rohan Rangray   Fixed SwaggerUI p...
370
371
          response = QuizResponseSerializer(instance=user_flashcard_quiz, mask=mask)
          return Response(response.data, status=HTTP_200_OK)
ee4104aa2   Rohan Rangray   Added a Study vie...
372

cf394ed25   Rohan Rangray   Changed /study/{p...
373
      def partial_update(self, request, *args, **kwargs):
ee4104aa2   Rohan Rangray   Added a Study vie...
374
375
376
377
378
          """
          Receive the user's response to the quiz.
          :param request: A request object.
          :param format: Format of the request.
          :return: A response containing
f66f9ca7d   Rohan Rangray   Fixed SwaggerUI p...
379
          request_serializer: serializers.QuizAnswerRequestSerializer
ee4104aa2   Rohan Rangray   Added a Study vie...
380
381
382
          """
          user_flashcard_quiz = self.get_object()
          serializer = QuizAnswerRequestSerializer(instance=user_flashcard_quiz, data=request.data)
7dcd94c01   Rohan Rangray   Added more extens...
383
          serializer.is_valid(raise_exception=True)
ee4104aa2   Rohan Rangray   Added a Study vie...
384
385
          serializer.update(user_flashcard_quiz, serializer.validated_data)
          return Response(status=HTTP_204_NO_CONTENT)