Commit 1915d3c7a46cff045e4f767ab785e7f1834b91d3

Authored by Andrew Buss
1 parent 3f97f29f08
Exists in master

import an exception before we try to catch it

Showing 1 changed file with 1 additions and 1 deletions Inline Diff

flashcards/views.py View file @ 1915d3c
import django 1 1 import django
from django.contrib import auth 2 2 from django.contrib import auth
from django.db import IntegrityError 3 3 from django.db import IntegrityError
from django.shortcuts import get_object_or_404 4 4 from django.shortcuts import get_object_or_404
from django.utils.log import getLogger 5 5 from django.utils.log import getLogger
from flashcards.api import StandardResultsSetPagination, IsEnrolledInAssociatedSection, IsFlashcardReviewer, \ 6 6 from flashcards.api import StandardResultsSetPagination, IsEnrolledInAssociatedSection, IsFlashcardReviewer, \
IsAuthenticatedAndConfirmed 7 7 IsAuthenticatedAndConfirmed
from flashcards.models import Section, User, Flashcard, FlashcardHide, UserFlashcard, UserFlashcardQuiz, \ 8 8 from flashcards.models import Section, User, Flashcard, FlashcardHide, UserFlashcard, UserFlashcardQuiz, \
FlashcardAlreadyPulledException 9 9 FlashcardAlreadyPulledException, FlashcardNotInDeckException
from flashcards.notifications import notify_new_card 10 10 from flashcards.notifications import notify_new_card
from flashcards.serializers import SectionSerializer, UserUpdateSerializer, RegistrationSerializer, UserSerializer, \ 11 11 from flashcards.serializers import SectionSerializer, UserUpdateSerializer, RegistrationSerializer, UserSerializer, \
PasswordResetSerializer, PasswordResetRequestSerializer, EmailPasswordSerializer, FlashcardSerializer, \ 12 12 PasswordResetSerializer, PasswordResetRequestSerializer, EmailPasswordSerializer, FlashcardSerializer, \
FlashcardUpdateSerializer, QuizRequestSerializer, QuizResponseSerializer, \ 13 13 FlashcardUpdateSerializer, QuizRequestSerializer, QuizResponseSerializer, \
QuizAnswerRequestSerializer, DeepSectionSerializer 14 14 QuizAnswerRequestSerializer, DeepSectionSerializer
from rest_framework.decorators import detail_route, permission_classes, api_view, list_route 15 15 from rest_framework.decorators import detail_route, permission_classes, api_view, list_route
from rest_framework.generics import ListAPIView, GenericAPIView 16 16 from rest_framework.generics import ListAPIView, GenericAPIView
from rest_framework.mixins import CreateModelMixin, RetrieveModelMixin, UpdateModelMixin 17 17 from rest_framework.mixins import CreateModelMixin, RetrieveModelMixin, UpdateModelMixin
from rest_framework.permissions import IsAuthenticated 18 18 from rest_framework.permissions import IsAuthenticated
from rest_framework.viewsets import ReadOnlyModelViewSet, GenericViewSet 19 19 from rest_framework.viewsets import ReadOnlyModelViewSet, GenericViewSet
from django.core.mail import send_mail 20 20 from django.core.mail import send_mail
from django.contrib.auth import authenticate 21 21 from django.contrib.auth import authenticate
from django.contrib.auth.tokens import default_token_generator 22 22 from django.contrib.auth.tokens import default_token_generator
from rest_framework.status import HTTP_204_NO_CONTENT, HTTP_201_CREATED, HTTP_200_OK 23 23 from rest_framework.status import HTTP_204_NO_CONTENT, HTTP_201_CREATED, HTTP_200_OK
from rest_framework.response import Response 24 24 from rest_framework.response import Response
from rest_framework.exceptions import AuthenticationFailed, NotAuthenticated, ValidationError, PermissionDenied 25 25 from rest_framework.exceptions import AuthenticationFailed, NotAuthenticated, ValidationError, PermissionDenied
from simple_email_confirmation import EmailAddress 26 26 from simple_email_confirmation import EmailAddress
27 27
28 28
def log_event(request, event=''): 29 29 def log_event(request, event=''):
getLogger('flashy.events').info( 30 30 getLogger('flashy.events').info(
'%s %s %s %s' % (request.META['REMOTE_ADDR'], str(request.user), request.path, event)) 31 31 '%s %s %s %s' % (request.META['REMOTE_ADDR'], str(request.user), request.path, event))
32 32
33 33
class SectionViewSet(ReadOnlyModelViewSet): 34 34 class SectionViewSet(ReadOnlyModelViewSet):
queryset = Section.objects.all() 35 35 queryset = Section.objects.all()
serializer_class = DeepSectionSerializer 36 36 serializer_class = DeepSectionSerializer
pagination_class = StandardResultsSetPagination 37 37 pagination_class = StandardResultsSetPagination
permission_classes = [IsAuthenticatedAndConfirmed] 38 38 permission_classes = [IsAuthenticatedAndConfirmed]
39 39
@detail_route(methods=['GET']) 40 40 @detail_route(methods=['GET'])
def flashcards(self, request, pk): 41 41 def flashcards(self, request, pk):
""" 42 42 """
Gets flashcards for a section, excluding hidden cards. 43 43 Gets flashcards for a section, excluding hidden cards.
Returned in strictly chronological order (material date). 44 44 Returned in strictly chronological order (material date).
""" 45 45 """
flashcards = Flashcard.cards_visible_to(request.user) 46 46 flashcards = Flashcard.cards_visible_to(request.user)
if 'hidden' in request.GET: 47 47 if 'hidden' in request.GET:
if request.GET['hidden'] == 'only': 48 48 if request.GET['hidden'] == 'only':
flashcards = Flashcard.cards_hidden_by(request.user) 49 49 flashcards = Flashcard.cards_hidden_by(request.user)
else: 50 50 else:
flashcards |= Flashcard.cards_hidden_by(request.user) 51 51 flashcards |= Flashcard.cards_hidden_by(request.user)
flashcards = flashcards.filter(section=self.get_object()).order_by('material_date').all() 52 52 flashcards = flashcards.filter(section=self.get_object()).order_by('material_date').all()
log_event(request, str(self.get_object())) 53 53 log_event(request, str(self.get_object()))
return Response(FlashcardSerializer(flashcards, context={"user": request.user}, many=True).data) 54 54 return Response(FlashcardSerializer(flashcards, context={"user": request.user}, many=True).data)
55 55
@detail_route(methods=['POST']) 56 56 @detail_route(methods=['POST'])
def enroll(self, request, pk): 57 57 def enroll(self, request, pk):
58 58
""" 59 59 """
Add the current user to a specified section 60 60 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. 61 61 If the class has a whitelist, but the user is not on the whitelist, the request will fail.
--- 62 62 ---
view_mocker: flashcards.api.mock_no_params 63 63 view_mocker: flashcards.api.mock_no_params
""" 64 64 """
try: 65 65 try:
self.get_object().enroll(request.user) 66 66 self.get_object().enroll(request.user)
log_event(request, str(self.get_object())) 67 67 log_event(request, str(self.get_object()))
except django.core.exceptions.PermissionDenied as e: 68 68 except django.core.exceptions.PermissionDenied as e:
raise PermissionDenied(e) 69 69 raise PermissionDenied(e)
except django.core.exceptions.ValidationError as e: 70 70 except django.core.exceptions.ValidationError as e:
raise ValidationError(e) 71 71 raise ValidationError(e)
return Response(status=HTTP_204_NO_CONTENT) 72 72 return Response(status=HTTP_204_NO_CONTENT)
73 73
@detail_route(methods=['POST']) 74 74 @detail_route(methods=['POST'])
def drop(self, request, pk): 75 75 def drop(self, request, pk):
""" 76 76 """
Remove the current user from a specified section 77 77 Remove the current user from a specified section
If the user is not in the class, the request will fail. 78 78 If the user is not in the class, the request will fail.
--- 79 79 ---
view_mocker: flashcards.api.mock_no_params 80 80 view_mocker: flashcards.api.mock_no_params
""" 81 81 """
try: 82 82 try:
self.get_object().drop(request.user) 83 83 self.get_object().drop(request.user)
log_event(request, str(self.get_object())) 84 84 log_event(request, str(self.get_object()))
except django.core.exceptions.PermissionDenied as e: 85 85 except django.core.exceptions.PermissionDenied as e:
raise PermissionDenied(e) 86 86 raise PermissionDenied(e)
except django.core.exceptions.ValidationError as e: 87 87 except django.core.exceptions.ValidationError as e:
raise ValidationError(e) 88 88 raise ValidationError(e)
return Response(status=HTTP_204_NO_CONTENT) 89 89 return Response(status=HTTP_204_NO_CONTENT)
90 90
@list_route(methods=['GET']) 91 91 @list_route(methods=['GET'])
def search(self, request): 92 92 def search(self, request):
""" 93 93 """
Returns a list of sections which match a user's query 94 94 Returns a list of sections which match a user's query
--- 95 95 ---
parameters: 96 96 parameters:
- name: q 97 97 - name: q
description: space-separated list of terms 98 98 description: space-separated list of terms
required: true 99 99 required: true
type: form 100 100 type: form
response_serializer: SectionSerializer 101 101 response_serializer: SectionSerializer
""" 102 102 """
query = request.GET.get('q', None) 103 103 query = request.GET.get('q', None)
if not query: return Response('[]') 104 104 if not query: return Response('[]')
qs = Section.search(query.split(' '))[:20] 105 105 qs = Section.search(query.split(' '))[:20]
data = SectionSerializer(qs, many=True).data 106 106 data = SectionSerializer(qs, many=True).data
log_event(request, query) 107 107 log_event(request, query)
return Response(data) 108 108 return Response(data)
109 109
@detail_route(methods=['GET']) 110 110 @detail_route(methods=['GET'])
def deck(self, request, pk): 111 111 def deck(self, request, pk):
""" 112 112 """
Gets the contents of a user's deck for a given section. 113 113 Gets the contents of a user's deck for a given section.
""" 114 114 """
qs = request.user.get_deck(self.get_object()) 115 115 qs = request.user.get_deck(self.get_object())
serializer = FlashcardSerializer(qs, many=True) 116 116 serializer = FlashcardSerializer(qs, many=True)
log_event(request, str(self.get_object())) 117 117 log_event(request, str(self.get_object()))
return Response(serializer.data) 118 118 return Response(serializer.data)
119 119
@detail_route(methods=['GET']) 120 120 @detail_route(methods=['GET'])
def feed(self, request, pk): 121 121 def feed(self, request, pk):
""" 122 122 """
Gets the contents of a user's feed for a section. 123 123 Gets the contents of a user's feed for a section.
Exclude cards that are already in the user's deck 124 124 Exclude cards that are already in the user's deck
""" 125 125 """
serializer = FlashcardSerializer(self.get_object().get_feed_for_user(request.user), many=True, 126 126 serializer = FlashcardSerializer(self.get_object().get_feed_for_user(request.user), many=True,
context={'user': request.user}) 127 127 context={'user': request.user})
log_event(request, str(self.get_object())) 128 128 log_event(request, str(self.get_object()))
return Response(serializer.data) 129 129 return Response(serializer.data)
130 130
131 131
class UserSectionListView(ListAPIView): 132 132 class UserSectionListView(ListAPIView):
serializer_class = DeepSectionSerializer 133 133 serializer_class = DeepSectionSerializer
permission_classes = [IsAuthenticatedAndConfirmed] 134 134 permission_classes = [IsAuthenticatedAndConfirmed]
135 135
def get_queryset(self): 136 136 def get_queryset(self):
return self.request.user.sections.all() 137 137 return self.request.user.sections.all()
138 138
def paginate_queryset(self, queryset): return None 139 139 def paginate_queryset(self, queryset): return None
140 140
141 141
class UserDetail(GenericAPIView): 142 142 class UserDetail(GenericAPIView):
serializer_class = UserSerializer 143 143 serializer_class = UserSerializer
permission_classes = [IsAuthenticatedAndConfirmed] 144 144 permission_classes = [IsAuthenticatedAndConfirmed]
145 145
def patch(self, request, format=None): 146 146 def patch(self, request, format=None):
""" 147 147 """
Updates the user's password, or verifies their email address 148 148 Updates the user's password, or verifies their email address
--- 149 149 ---
request_serializer: UserUpdateSerializer 150 150 request_serializer: UserUpdateSerializer
response_serializer: UserSerializer 151 151 response_serializer: UserSerializer
""" 152 152 """
data = UserUpdateSerializer(data=request.data, context={'user': request.user}) 153 153 data = UserUpdateSerializer(data=request.data, context={'user': request.user})
data.is_valid(raise_exception=True) 154 154 data.is_valid(raise_exception=True)
data = data.validated_data 155 155 data = data.validated_data
156 156
if 'new_password' in data: 157 157 if 'new_password' in data:
if not request.user.check_password(data['old_password']): 158 158 if not request.user.check_password(data['old_password']):
raise ValidationError('old_password is incorrect') 159 159 raise ValidationError('old_password is incorrect')
request.user.set_password(data['new_password']) 160 160 request.user.set_password(data['new_password'])
request.user.save() 161 161 request.user.save()
log_event(request, 'change password') 162 162 log_event(request, 'change password')
163 163
if 'confirmation_key' in data: 164 164 if 'confirmation_key' in data:
try: 165 165 try:
request.user.confirm_email(data['confirmation_key']) 166 166 request.user.confirm_email(data['confirmation_key'])
log_event(request, 'confirm email') 167 167 log_event(request, 'confirm email')
except EmailAddress.DoesNotExist: 168 168 except EmailAddress.DoesNotExist:
raise ValidationError('confirmation_key is invalid') 169 169 raise ValidationError('confirmation_key is invalid')
170 170
return Response(UserSerializer(request.user).data) 171 171 return Response(UserSerializer(request.user).data)
172 172
def get(self, request, format=None): 173 173 def get(self, request, format=None):
""" 174 174 """
Return data about the user 175 175 Return data about the user
--- 176 176 ---
response_serializer: UserSerializer 177 177 response_serializer: UserSerializer
""" 178 178 """
serializer = UserSerializer(request.user, context={'request': request}) 179 179 serializer = UserSerializer(request.user, context={'request': request})
return Response(serializer.data) 180 180 return Response(serializer.data)
181 181
def delete(self, request): 182 182 def delete(self, request):
""" 183 183 """
Irrevocably delete the user and their data 184 184 Irrevocably delete the user and their data
185 185
Yes, really 186 186 Yes, really
""" 187 187 """
request.user.delete() 188 188 request.user.delete()
log_event(request) 189 189 log_event(request)
return Response(status=HTTP_204_NO_CONTENT) 190 190 return Response(status=HTTP_204_NO_CONTENT)
191 191
192 192
@api_view(['POST']) 193 193 @api_view(['POST'])
def register(request, format=None): 194 194 def register(request, format=None):
""" 195 195 """
Register a new user 196 196 Register a new user
--- 197 197 ---
request_serializer: EmailPasswordSerializer 198 198 request_serializer: EmailPasswordSerializer
response_serializer: UserSerializer 199 199 response_serializer: UserSerializer
""" 200 200 """
data = RegistrationSerializer(data=request.data) 201 201 data = RegistrationSerializer(data=request.data)
data.is_valid(raise_exception=True) 202 202 data.is_valid(raise_exception=True)
203 203
User.objects.create_user(**data.validated_data) 204 204 User.objects.create_user(**data.validated_data)
user = authenticate(**data.validated_data) 205 205 user = authenticate(**data.validated_data)
auth.login(request, user) 206 206 auth.login(request, user)
log_event(request) 207 207 log_event(request)
return Response(UserSerializer(request.user).data, status=HTTP_201_CREATED) 208 208 return Response(UserSerializer(request.user).data, status=HTTP_201_CREATED)
209 209
210 210
@api_view(['POST']) 211 211 @api_view(['POST'])
def login(request): 212 212 def login(request):
""" 213 213 """
Authenticates user and returns user data if valid. 214 214 Authenticates user and returns user data if valid.
--- 215 215 ---
request_serializer: EmailPasswordSerializer 216 216 request_serializer: EmailPasswordSerializer
response_serializer: UserSerializer 217 217 response_serializer: UserSerializer
""" 218 218 """
219 219
data = EmailPasswordSerializer(data=request.data) 220 220 data = EmailPasswordSerializer(data=request.data)
data.is_valid(raise_exception=True) 221 221 data.is_valid(raise_exception=True)
user = authenticate(**data.validated_data) 222 222 user = authenticate(**data.validated_data)
223 223
if user is None: 224 224 if user is None:
raise AuthenticationFailed('Invalid email or password') 225 225 raise AuthenticationFailed('Invalid email or password')
if not user.is_active: 226 226 if not user.is_active:
raise NotAuthenticated('Account is disabled') 227 227 raise NotAuthenticated('Account is disabled')
auth.login(request, user) 228 228 auth.login(request, user)
log_event(request) 229 229 log_event(request)
return Response(UserSerializer(request.user).data) 230 230 return Response(UserSerializer(request.user).data)
231 231
232 232
@api_view(['POST']) 233 233 @api_view(['POST'])
@permission_classes((IsAuthenticated,)) 234 234 @permission_classes((IsAuthenticated,))
def logout(request, format=None): 235 235 def logout(request, format=None):
""" 236 236 """
Logs the authenticated user out. 237 237 Logs the authenticated user out.
""" 238 238 """
auth.logout(request) 239 239 auth.logout(request)
log_event(request) 240 240 log_event(request)
return Response(status=HTTP_204_NO_CONTENT) 241 241 return Response(status=HTTP_204_NO_CONTENT)
242 242
243 243
@api_view(['POST']) 244 244 @api_view(['POST'])
def request_password_reset(request, format=None): 245 245 def request_password_reset(request, format=None):
""" 246 246 """
Send a password reset token/link to the provided email. 247 247 Send a password reset token/link to the provided email.
--- 248 248 ---
request_serializer: PasswordResetRequestSerializer 249 249 request_serializer: PasswordResetRequestSerializer
""" 250 250 """
data = PasswordResetRequestSerializer(data=request.data) 251 251 data = PasswordResetRequestSerializer(data=request.data)
data.is_valid(raise_exception=True) 252 252 data.is_valid(raise_exception=True)
log_event(request, 'email: ' + str(data['email'])) 253 253 log_event(request, 'email: ' + str(data['email']))
get_object_or_404(User, email=data['email'].value).request_password_reset() 254 254 get_object_or_404(User, email=data['email'].value).request_password_reset()
return Response(status=HTTP_204_NO_CONTENT) 255 255 return Response(status=HTTP_204_NO_CONTENT)
256 256
257 257
@api_view(['POST']) 258 258 @api_view(['POST'])
def reset_password(request, format=None): 259 259 def reset_password(request, format=None):
""" 260 260 """
Updates user's password to new password if token is valid. 261 261 Updates user's password to new password if token is valid.
--- 262 262 ---
request_serializer: PasswordResetSerializer 263 263 request_serializer: PasswordResetSerializer
""" 264 264 """
data = PasswordResetSerializer(data=request.data) 265 265 data = PasswordResetSerializer(data=request.data)
data.is_valid(raise_exception=True) 266 266 data.is_valid(raise_exception=True)
267 267
user = User.objects.get(id=data['uid'].value) 268 268 user = User.objects.get(id=data['uid'].value)
# Check token validity. 269 269 # Check token validity.
270 270
if default_token_generator.check_token(user, data['token'].value): 271 271 if default_token_generator.check_token(user, data['token'].value):
user.set_password(data['new_password'].value) 272 272 user.set_password(data['new_password'].value)
user.save() 273 273 user.save()
log_event(request) 274 274 log_event(request)
else: 275 275 else:
raise ValidationError('Could not verify reset token') 276 276 raise ValidationError('Could not verify reset token')
return Response(status=HTTP_204_NO_CONTENT) 277 277 return Response(status=HTTP_204_NO_CONTENT)
278 278
279 279
280 280
class FlashcardViewSet(GenericViewSet, CreateModelMixin, RetrieveModelMixin): 281 281 class FlashcardViewSet(GenericViewSet, CreateModelMixin, RetrieveModelMixin):
queryset = Flashcard.objects.all() 282 282 queryset = Flashcard.objects.all()
serializer_class = FlashcardSerializer 283 283 serializer_class = FlashcardSerializer
permission_classes = [IsAuthenticatedAndConfirmed, IsEnrolledInAssociatedSection] 284 284 permission_classes = [IsAuthenticatedAndConfirmed, IsEnrolledInAssociatedSection]
# Override create in CreateModelMixin 285 285 # Override create in CreateModelMixin
def create(self, request, *args, **kwargs): 286 286 def create(self, request, *args, **kwargs):
serializer = FlashcardSerializer(data=request.data) 287 287 serializer = FlashcardSerializer(data=request.data)
serializer.is_valid(raise_exception=True) 288 288 serializer.is_valid(raise_exception=True)
data = serializer.validated_data 289 289 data = serializer.validated_data
if not request.user.is_in_section(data['section']): 290 290 if not request.user.is_in_section(data['section']):
raise PermissionDenied('The user is not enrolled in that section') 291 291 raise PermissionDenied('The user is not enrolled in that section')
data['author'] = request.user 292 292 data['author'] = request.user
flashcard = Flashcard.objects.create(**data) 293 293 flashcard = Flashcard.objects.create(**data)
self.perform_create(flashcard) 294 294 self.perform_create(flashcard)
notify_new_card(flashcard) 295 295 notify_new_card(flashcard)
headers = self.get_success_headers(data) 296 296 headers = self.get_success_headers(data)
request.user.pull(flashcard) 297 297 request.user.pull(flashcard)
response_data = FlashcardSerializer(flashcard).data 298 298 response_data = FlashcardSerializer(flashcard).data
log_event(request, response_data) 299 299 log_event(request, response_data)
return Response(response_data, status=HTTP_201_CREATED, headers=headers) 300 300 return Response(response_data, status=HTTP_201_CREATED, headers=headers)
301 301
@detail_route(methods=['POST']) 302 302 @detail_route(methods=['POST'])
def unhide(self, request, pk): 303 303 def unhide(self, request, pk):
""" 304 304 """
Unhide the given card 305 305 Unhide the given card
--- 306 306 ---
view_mocker: flashcards.api.mock_no_params 307 307 view_mocker: flashcards.api.mock_no_params
""" 308 308 """
hide = get_object_or_404(FlashcardHide, user=request.user, flashcard=self.get_object()) 309 309 hide = get_object_or_404(FlashcardHide, user=request.user, flashcard=self.get_object())
hide.delete() 310 310 hide.delete()
log_event(request, str(self.get_object())) 311 311 log_event(request, str(self.get_object()))
return Response(status=HTTP_204_NO_CONTENT) 312 312 return Response(status=HTTP_204_NO_CONTENT)
313 313
@detail_route(methods=['POST']) 314 314 @detail_route(methods=['POST'])
def report(self, request, pk): 315 315 def report(self, request, pk):
""" 316 316 """
Hide the given card 317 317 Hide the given card
--- 318 318 ---
view_mocker: flashcards.api.mock_no_params 319 319 view_mocker: flashcards.api.mock_no_params
""" 320 320 """
self.get_object().report(request.user) 321 321 self.get_object().report(request.user)
log_event(request, str(self.get_object())) 322 322 log_event(request, str(self.get_object()))
return Response(status=HTTP_204_NO_CONTENT) 323 323 return Response(status=HTTP_204_NO_CONTENT)
324 324
hide = report 325 325 hide = report
326 326
@detail_route(methods=['POST']) 327 327 @detail_route(methods=['POST'])
def pull(self, request, pk): 328 328 def pull(self, request, pk):
""" 329 329 """
Pull a card from the live feed into the user's deck. 330 330 Pull a card from the live feed into the user's deck.
--- 331 331 ---
view_mocker: flashcards.api.mock_no_params 332 332 view_mocker: flashcards.api.mock_no_params
""" 333 333 """
try: 334 334 try:
request.user.pull(self.get_object()) 335 335 request.user.pull(self.get_object())
log_event(request, str(self.get_object())) 336 336 log_event(request, str(self.get_object()))
return Response(status=HTTP_204_NO_CONTENT) 337 337 return Response(status=HTTP_204_NO_CONTENT)
except FlashcardAlreadyPulledException: 338 338 except FlashcardAlreadyPulledException:
raise ValidationError('Cannot pull a card already in deck') 339 339 raise ValidationError('Cannot pull a card already in deck')
340 340
@detail_route(methods=['POST']) 341 341 @detail_route(methods=['POST'])
def unpull(self, request, pk): 342 342 def unpull(self, request, pk):
""" 343 343 """
Unpull a card from the user's deck 344 344 Unpull a card from the user's deck
--- 345 345 ---
view_mocker: flashcards.api.mock_no_params 346 346 view_mocker: flashcards.api.mock_no_params
""" 347 347 """
user = request.user 348 348 user = request.user
flashcard = self.get_object() 349 349 flashcard = self.get_object()
try: 350 350 try:
user.unpull(flashcard) 351 351 user.unpull(flashcard)
log_event(request, str(self.get_object())) 352 352 log_event(request, str(self.get_object()))
return Response(status=HTTP_204_NO_CONTENT) 353 353 return Response(status=HTTP_204_NO_CONTENT)
except FlashcardNotInDeckException: 354 354 except FlashcardNotInDeckException:
raise ValidationError('Cannot unpull a card not in deck') 355 355 raise ValidationError('Cannot unpull a card not in deck')
356 356
357 357
def partial_update(self, request, *args, **kwargs): 358 358 def partial_update(self, request, *args, **kwargs):
""" 359 359 """
Edit settings related to a card for the user. 360 360 Edit settings related to a card for the user.
--- 361 361 ---
request_serializer: FlashcardUpdateSerializer 362 362 request_serializer: FlashcardUpdateSerializer
""" 363 363 """
user = request.user 364 364 user = request.user
flashcard = self.get_object() 365 365 flashcard = self.get_object()
data = FlashcardUpdateSerializer(data=request.data) 366 366 data = FlashcardUpdateSerializer(data=request.data)
data.is_valid(raise_exception=True) 367 367 data.is_valid(raise_exception=True)
new_flashcard = data.validated_data 368 368 new_flashcard = data.validated_data
new_flashcard = flashcard.edit(user, new_flashcard) 369 369 new_flashcard = flashcard.edit(user, new_flashcard)
log_event(request, str(new_flashcard)) 370 370 log_event(request, str(new_flashcard))
return Response(FlashcardSerializer(new_flashcard, context={'user': request.user}).data, status=HTTP_200_OK) 371 371 return Response(FlashcardSerializer(new_flashcard, context={'user': request.user}).data, status=HTTP_200_OK)
372 372
373 373
class UserFlashcardQuizViewSet(GenericViewSet, CreateModelMixin, UpdateModelMixin): 374 374 class UserFlashcardQuizViewSet(GenericViewSet, CreateModelMixin, UpdateModelMixin):
permission_classes = [IsAuthenticatedAndConfirmed, IsFlashcardReviewer] 375 375 permission_classes = [IsAuthenticatedAndConfirmed, IsFlashcardReviewer]
queryset = UserFlashcardQuiz.objects.all() 376 376 queryset = UserFlashcardQuiz.objects.all()
377 377
def get_serializer_class(self): 378 378 def get_serializer_class(self):
if self.request.method == 'POST': 379 379 if self.request.method == 'POST':
return QuizRequestSerializer 380 380 return QuizRequestSerializer