Commit 088cf219df1e1feb10ff2dbb3d2b6efb2e64915d

Authored by Rohan Rangray
1 parent 2f03453104
Exists in master

Update the userflashcard's score after reviewing it

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

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