Commit 81749b02fe633a30ac2da395eb58cad775385f77

Authored by Andrew Buss
1 parent 8a4e3756dd
Exists in master

unicode for logging edit flashcard

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

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