Blame view

flashcards/views.py 16.4 KB
776577266   Andrew Buss   websockets notifi...
1
  import django
72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
2
  from django.contrib import auth
29c433096   Andrew Buss   updated readme to...
3
  from django.shortcuts import get_object_or_404
5a2899b9d   Andrew Buss   add gobs of logging
4
  from django.utils.log import getLogger
33f8a47a8   Andrew Buss   enforce 24 hour g...
5
6
  from flashcards.api import StandardResultsSetPagination, IsEnrolledInAssociatedSection, IsFlashcardReviewer, \
      IsAuthenticatedAndConfirmed
1eebdbcc4   Andrew Buss   merge
7
8
  from flashcards.models import Section, User, Flashcard, FlashcardHide, UserFlashcardQuiz, \
      FlashcardAlreadyPulledException, FlashcardNotInDeckException
148b7e62d   Rohan Rangray   Attempted fix for...
9
  from flashcards.notifications import notify_new_card, notify_pull
ce17f969f   Andrew Buss   Restructured api,...
10
  from flashcards.serializers import SectionSerializer, UserUpdateSerializer, RegistrationSerializer, UserSerializer, \
bca16d61f   Rohan Rangray   Added the patch m...
11
      PasswordResetSerializer, PasswordResetRequestSerializer, EmailPasswordSerializer, FlashcardSerializer, \
d818aecbc   Rohan Rangray   Merged.
12
      FlashcardUpdateSerializer, QuizRequestSerializer, QuizResponseSerializer, \
1890b5a76   Rohan Rangray   Resolved conflict
13
      QuizAnswerRequestSerializer, DeepSectionSerializer, EmailVerificationSerializer, FeedRequestSerializer
3188404a6   Andrew Buss   feed tests works(?)
14
  from rest_framework.decorators import detail_route, permission_classes, api_view, list_route
2c22131d9   Andrew Buss   Added department ...
15
  from rest_framework.generics import ListAPIView, GenericAPIView
ee4104aa2   Rohan Rangray   Added a Study vie...
16
  from rest_framework.mixins import CreateModelMixin, RetrieveModelMixin, UpdateModelMixin
01ea09616   Andrew Buss   Don't be silly, y...
17
  from rest_framework.permissions import IsAuthenticated
72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
18
  from rest_framework.viewsets import ReadOnlyModelViewSet, GenericViewSet
ce17f969f   Andrew Buss   Restructured api,...
19
  from django.core.mail import send_mail
72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
20
  from django.contrib.auth import authenticate
ce17f969f   Andrew Buss   Restructured api,...
21
  from django.contrib.auth.tokens import default_token_generator
5d861cbfb   Rohan Rangray   Wrote tests for F...
22
  from rest_framework.status import HTTP_204_NO_CONTENT, HTTP_201_CREATED, HTTP_200_OK
ce17f969f   Andrew Buss   Restructured api,...
23
  from rest_framework.response import Response
72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
24
  from rest_framework.exceptions import AuthenticationFailed, NotAuthenticated, ValidationError, PermissionDenied
7aa4b42d3   Andrew Buss   Fixed registratio...
25
  from simple_email_confirmation import EmailAddress
2a72f1a8a   Andrew Buss   Development serve...
26

c4a8e6cef   Rohan Rangray   Added pagination ...
27

5a2899b9d   Andrew Buss   add gobs of logging
28
  def log_event(request, event=''):
179c42337   Andrew Buss   unicode sux; use ...
29
30
      logstr = u'%s %s %s %s' % (request.META['REMOTE_ADDR'], request.user, request.path, event)
      getLogger('flashy.events').info(logstr)
2a72f1a8a   Andrew Buss   Development serve...
31

c4a8e6cef   Rohan Rangray   Added pagination ...
32

ce17f969f   Andrew Buss   Restructured api,...
33
  class SectionViewSet(ReadOnlyModelViewSet):
491577131   Andrew Buss   Class -> section....
34
      queryset = Section.objects.all()
bd04c9af5   Andrew Buss   Only retrieve lec...
35
      serializer_class = DeepSectionSerializer
c4a8e6cef   Rohan Rangray   Added pagination ...
36
      pagination_class = StandardResultsSetPagination
33f8a47a8   Andrew Buss   enforce 24 hour g...
37
      permission_classes = [IsAuthenticatedAndConfirmed]
72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
38

a2d8c4229   Andrew Buss   added feed
39
      @detail_route(methods=['GET'])
57168cbc2   Rachel Lee   Stuff
40
41
42
43
44
      def flashcards(self, request, pk):
          """
          Gets flashcards for a section, excluding hidden cards.
          Returned in strictly chronological order (material date).
          """
ad724d791   Andrew Buss   is_hidden specifi...
45
          flashcards = Flashcard.cards_visible_to(request.user)
fe44f1608   Andrew Buss   remove ordered_de...
46
          if 'hidden' in request.GET:
d370a0e68   Andrew Buss   fix get/GET in fl...
47
              if request.GET['hidden'] == 'only':
ad724d791   Andrew Buss   is_hidden specifi...
48
49
50
                  flashcards = Flashcard.cards_hidden_by(request.user)
              else:
                  flashcards |= Flashcard.cards_hidden_by(request.user)
cf248fe50   Andrew Buss   sort flashcard li...
51
          flashcards = flashcards.filter(section=self.get_object()).order_by('material_date').all()
5a2899b9d   Andrew Buss   add gobs of logging
52
          log_event(request, str(self.get_object()))
ad724d791   Andrew Buss   is_hidden specifi...
53
          return Response(FlashcardSerializer(flashcards, context={"user": request.user}, many=True).data)
57168cbc2   Rachel Lee   Stuff
54

ee4104aa2   Rohan Rangray   Added a Study vie...
55
      @detail_route(methods=['POST'])
72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
56
      def enroll(self, request, pk):
5a2899b9d   Andrew Buss   add gobs of logging
57

72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
58
59
60
61
          """
          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...
62
          view_mocker: flashcards.api.mock_no_params
72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
63
          """
b8dcac27e   Andrew Buss   friendlier error ...
64
65
          try:
              self.get_object().enroll(request.user)
5a2899b9d   Andrew Buss   add gobs of logging
66
              log_event(request, str(self.get_object()))
b8dcac27e   Andrew Buss   friendlier error ...
67
68
69
70
          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...
71
          return Response(status=HTTP_204_NO_CONTENT)
ee4104aa2   Rohan Rangray   Added a Study vie...
72
      @detail_route(methods=['POST'])
72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
73
74
75
76
77
      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...
78
          view_mocker: flashcards.api.mock_no_params
72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
79
          """
54bba1fea   Andrew Buss   enforce enrollmen...
80
81
          try:
              self.get_object().drop(request.user)
5a2899b9d   Andrew Buss   add gobs of logging
82
              log_event(request, str(self.get_object()))
2958a1827   Andrew Buss   cleaned up some d...
83
84
85
86
          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...
87
          return Response(status=HTTP_204_NO_CONTENT)
491577131   Andrew Buss   Class -> section....
88

3188404a6   Andrew Buss   feed tests works(?)
89
      @list_route(methods=['GET'])
2c22131d9   Andrew Buss   Added department ...
90
      def search(self, request):
a2d8c4229   Andrew Buss   added feed
91
92
          """
          Returns a list of sections which match a user's query
2958a1827   Andrew Buss   cleaned up some d...
93
94
95
96
97
98
          ---
          parameters:
              - name: q
                description: space-separated list of terms
                required: true
                type: form
bd04c9af5   Andrew Buss   Only retrieve lec...
99
          response_serializer: SectionSerializer
a2d8c4229   Andrew Buss   added feed
100
          """
c66537ab3   Andrew Buss   nitpicky space fi...
101
          query = request.GET.get('q', None)
dc685f192   Andrew Buss   Section search wo...
102
          if not query: return Response('[]')
fe0d37016   Andrew Buss   changed some thin...
103
          qs = Section.search(query.split(' '))[:20]
4ff4160a6   Andrew Buss   include can_enrol...
104
          data = SectionSerializer(qs, many=True, context={'user': request.user}).data
5a2899b9d   Andrew Buss   add gobs of logging
105
          log_event(request, query)
5a0ce27d1   Andrew Buss   cache rice
106
          return Response(data)
3709ee645   Laura Hawkins   working on deck view
107

a2d8c4229   Andrew Buss   added feed
108
      @detail_route(methods=['GET'])
6158afb2d   Laura Hawkins   my deckview is pe...
109
      def deck(self, request, pk):
3709ee645   Laura Hawkins   working on deck view
110
111
112
          """
          Gets the contents of a user's deck for a given section.
          """
fedcc8ded   Rohan Rangray   Wrote tests for F...
113
          qs = request.user.get_deck(self.get_object())
3709ee645   Laura Hawkins   working on deck view
114
          serializer = FlashcardSerializer(qs, many=True)
5a2899b9d   Andrew Buss   add gobs of logging
115
          log_event(request, str(self.get_object()))
3709ee645   Laura Hawkins   working on deck view
116
          return Response(serializer.data)
2c22131d9   Andrew Buss   Added department ...
117

a2d8c4229   Andrew Buss   added feed
118
119
120
121
122
      @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
901ee2fa9   Rohan Rangray   Added pagination ...
123
124
          request_serializer: FeedRequestSerializer
          response_serializer: FlashcardSerializer
a2d8c4229   Andrew Buss   added feed
125
          """
901ee2fa9   Rohan Rangray   Added pagination ...
126
127
128
129
130
          feed_serializer = FeedRequestSerializer(data=request.data)
          feed_serializer.is_valid(raise_exception=True)
          page = feed_serializer.validated_data['page']
          serializer = FlashcardSerializer(self.get_object().get_feed_for_user(request.user, page=page),
                                           many=True, context={'user': request.user})
5a2899b9d   Andrew Buss   add gobs of logging
131
          log_event(request, str(self.get_object()))
a2d8c4229   Andrew Buss   added feed
132
          return Response(serializer.data)
2a72f1a8a   Andrew Buss   Development serve...
133

72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
134
  class UserSectionListView(ListAPIView):
bd04c9af5   Andrew Buss   Only retrieve lec...
135
      serializer_class = DeepSectionSerializer
33f8a47a8   Andrew Buss   enforce 24 hour g...
136
      permission_classes = [IsAuthenticatedAndConfirmed]
2a72f1a8a   Andrew Buss   Development serve...
137

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

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

2a72f1a8a   Andrew Buss   Development serve...
143

72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
144
145
  class UserDetail(GenericAPIView):
      serializer_class = UserSerializer
2684ad8b4   Andrew Buss   restructure email...
146
      permission_classes = [IsAuthenticated]
72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
147

ce17f969f   Andrew Buss   Restructured api,...
148
149
      def patch(self, request, format=None):
          """
fe546f43f   Andrew Buss   move verify_email...
150
          Updates the user's password
ce17f969f   Andrew Buss   Restructured api,...
151
152
153
154
155
156
          ---
          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...
157
          data = data.validated_data
ce17f969f   Andrew Buss   Restructured api,...
158
159
160
161
  
          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...
162
              request.user.set_password(data['new_password'])
ce17f969f   Andrew Buss   Restructured api,...
163
              request.user.save()
5a2899b9d   Andrew Buss   add gobs of logging
164
              log_event(request, 'change password')
ce17f969f   Andrew Buss   Restructured api,...
165

ce17f969f   Andrew Buss   Restructured api,...
166
167
168
169
170
171
172
173
          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...
174
          serializer = UserSerializer(request.user, context={'request': request})
ce17f969f   Andrew Buss   Restructured api,...
175
          return Response(serializer.data)
ce17f969f   Andrew Buss   Restructured api,...
176
177
178
179
180
181
182
      def delete(self, request):
          """
          Irrevocably delete the user and their data
  
          Yes, really
          """
          request.user.delete()
5a2899b9d   Andrew Buss   add gobs of logging
183
          log_event(request)
ce17f969f   Andrew Buss   Restructured api,...
184
          return Response(status=HTTP_204_NO_CONTENT)
2c22131d9   Andrew Buss   Added department ...
185

72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
186
  @api_view(['POST'])
7a17fac38   Andrew Buss   add view to resen...
187
188
  @permission_classes([IsAuthenticated])
  def resend_confirmation_email(request):
fe546f43f   Andrew Buss   move verify_email...
189
      "Resends a confirmation email to a user"
7a17fac38   Andrew Buss   add view to resen...
190
191
      request.user.send_confirmation_email()
      return Response(status=HTTP_204_NO_CONTENT)
fe546f43f   Andrew Buss   move verify_email...
192
193
194
195
196
197
198
199
200
  
  @api_view(['POST'])
  @permission_classes([IsAuthenticated])
  def verify_email(request):
      """
      Accepts a user's email confirmation_key to verify their email address
      ---
      request_serializer: EmailVerificationSerializer
      """
2684ad8b4   Andrew Buss   restructure email...
201
202
203
  
      data = EmailVerificationSerializer(data=request.data)
      data.is_valid(raise_exception=True)
fe546f43f   Andrew Buss   move verify_email...
204
      try:
e28ff3999   Andrew Buss   unbreak email ver...
205
          email = User.confirm_email(data.validated_data['confirmation_key'])
fe546f43f   Andrew Buss   move verify_email...
206
207
      except EmailAddress.DoesNotExist:
          raise ValidationError('confirmation_key is invalid')
2684ad8b4   Andrew Buss   restructure email...
208
209
      log_event(request, 'confirm email' + str(email))
      return Response(status=HTTP_204_NO_CONTENT)
fe546f43f   Andrew Buss   move verify_email...
210

72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
211
212
213
214
215
216
217
218
219
220
  @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,...
221

72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
222
223
224
      User.objects.create_user(**data.validated_data)
      user = authenticate(**data.validated_data)
      auth.login(request, user)
5a2899b9d   Andrew Buss   add gobs of logging
225
      log_event(request)
72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
226
      return Response(UserSerializer(request.user).data, status=HTTP_201_CREATED)
2a72f1a8a   Andrew Buss   Development serve...
227

2c22131d9   Andrew Buss   Added department ...
228

72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
  @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)
5a2899b9d   Andrew Buss   add gobs of logging
247
      log_event(request)
72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
248
      return Response(UserSerializer(request.user).data)
ce17f969f   Andrew Buss   Restructured api,...
249

72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
250
  @api_view(['POST'])
776577266   Andrew Buss   websockets notifi...
251
  @permission_classes((IsAuthenticated,))
72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
252
  def logout(request, format=None):
ce17f969f   Andrew Buss   Restructured api,...
253
      """
72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
254
      Logs the authenticated user out.
ce17f969f   Andrew Buss   Restructured api,...
255
      """
72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
256
      auth.logout(request)
5a2899b9d   Andrew Buss   add gobs of logging
257
      log_event(request)
72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
258
      return Response(status=HTTP_204_NO_CONTENT)
ce17f969f   Andrew Buss   Restructured api,...
259

ce17f969f   Andrew Buss   Restructured api,...
260

72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
261
262
263
264
265
266
267
268
269
  @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)
5a2899b9d   Andrew Buss   add gobs of logging
270
      log_event(request, 'email: ' + str(data['email']))
8e66c8186   Andrew Buss   moved still more ...
271
      get_object_or_404(User, email=data['email'].value).request_password_reset()
72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
272
      return Response(status=HTTP_204_NO_CONTENT)
ce17f969f   Andrew Buss   Restructured api,...
273

2dc11d15d   Laura Hawkins   password and user...
274

72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
275
276
277
278
279
280
281
282
283
  @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 ...
284

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

72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
288
289
290
      if default_token_generator.check_token(user, data['token'].value):
          user.set_password(data['new_password'].value)
          user.save()
5a2899b9d   Andrew Buss   add gobs of logging
291
          log_event(request)
72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
292
293
294
      else:
          raise ValidationError('Could not verify reset token')
      return Response(status=HTTP_204_NO_CONTENT)
2a72f1a8a   Andrew Buss   Development serve...
295

2a9edd990   Chung Wang   Flashcard detail ...
296

2958a1827   Andrew Buss   cleaned up some d...
297
  class FlashcardViewSet(GenericViewSet, CreateModelMixin, RetrieveModelMixin):
72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
298
299
      queryset = Flashcard.objects.all()
      serializer_class = FlashcardSerializer
33f8a47a8   Andrew Buss   enforce 24 hour g...
300
      permission_classes = [IsAuthenticatedAndConfirmed, IsEnrolledInAssociatedSection]
c95e5d254   Rohan Rangray   Silently allow fo...
301

c2b6dc852   Rachel Lee   Wrote test for se...
302
303
      # Override create in CreateModelMixin
      def create(self, request, *args, **kwargs):
8964ffa26   Rohan Rangray   Corrected Flashca...
304
          serializer = FlashcardSerializer(data=request.data)
c2b6dc852   Rachel Lee   Wrote test for se...
305
          serializer.is_valid(raise_exception=True)
8964ffa26   Rohan Rangray   Corrected Flashca...
306
          data = serializer.validated_data
2958a1827   Andrew Buss   cleaned up some d...
307
308
          if not request.user.is_in_section(data['section']):
              raise PermissionDenied('The user is not enrolled in that section')
8964ffa26   Rohan Rangray   Corrected Flashca...
309
310
311
          data['author'] = request.user
          flashcard = Flashcard.objects.create(**data)
          self.perform_create(flashcard)
776577266   Andrew Buss   websockets notifi...
312
          notify_new_card(flashcard)
8964ffa26   Rohan Rangray   Corrected Flashca...
313
          headers = self.get_success_headers(data)
22a482a65   Andrew Buss   pull a card that ...
314
          request.user.pull(flashcard)
776577266   Andrew Buss   websockets notifi...
315
          response_data = FlashcardSerializer(flashcard).data
5a2899b9d   Andrew Buss   add gobs of logging
316
          log_event(request, response_data)
776577266   Andrew Buss   websockets notifi...
317
          return Response(response_data, status=HTTP_201_CREATED, headers=headers)
c7885ab86   Chung Wang   hide flashcard an...
318

ee4104aa2   Rohan Rangray   Added a Study vie...
319
      @detail_route(methods=['POST'])
29c433096   Andrew Buss   updated readme to...
320
321
      def unhide(self, request, pk):
          """
2958a1827   Andrew Buss   cleaned up some d...
322
          Unhide the given card
29c433096   Andrew Buss   updated readme to...
323
          ---
2958a1827   Andrew Buss   cleaned up some d...
324
          view_mocker: flashcards.api.mock_no_params
29c433096   Andrew Buss   updated readme to...
325
          """
54bba1fea   Andrew Buss   enforce enrollmen...
326
          hide = get_object_or_404(FlashcardHide, user=request.user, flashcard=self.get_object())
29c433096   Andrew Buss   updated readme to...
327
          hide.delete()
5a2899b9d   Andrew Buss   add gobs of logging
328
          log_event(request, str(self.get_object()))
c7885ab86   Chung Wang   hide flashcard an...
329
          return Response(status=HTTP_204_NO_CONTENT)
29c433096   Andrew Buss   updated readme to...
330

ee4104aa2   Rohan Rangray   Added a Study vie...
331
      @detail_route(methods=['POST'])
72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
332
333
      def report(self, request, pk):
          """
2958a1827   Andrew Buss   cleaned up some d...
334
          Hide the given card
72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
335
          ---
2958a1827   Andrew Buss   cleaned up some d...
336
          view_mocker: flashcards.api.mock_no_params
72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
337
          """
8e66c8186   Andrew Buss   moved still more ...
338
          self.get_object().report(request.user)
5a2899b9d   Andrew Buss   add gobs of logging
339
          log_event(request, str(self.get_object()))
72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
340
          return Response(status=HTTP_204_NO_CONTENT)
fe6a4ff63   Rohan Rangray   Replaced Flashcar...
341

2958a1827   Andrew Buss   cleaned up some d...
342
      hide = report
a2d8c4229   Andrew Buss   added feed
343
      @detail_route(methods=['POST'])
be6cc9169   Rohan Rangray   Fixed a bug in th...
344
345
346
      def pull(self, request, pk):
          """
          Pull a card from the live feed into the user's deck.
2958a1827   Andrew Buss   cleaned up some d...
347
348
          ---
          view_mocker: flashcards.api.mock_no_params
be6cc9169   Rohan Rangray   Fixed a bug in th...
349
          """
390189eb6   Andrew Buss   handle pulling a ...
350
351
          try:
              request.user.pull(self.get_object())
179c42337   Andrew Buss   unicode sux; use ...
352
              log_event(request, self.get_object())
390189eb6   Andrew Buss   handle pulling a ...
353
              return Response(status=HTTP_204_NO_CONTENT)
3f97f29f0   Andrew Buss   improve error han...
354
          except FlashcardAlreadyPulledException:
390189eb6   Andrew Buss   handle pulling a ...
355
              raise ValidationError('Cannot pull a card already in deck')
17ecc595a   Laura Hawkins   trying to pull
356

b048e96a2   Chung Wang   unpull flashcard
357
358
359
360
      @detail_route(methods=['POST'])
      def unpull(self, request, pk):
          """
          Unpull a card from the user's deck
2958a1827   Andrew Buss   cleaned up some d...
361
362
          ---
          view_mocker: flashcards.api.mock_no_params
b048e96a2   Chung Wang   unpull flashcard
363
364
365
          """
          user = request.user
          flashcard = self.get_object()
3f97f29f0   Andrew Buss   improve error han...
366
367
          try:
              user.unpull(flashcard)
179c42337   Andrew Buss   unicode sux; use ...
368
              log_event(request, self.get_object())
3f97f29f0   Andrew Buss   improve error han...
369
370
371
              return Response(status=HTTP_204_NO_CONTENT)
          except FlashcardNotInDeckException:
              raise ValidationError('Cannot unpull a card not in deck')
2958a1827   Andrew Buss   cleaned up some d...
372
      def partial_update(self, request, *args, **kwargs):
bca16d61f   Rohan Rangray   Added the patch m...
373
374
          """
          Edit settings related to a card for the user.
2958a1827   Andrew Buss   cleaned up some d...
375
376
          ---
          request_serializer: FlashcardUpdateSerializer
bca16d61f   Rohan Rangray   Added the patch m...
377
378
          """
          user = request.user
a2d8c4229   Andrew Buss   added feed
379
          flashcard = self.get_object()
bca16d61f   Rohan Rangray   Added the patch m...
380
381
382
          data = FlashcardUpdateSerializer(data=request.data)
          data.is_valid(raise_exception=True)
          new_flashcard = data.validated_data
cec534fd3   Andrew Buss   moved more view l...
383
          new_flashcard = flashcard.edit(user, new_flashcard)
5a2899b9d   Andrew Buss   add gobs of logging
384
          log_event(request, str(new_flashcard))
ad724d791   Andrew Buss   is_hidden specifi...
385
          return Response(FlashcardSerializer(new_flashcard, context={'user': request.user}).data, status=HTTP_200_OK)
ee4104aa2   Rohan Rangray   Added a Study vie...
386

28a4bd2e7   Rohan Rangray   Refactored code i...
387

ee4104aa2   Rohan Rangray   Added a Study vie...
388
  class UserFlashcardQuizViewSet(GenericViewSet, CreateModelMixin, UpdateModelMixin):
33f8a47a8   Andrew Buss   enforce 24 hour g...
389
      permission_classes = [IsAuthenticatedAndConfirmed, IsFlashcardReviewer]
f66f9ca7d   Rohan Rangray   Fixed SwaggerUI p...
390
391
392
393
394
395
      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...
396
397
398
399
400
401
402
  
      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...
403
404
          request_serializer: serializers.QuizRequestSerializer
          response_serializer: serializers.QuizResponseSerializer
ee4104aa2   Rohan Rangray   Added a Study vie...
405
          """
f66f9ca7d   Rohan Rangray   Fixed SwaggerUI p...
406
          serializer = QuizRequestSerializer(data=request.data)
ee4104aa2   Rohan Rangray   Added a Study vie...
407
          serializer.is_valid(raise_exception=True)
f66f9ca7d   Rohan Rangray   Fixed SwaggerUI p...
408
          data = serializer.validated_data
2c00c7d07   Rohan Rangray   Refactored soem c...
409
          user_flashcard = request.user.by_retention(**data).first()
1eebdbcc4   Andrew Buss   merge
410

28a4bd2e7   Rohan Rangray   Refactored code i...
411
          mask = user_flashcard.get_mask().get_random_blank()
2c00c7d07   Rohan Rangray   Refactored soem c...
412
413
          blanked_word = ""
          if mask:
c95e5d254   Rohan Rangray   Silently allow fo...
414
              blanked_word = user_flashcard.flashcard.text[slice(*mask)]
2c00c7d07   Rohan Rangray   Refactored soem c...
415
416
  
          user_flashcard_quiz = UserFlashcardQuiz(user_flashcard=user_flashcard, blanked_word=blanked_word)
ee4104aa2   Rohan Rangray   Added a Study vie...
417
          user_flashcard_quiz.save()
2c00c7d07   Rohan Rangray   Refactored soem c...
418

f66f9ca7d   Rohan Rangray   Fixed SwaggerUI p...
419
          response = QuizResponseSerializer(instance=user_flashcard_quiz, mask=mask)
a9cb7ae51   Andrew Buss   log serializer da...
420
          log_event(request, response.data)
f66f9ca7d   Rohan Rangray   Fixed SwaggerUI p...
421
          return Response(response.data, status=HTTP_200_OK)
ee4104aa2   Rohan Rangray   Added a Study vie...
422

cf394ed25   Rohan Rangray   Changed /study/{p...
423
      def partial_update(self, request, *args, **kwargs):
ee4104aa2   Rohan Rangray   Added a Study vie...
424
425
426
427
428
          """
          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...
429
          request_serializer: serializers.QuizAnswerRequestSerializer
ee4104aa2   Rohan Rangray   Added a Study vie...
430
431
432
          """
          user_flashcard_quiz = self.get_object()
          serializer = QuizAnswerRequestSerializer(instance=user_flashcard_quiz, data=request.data)
7dcd94c01   Rohan Rangray   Added more extens...
433
          serializer.is_valid(raise_exception=True)
ee4104aa2   Rohan Rangray   Added a Study vie...
434
          serializer.update(user_flashcard_quiz, serializer.validated_data)
088cf219d   Rohan Rangray   Update the userfl...
435
          user_flashcard_quiz.user_flashcard.review(user_flashcard_quiz)
5a2899b9d   Andrew Buss   add gobs of logging
436
          log_event(request, serializer.data)
ee4104aa2   Rohan Rangray   Added a Study vie...
437
          return Response(status=HTTP_204_NO_CONTENT)