Commit 97581679d0ba670b3244408cc9c4062482314bd2

Authored by Rohan Rangray
1 parent f441be6101
Exists in master

Fix the server error message upon login

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

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