Commit fa34dcd5f150dabfb932ba5f6c407425ca47c1f2

Authored by Rachel Lee
1 parent 1acf45a73e
Exists in master

fixed merge thing

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

flashcards/views.py View file @ fa34dcd
from django.contrib import auth 1 1 from django.contrib import auth
from flashcards.api import StandardResultsSetPagination 2 2 from flashcards.api import StandardResultsSetPagination
from flashcards.models import Section, User, Flashcard, FlashcardHide, UserFlashcard 3 3 from flashcards.models import Section, User, Flashcard, FlashcardHide, UserFlashcard
from flashcards.serializers import SectionSerializer, UserUpdateSerializer, RegistrationSerializer, UserSerializer, \ 4 4 from flashcards.serializers import SectionSerializer, UserUpdateSerializer, RegistrationSerializer, UserSerializer, \
PasswordResetSerializer, PasswordResetRequestSerializer, EmailPasswordSerializer, FlashcardSerializer, \ 5 5 PasswordResetSerializer, PasswordResetRequestSerializer, EmailPasswordSerializer, FlashcardSerializer, \
FlashcardUpdateSerializer 6 6 FlashcardUpdateSerializer
from rest_framework.decorators import detail_route, permission_classes, api_view, list_route 7 7 from rest_framework.decorators import detail_route, permission_classes, api_view, list_route
from rest_framework.generics import ListAPIView, GenericAPIView 8 8 from rest_framework.generics import ListAPIView, GenericAPIView
from rest_framework.mixins import CreateModelMixin, RetrieveModelMixin 9 9 from rest_framework.mixins import CreateModelMixin, RetrieveModelMixin
from rest_framework.permissions import IsAuthenticated 10 10 from rest_framework.permissions import IsAuthenticated
from rest_framework.viewsets import ReadOnlyModelViewSet, GenericViewSet 11 11 from rest_framework.viewsets import ReadOnlyModelViewSet, GenericViewSet
from django.core.mail import send_mail 12 12 from django.core.mail import send_mail
from django.contrib.auth import authenticate 13 13 from django.contrib.auth import authenticate
from django.contrib.auth.tokens import default_token_generator 14 14 from django.contrib.auth.tokens import default_token_generator
from rest_framework.status import HTTP_204_NO_CONTENT, HTTP_201_CREATED 15 15 from rest_framework.status import HTTP_204_NO_CONTENT, HTTP_201_CREATED
from rest_framework.response import Response 16 16 from rest_framework.response import Response
from rest_framework.exceptions import AuthenticationFailed, NotAuthenticated, ValidationError, PermissionDenied 17 17 from rest_framework.exceptions import AuthenticationFailed, NotAuthenticated, ValidationError, PermissionDenied
from simple_email_confirmation import EmailAddress 18 18 from simple_email_confirmation import EmailAddress
19 19
20 20
class SectionViewSet(ReadOnlyModelViewSet): 21 21 class SectionViewSet(ReadOnlyModelViewSet):
queryset = Section.objects.all() 22 22 queryset = Section.objects.all()
serializer_class = SectionSerializer 23 23 serializer_class = SectionSerializer
pagination_class = StandardResultsSetPagination 24 24 pagination_class = StandardResultsSetPagination
permission_classes = [IsAuthenticated] 25 25 permission_classes = [IsAuthenticated]
26 26
@detail_route(methods=['get'], permission_classes=[IsAuthenticated]) 27 27 @detail_route(methods=['get'], permission_classes=[IsAuthenticated])
def flashcards(self, request, pk): 28 28 def flashcards(self, request, pk):
""" 29 29 """
Gets flashcards for a section, excluding hidden cards. 30 30 Gets flashcards for a section, excluding hidden cards.
Returned in strictly chronological order (material date). 31 31 Returned in strictly chronological order (material date).
""" 32 32 """
<<<<<<< HEAD 33
flashcards = Flashcard.cards_visible_to(request.user).filter( \ 34 33 flashcards = Flashcard.cards_visible_to(request.user).filter( \
section=self.get_object()).all() 35 34 section=self.get_object()).all()
36
======= 37
flashcards = Flashcard.cards_visible_to(request.user).filter(section=self.get_object()) 38
>>>>>>> 41819dd7ec2f22a04db83459c33e31c2b7d16d99 39
return Response(FlashcardSerializer(flashcards, many=True).data) 40 35 return Response(FlashcardSerializer(flashcards, many=True).data)
41 36
@detail_route(methods=['post'], permission_classes=[IsAuthenticated]) 42 37 @detail_route(methods=['post'], permission_classes=[IsAuthenticated])
def enroll(self, request, pk): 43 38 def enroll(self, request, pk):
""" 44 39 """
Add the current user to a specified section 45 40 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. 46 41 If the class has a whitelist, but the user is not on the whitelist, the request will fail.
--- 47 42 ---
omit_serializer: true 48 43 omit_serializer: true
parameters: 49 44 parameters:
- fake: None 50 45 - fake: None
parameters_strategy: 51 46 parameters_strategy:
form: replace 52 47 form: replace
""" 53 48 """
section = self.get_object() 54 49 section = self.get_object()
if request.user.sections.filter(pk=section.pk).exists(): 55 50 if request.user.sections.filter(pk=section.pk).exists():
raise ValidationError("You are already in this section.") 56 51 raise ValidationError("You are already in this section.")
if section.is_whitelisted and not section.is_user_on_whitelist(request.user): 57 52 if section.is_whitelisted and not section.is_user_on_whitelist(request.user):
raise PermissionDenied("You must be on the whitelist to add this section.") 58 53 raise PermissionDenied("You must be on the whitelist to add this section.")
request.user.sections.add(section) 59 54 request.user.sections.add(section)
return Response(status=HTTP_204_NO_CONTENT) 60 55 return Response(status=HTTP_204_NO_CONTENT)
61 56
@detail_route(methods=['post'], permission_classes=[IsAuthenticated]) 62 57 @detail_route(methods=['post'], permission_classes=[IsAuthenticated])
def drop(self, request, pk): 63 58 def drop(self, request, pk):
""" 64 59 """
Remove the current user from a specified section 65 60 Remove the current user from a specified section
If the user is not in the class, the request will fail. 66 61 If the user is not in the class, the request will fail.
--- 67 62 ---
omit_serializer: true 68 63 omit_serializer: true
parameters: 69 64 parameters:
- fake: None 70 65 - fake: None
parameters_strategy: 71 66 parameters_strategy:
form: replace 72 67 form: replace
""" 73 68 """
section = self.get_object() 74 69 section = self.get_object()
if not section.user_set.filter(pk=request.user.pk).exists(): 75 70 if not section.user_set.filter(pk=request.user.pk).exists():
raise ValidationError("You are not in the section.") 76 71 raise ValidationError("You are not in the section.")
section.user_set.remove(request.user) 77 72 section.user_set.remove(request.user)
return Response(status=HTTP_204_NO_CONTENT) 78 73 return Response(status=HTTP_204_NO_CONTENT)
79 74
@list_route(methods=['get'], permission_classes=[IsAuthenticated]) 80 75 @list_route(methods=['get'], permission_classes=[IsAuthenticated])
def search(self, request): 81 76 def search(self, request):
query = request.GET.get('q', None) 82 77 query = request.GET.get('q', None)
if not query: return Response('[]') 83 78 if not query: return Response('[]')
qs = Section.search(query.split(' '))[:20] 84 79 qs = Section.search(query.split(' '))[:20]
serializer = SectionSerializer(qs, many=True) 85 80 serializer = SectionSerializer(qs, many=True)
return Response(serializer.data) 86 81 return Response(serializer.data)
87 82
@detail_route(methods=['get'], permission_classes=[IsAuthenticated]) 88 83 @detail_route(methods=['get'], permission_classes=[IsAuthenticated])
def deck(self, request, pk): 89 84 def deck(self, request, pk):
""" 90 85 """
Gets the contents of a user's deck for a given section. 91 86 Gets the contents of a user's deck for a given section.
""" 92 87 """
qs = request.user.get_deck(self.get_object()) 93 88 qs = request.user.get_deck(self.get_object())
serializer = FlashcardSerializer(qs, many=True) 94 89 serializer = FlashcardSerializer(qs, many=True)
return Response(serializer.data) 95 90 return Response(serializer.data)
96 91
@detail_route(methods=['get'], permission_classes=[IsAuthenticated]) 97 92 @detail_route(methods=['get'], permission_classes=[IsAuthenticated])
def ordered_deck(self, request, pk): 98 93 def ordered_deck(self, request, pk):
""" 99 94 """
Get a chronological order by material_date of flashcards for a section. 100 95 Get a chronological order by material_date of flashcards for a section.
This excludes hidden card. 101 96 This excludes hidden card.
""" 102 97 """
qs = request.user.get_deck(self.get_object()).order_by('-material_date') 103 98 qs = request.user.get_deck(self.get_object()).order_by('-material_date')
serializer = FlashcardSerializer(qs, many=True) 104 99 serializer = FlashcardSerializer(qs, many=True)
return Response(serializer.data) 105 100 return Response(serializer.data)
106 101
107 102
class UserSectionListView(ListAPIView): 108 103 class UserSectionListView(ListAPIView):
serializer_class = SectionSerializer 109 104 serializer_class = SectionSerializer
permission_classes = [IsAuthenticated] 110 105 permission_classes = [IsAuthenticated]
111 106
def get_queryset(self): 112 107 def get_queryset(self):
return self.request.user.sections.all() 113 108 return self.request.user.sections.all()
114 109
def paginate_queryset(self, queryset): return None 115 110 def paginate_queryset(self, queryset): return None
116 111
117 112
class UserDetail(GenericAPIView): 118 113 class UserDetail(GenericAPIView):
serializer_class = UserSerializer 119 114 serializer_class = UserSerializer
permission_classes = [IsAuthenticated] 120 115 permission_classes = [IsAuthenticated]
121 116
def get_queryset(self): 122 117 def get_queryset(self):
return User.objects.all() 123 118 return User.objects.all()
124 119
def patch(self, request, format=None): 125 120 def patch(self, request, format=None):
""" 126 121 """
Updates the user's password, or verifies their email address 127 122 Updates the user's password, or verifies their email address
--- 128 123 ---
request_serializer: UserUpdateSerializer 129 124 request_serializer: UserUpdateSerializer
response_serializer: UserSerializer 130 125 response_serializer: UserSerializer
""" 131 126 """
data = UserUpdateSerializer(data=request.data, context={'user': request.user}) 132 127 data = UserUpdateSerializer(data=request.data, context={'user': request.user})
data.is_valid(raise_exception=True) 133 128 data.is_valid(raise_exception=True)
data = data.validated_data 134 129 data = data.validated_data
135 130
if 'new_password' in data: 136 131 if 'new_password' in data:
if not request.user.check_password(data['old_password']): 137 132 if not request.user.check_password(data['old_password']):
raise ValidationError('old_password is incorrect') 138 133 raise ValidationError('old_password is incorrect')
request.user.set_password(data['new_password']) 139 134 request.user.set_password(data['new_password'])
request.user.save() 140 135 request.user.save()
141 136
if 'confirmation_key' in data: 142 137 if 'confirmation_key' in data:
try: 143 138 try:
request.user.confirm_email(data['confirmation_key']) 144 139 request.user.confirm_email(data['confirmation_key'])
except EmailAddress.DoesNotExist: 145 140 except EmailAddress.DoesNotExist:
raise ValidationError('confirmation_key is invalid') 146 141 raise ValidationError('confirmation_key is invalid')
147 142
return Response(UserSerializer(request.user).data) 148 143 return Response(UserSerializer(request.user).data)
149 144
def get(self, request, format=None): 150 145 def get(self, request, format=None):
""" 151 146 """
Return data about the user 152 147 Return data about the user
--- 153 148 ---
response_serializer: UserSerializer 154 149 response_serializer: UserSerializer
""" 155 150 """
serializer = UserSerializer(request.user, context={'request': request}) 156 151 serializer = UserSerializer(request.user, context={'request': request})
return Response(serializer.data) 157 152 return Response(serializer.data)
158 153
def delete(self, request): 159 154 def delete(self, request):
""" 160 155 """
Irrevocably delete the user and their data 161 156 Irrevocably delete the user and their data
162 157
Yes, really 163 158 Yes, really
""" 164 159 """
request.user.delete() 165 160 request.user.delete()
return Response(status=HTTP_204_NO_CONTENT) 166 161 return Response(status=HTTP_204_NO_CONTENT)
167 162
168 163
@api_view(['POST']) 169 164 @api_view(['POST'])
def register(request, format=None): 170 165 def register(request, format=None):
""" 171 166 """
Register a new user 172 167 Register a new user
--- 173 168 ---
request_serializer: EmailPasswordSerializer 174 169 request_serializer: EmailPasswordSerializer
response_serializer: UserSerializer 175 170 response_serializer: UserSerializer
""" 176 171 """
data = RegistrationSerializer(data=request.data) 177 172 data = RegistrationSerializer(data=request.data)
data.is_valid(raise_exception=True) 178 173 data.is_valid(raise_exception=True)
179 174
User.objects.create_user(**data.validated_data) 180 175 User.objects.create_user(**data.validated_data)
user = authenticate(**data.validated_data) 181 176 user = authenticate(**data.validated_data)
auth.login(request, user) 182 177 auth.login(request, user)
183 178
body = ''' 184 179 body = '''
Visit the following link to confirm your email address: 185 180 Visit the following link to confirm your email address:
https://flashy.cards/app/verify_email/%s 186 181 https://flashy.cards/app/verify_email/%s
187 182
If you did not register for Flashy, no action is required. 188 183 If you did not register for Flashy, no action is required.
''' 189 184 '''
190 185
assert send_mail("Flashy email verification", 191 186 assert send_mail("Flashy email verification",
body % user.confirmation_key, 192 187 body % user.confirmation_key,
"noreply@flashy.cards", 193 188 "noreply@flashy.cards",
[user.email]) 194 189 [user.email])
195 190
return Response(UserSerializer(request.user).data, status=HTTP_201_CREATED) 196 191 return Response(UserSerializer(request.user).data, status=HTTP_201_CREATED)
197 192
198 193
@api_view(['POST']) 199 194 @api_view(['POST'])
def login(request): 200 195 def login(request):
""" 201 196 """
Authenticates user and returns user data if valid. 202 197 Authenticates user and returns user data if valid.
--- 203 198 ---
request_serializer: EmailPasswordSerializer 204 199 request_serializer: EmailPasswordSerializer
response_serializer: UserSerializer 205 200 response_serializer: UserSerializer
""" 206 201 """
207 202
data = EmailPasswordSerializer(data=request.data) 208 203 data = EmailPasswordSerializer(data=request.data)
data.is_valid(raise_exception=True) 209 204 data.is_valid(raise_exception=True)
user = authenticate(**data.validated_data) 210 205 user = authenticate(**data.validated_data)
211 206
if user is None: 212 207 if user is None:
raise AuthenticationFailed('Invalid email or password') 213 208 raise AuthenticationFailed('Invalid email or password')
if not user.is_active: 214 209 if not user.is_active:
raise NotAuthenticated('Account is disabled') 215 210 raise NotAuthenticated('Account is disabled')
auth.login(request, user) 216 211 auth.login(request, user)
return Response(UserSerializer(request.user).data) 217 212 return Response(UserSerializer(request.user).data)
218 213
219 214
@api_view(['POST']) 220 215 @api_view(['POST'])
@permission_classes((IsAuthenticated, )) 221 216 @permission_classes((IsAuthenticated, ))
def logout(request, format=None): 222 217 def logout(request, format=None):
""" 223 218 """
Logs the authenticated user out. 224 219 Logs the authenticated user out.
""" 225 220 """
auth.logout(request) 226 221 auth.logout(request)
return Response(status=HTTP_204_NO_CONTENT) 227 222 return Response(status=HTTP_204_NO_CONTENT)
228 223
229 224
@api_view(['POST']) 230 225 @api_view(['POST'])
def request_password_reset(request, format=None): 231 226 def request_password_reset(request, format=None):
""" 232 227 """
Send a password reset token/link to the provided email. 233 228 Send a password reset token/link to the provided email.
--- 234 229 ---
request_serializer: PasswordResetRequestSerializer 235 230 request_serializer: PasswordResetRequestSerializer
""" 236 231 """
data = PasswordResetRequestSerializer(data=request.data) 237 232 data = PasswordResetRequestSerializer(data=request.data)
data.is_valid(raise_exception=True) 238 233 data.is_valid(raise_exception=True)
user = User.objects.get(email=data['email'].value) 239 234 user = User.objects.get(email=data['email'].value)
token = default_token_generator.make_token(user) 240 235 token = default_token_generator.make_token(user)
241 236
body = ''' 242 237 body = '''
Visit the following link to reset your password: 243 238 Visit the following link to reset your password:
https://flashy.cards/app/reset_password/%d/%s 244 239 https://flashy.cards/app/reset_password/%d/%s
245 240
If you did not request a password reset, no action is required. 246 241 If you did not request a password reset, no action is required.
''' 247 242 '''
248 243
send_mail("Flashy password reset", 249 244 send_mail("Flashy password reset",
body % (user.pk, token), 250 245 body % (user.pk, token),
"noreply@flashy.cards", 251 246 "noreply@flashy.cards",
[user.email]) 252 247 [user.email])
253 248
return Response(status=HTTP_204_NO_CONTENT) 254 249 return Response(status=HTTP_204_NO_CONTENT)
255 250
256 251
@api_view(['POST']) 257 252 @api_view(['POST'])
def reset_password(request, format=None): 258 253 def reset_password(request, format=None):
""" 259 254 """
Updates user's password to new password if token is valid. 260 255 Updates user's password to new password if token is valid.
--- 261 256 ---
request_serializer: PasswordResetSerializer 262 257 request_serializer: PasswordResetSerializer
""" 263 258 """
data = PasswordResetSerializer(data=request.data) 264 259 data = PasswordResetSerializer(data=request.data)
data.is_valid(raise_exception=True) 265 260 data.is_valid(raise_exception=True)
266 261
user = User.objects.get(id=data['uid'].value) 267 262 user = User.objects.get(id=data['uid'].value)
# Check token validity. 268 263 # Check token validity.
269 264
if default_token_generator.check_token(user, data['token'].value): 270 265 if default_token_generator.check_token(user, data['token'].value):
user.set_password(data['new_password'].value) 271 266 user.set_password(data['new_password'].value)
user.save() 272 267 user.save()
else: 273 268 else:
raise ValidationError('Could not verify reset token') 274 269 raise ValidationError('Could not verify reset token')
return Response(status=HTTP_204_NO_CONTENT) 275 270 return Response(status=HTTP_204_NO_CONTENT)
276 271
277 272
class FlashcardViewSet(GenericViewSet, CreateModelMixin, RetrieveModelMixin): 278 273 class FlashcardViewSet(GenericViewSet, CreateModelMixin, RetrieveModelMixin):
queryset = Flashcard.objects.all() 279 274 queryset = Flashcard.objects.all()
serializer_class = FlashcardSerializer 280 275 serializer_class = FlashcardSerializer
permission_classes = [IsAuthenticated] 281 276 permission_classes = [IsAuthenticated]
282 277
# Override create in CreateModelMixin 283 278 # Override create in CreateModelMixin
def create(self, request, *args, **kwargs): 284 279 def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data) 285 280 serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True) 286 281 serializer.is_valid(raise_exception=True)
serializer.validated_data['author'] = request.user 287 282 serializer.validated_data['author'] = request.user
self.perform_create(serializer) 288 283 self.perform_create(serializer)
headers = self.get_success_headers(serializer.data) 289 284 headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=HTTP_201_CREATED, headers=headers) 290 285 return Response(serializer.data, status=HTTP_201_CREATED, headers=headers)