Commit 366764be3b875cda6c3612cbb2dffe4e56a429eb

Authored by Laura Hawkins
1 parent 628f642f30
Exists in master

just working on things

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

flashcards/views.py View file @ 366764b
from django.contrib import auth 1 1 from django.contrib import auth
from django.db.models import Q 2 2 from django.db.models import Q
from flashcards.api import StandardResultsSetPagination 3 3 from flashcards.api import StandardResultsSetPagination
from flashcards.models import Section, User, Flashcard, FlashcardReport, UserFlashcard 4 4 from flashcards.models import Section, User, Flashcard, FlashcardReport, UserFlashcard
from flashcards.serializers import SectionSerializer, UserUpdateSerializer, RegistrationSerializer, UserSerializer, \ 5 5 from flashcards.serializers import SectionSerializer, UserUpdateSerializer, RegistrationSerializer, UserSerializer, \
PasswordResetSerializer, PasswordResetRequestSerializer, EmailPasswordSerializer, FlashcardSerializer 6 6 PasswordResetSerializer, PasswordResetRequestSerializer, EmailPasswordSerializer, FlashcardSerializer
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 """
flashcards = Flashcard.cards_visible_to(request.user).filter( \ 33 33 flashcards = Flashcard.cards_visible_to(request.user).filter( \
section=self.get_object(), is_hidden=False).all() 34 34 section=self.get_object(), is_hidden=False).all()
35 35
return Response(FlashcardSerializer(flashcards, many=True).data) 36 36 return Response(FlashcardSerializer(flashcards, many=True).data)
37 37
@detail_route(methods=['post'], permission_classes=[IsAuthenticated]) 38 38 @detail_route(methods=['post'], permission_classes=[IsAuthenticated])
def enroll(self, request, pk): 39 39 def enroll(self, request, pk):
""" 40 40 """
Add the current user to a specified section 41 41 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. 42 42 If the class has a whitelist, but the user is not on the whitelist, the request will fail.
--- 43 43 ---
omit_serializer: true 44 44 omit_serializer: true
parameters: 45 45 parameters:
- fake: None 46 46 - fake: None
parameters_strategy: 47 47 parameters_strategy:
form: replace 48 48 form: replace
""" 49 49 """
section = self.get_object() 50 50 section = self.get_object()
if request.user.sections.filter(pk=section.pk).exists(): 51 51 if request.user.sections.filter(pk=section.pk).exists():
raise ValidationError("You are already in this section.") 52 52 raise ValidationError("You are already in this section.")
if section.is_whitelisted and not section.is_user_on_whitelist(request.user): 53 53 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.") 54 54 raise PermissionDenied("You must be on the whitelist to add this section.")
request.user.sections.add(section) 55 55 request.user.sections.add(section)
return Response(status=HTTP_204_NO_CONTENT) 56 56 return Response(status=HTTP_204_NO_CONTENT)
57 57
@detail_route(methods=['post'], permission_classes=[IsAuthenticated]) 58 58 @detail_route(methods=['post'], permission_classes=[IsAuthenticated])
def drop(self, request, pk): 59 59 def drop(self, request, pk):
""" 60 60 """
Remove the current user from a specified section 61 61 Remove the current user from a specified section
If the user is not in the class, the request will fail. 62 62 If the user is not in the class, the request will fail.
--- 63 63 ---
omit_serializer: true 64 64 omit_serializer: true
parameters: 65 65 parameters:
- fake: None 66 66 - fake: None
parameters_strategy: 67 67 parameters_strategy:
form: replace 68 68 form: replace
""" 69 69 """
section = self.get_object() 70 70 section = self.get_object()
if not section.user_set.filter(pk=request.user.pk).exists(): 71 71 if not section.user_set.filter(pk=request.user.pk).exists():
raise ValidationError("You are not in the section.") 72 72 raise ValidationError("You are not in the section.")
section.user_set.remove(request.user) 73 73 section.user_set.remove(request.user)
return Response(status=HTTP_204_NO_CONTENT) 74 74 return Response(status=HTTP_204_NO_CONTENT)
75 75
@list_route(methods=['get'], permission_classes=[IsAuthenticated]) 76 76 @list_route(methods=['get'], permission_classes=[IsAuthenticated])
def search(self, request): 77 77 def search(self, request):
query = request.GET.get('q',None) 78 78 query = request.GET.get('q',None)
if not query: return Response('[]') 79 79 if not query: return Response('[]')
qs = Section.search(query.split(' '))[:8] 80 80 qs = Section.search(query.split(' '))[:8]
serializer = SectionSerializer(qs, many=True) 81 81 serializer = SectionSerializer(qs, many=True)
return Response(serializer.data) 82 82 return Response(serializer.data)
83 """
84 @list_route(methods=['get'], permission_classes=[IsAuthenticated])
85 def deck(self, request):
86 query = request.GET.get('q', None)
87 if not query: return Response('[]')
88 qs = Section.search(query.split(' '))[:8]
89 serializer = SectionSerializer(qs, many=True)
90 return Response(serializer.data)"""
83 91
84 92
class UserSectionListView(ListAPIView): 85 93 class UserSectionListView(ListAPIView):
serializer_class = SectionSerializer 86 94 serializer_class = SectionSerializer
permission_classes = [IsAuthenticated] 87 95 permission_classes = [IsAuthenticated]
88 96
def get_queryset(self): 89 97 def get_queryset(self):
return self.request.user.sections.all() 90 98 return self.request.user.sections.all()
91 99
def paginate_queryset(self, queryset): return None 92 100 def paginate_queryset(self, queryset): return None
93 101
94 102
class UserDetail(GenericAPIView): 95 103 class UserDetail(GenericAPIView):
serializer_class = UserSerializer 96 104 serializer_class = UserSerializer
permission_classes = [IsAuthenticated] 97 105 permission_classes = [IsAuthenticated]
98 106
def get_queryset(self): 99 107 def get_queryset(self):
return User.objects.all() 100 108 return User.objects.all()
101 109
def patch(self, request, format=None): 102 110 def patch(self, request, format=None):
""" 103 111 """
Updates the user's password, or verifies their email address 104 112 Updates the user's password, or verifies their email address
--- 105 113 ---
request_serializer: UserUpdateSerializer 106 114 request_serializer: UserUpdateSerializer
response_serializer: UserSerializer 107 115 response_serializer: UserSerializer
""" 108 116 """
data = UserUpdateSerializer(data=request.data, context={'user': request.user}) 109 117 data = UserUpdateSerializer(data=request.data, context={'user': request.user})
data.is_valid(raise_exception=True) 110 118 data.is_valid(raise_exception=True)
data = data.validated_data 111 119 data = data.validated_data
112 120
if 'new_password' in data: 113 121 if 'new_password' in data:
if not request.user.check_password(data['old_password']): 114 122 if not request.user.check_password(data['old_password']):
raise ValidationError('old_password is incorrect') 115 123 raise ValidationError('old_password is incorrect')
request.user.set_password(data['new_password']) 116 124 request.user.set_password(data['new_password'])
request.user.save() 117 125 request.user.save()
118 126
if 'confirmation_key' in data: 119 127 if 'confirmation_key' in data:
try: 120 128 try:
request.user.confirm_email(data['confirmation_key']) 121 129 request.user.confirm_email(data['confirmation_key'])
except EmailAddress.DoesNotExist: 122 130 except EmailAddress.DoesNotExist:
raise ValidationError('confirmation_key is invalid') 123 131 raise ValidationError('confirmation_key is invalid')
124 132
return Response(UserSerializer(request.user).data) 125 133 return Response(UserSerializer(request.user).data)
126 134
def get(self, request, format=None): 127 135 def get(self, request, format=None):
""" 128 136 """
Return data about the user 129 137 Return data about the user
--- 130 138 ---
response_serializer: UserSerializer 131 139 response_serializer: UserSerializer
""" 132 140 """
serializer = UserSerializer(request.user, context={'request': request}) 133 141 serializer = UserSerializer(request.user, context={'request': request})
return Response(serializer.data) 134 142 return Response(serializer.data)
135 143
def delete(self, request): 136 144 def delete(self, request):
""" 137 145 """
Irrevocably delete the user and their data 138 146 Irrevocably delete the user and their data
139 147
Yes, really 140 148 Yes, really
""" 141 149 """
request.user.delete() 142 150 request.user.delete()
return Response(status=HTTP_204_NO_CONTENT) 143 151 return Response(status=HTTP_204_NO_CONTENT)
144 152
145 153
@api_view(['POST']) 146 154 @api_view(['POST'])
def register(request, format=None): 147 155 def register(request, format=None):
""" 148 156 """
Register a new user 149 157 Register a new user
--- 150 158 ---
request_serializer: EmailPasswordSerializer 151 159 request_serializer: EmailPasswordSerializer
response_serializer: UserSerializer 152 160 response_serializer: UserSerializer
""" 153 161 """
data = RegistrationSerializer(data=request.data) 154 162 data = RegistrationSerializer(data=request.data)
data.is_valid(raise_exception=True) 155 163 data.is_valid(raise_exception=True)
156 164
User.objects.create_user(**data.validated_data) 157 165 User.objects.create_user(**data.validated_data)
user = authenticate(**data.validated_data) 158 166 user = authenticate(**data.validated_data)
auth.login(request, user) 159 167 auth.login(request, user)
160 168
body = ''' 161 169 body = '''
Visit the following link to confirm your email address: 162 170 Visit the following link to confirm your email address:
https://flashy.cards/app/verify_email/%s 163 171 https://flashy.cards/app/verify_email/%s
164 172
If you did not register for Flashy, no action is required. 165 173 If you did not register for Flashy, no action is required.
''' 166 174 '''
167 175
assert send_mail("Flashy email verification", 168 176 assert send_mail("Flashy email verification",
body % user.confirmation_key, 169 177 body % user.confirmation_key,
"noreply@flashy.cards", 170 178 "noreply@flashy.cards",
[user.email]) 171 179 [user.email])
172 180
return Response(UserSerializer(request.user).data, status=HTTP_201_CREATED) 173 181 return Response(UserSerializer(request.user).data, status=HTTP_201_CREATED)
174 182
175 183
@api_view(['POST']) 176 184 @api_view(['POST'])
def login(request): 177 185 def login(request):
""" 178 186 """
Authenticates user and returns user data if valid. 179 187 Authenticates user and returns user data if valid.
--- 180 188 ---
request_serializer: EmailPasswordSerializer 181 189 request_serializer: EmailPasswordSerializer
response_serializer: UserSerializer 182 190 response_serializer: UserSerializer
""" 183 191 """
184 192
data = EmailPasswordSerializer(data=request.data) 185 193 data = EmailPasswordSerializer(data=request.data)
data.is_valid(raise_exception=True) 186 194 data.is_valid(raise_exception=True)
user = authenticate(**data.validated_data) 187 195 user = authenticate(**data.validated_data)
188 196
if user is None: 189 197 if user is None:
raise AuthenticationFailed('Invalid email or password') 190 198 raise AuthenticationFailed('Invalid email or password')
if not user.is_active: 191 199 if not user.is_active:
raise NotAuthenticated('Account is disabled') 192 200 raise NotAuthenticated('Account is disabled')
auth.login(request, user) 193 201 auth.login(request, user)
return Response(UserSerializer(request.user).data) 194 202 return Response(UserSerializer(request.user).data)
195 203
196 204
@api_view(['POST']) 197 205 @api_view(['POST'])
@permission_classes((IsAuthenticated, )) 198 206 @permission_classes((IsAuthenticated, ))
def logout(request, format=None): 199 207 def logout(request, format=None):
""" 200 208 """
Logs the authenticated user out. 201 209 Logs the authenticated user out.
""" 202 210 """
auth.logout(request) 203 211 auth.logout(request)
return Response(status=HTTP_204_NO_CONTENT) 204 212 return Response(status=HTTP_204_NO_CONTENT)
205 213
206 214
@api_view(['POST']) 207 215 @api_view(['POST'])
def request_password_reset(request, format=None): 208 216 def request_password_reset(request, format=None):
""" 209 217 """
Send a password reset token/link to the provided email. 210 218 Send a password reset token/link to the provided email.
--- 211 219 ---
request_serializer: PasswordResetRequestSerializer 212 220 request_serializer: PasswordResetRequestSerializer
""" 213 221 """
data = PasswordResetRequestSerializer(data=request.data) 214 222 data = PasswordResetRequestSerializer(data=request.data)
data.is_valid(raise_exception=True) 215 223 data.is_valid(raise_exception=True)
user = User.objects.get(email=data['email'].value) 216 224 user = User.objects.get(email=data['email'].value)
token = default_token_generator.make_token(user) 217 225 token = default_token_generator.make_token(user)
218 226
body = ''' 219 227 body = '''
Visit the following link to reset your password: 220 228 Visit the following link to reset your password:
https://flashy.cards/app/reset_password/%d/%s 221 229 https://flashy.cards/app/reset_password/%d/%s
222 230
If you did not request a password reset, no action is required. 223 231 If you did not request a password reset, no action is required.
''' 224 232 '''
225 233
send_mail("Flashy password reset", 226 234 send_mail("Flashy password reset",
body % (user.pk, token), 227 235 body % (user.pk, token),
"noreply@flashy.cards", 228 236 "noreply@flashy.cards",
[user.email]) 229 237 [user.email])
230 238
return Response(status=HTTP_204_NO_CONTENT) 231 239 return Response(status=HTTP_204_NO_CONTENT)
232 240
233 241
@api_view(['POST']) 234 242 @api_view(['POST'])
def reset_password(request, format=None): 235 243 def reset_password(request, format=None):
""" 236 244 """
Updates user's password to new password if token is valid. 237 245 Updates user's password to new password if token is valid.
--- 238 246 ---
request_serializer: PasswordResetSerializer 239 247 request_serializer: PasswordResetSerializer
""" 240 248 """
data = PasswordResetSerializer(data=request.data) 241 249 data = PasswordResetSerializer(data=request.data)
data.is_valid(raise_exception=True) 242 250 data.is_valid(raise_exception=True)
243 251
user = User.objects.get(id=data['uid'].value) 244 252 user = User.objects.get(id=data['uid'].value)
# Check token validity. 245 253 # Check token validity.
246 254
if default_token_generator.check_token(user, data['token'].value): 247 255 if default_token_generator.check_token(user, data['token'].value):
user.set_password(data['new_password'].value) 248 256 user.set_password(data['new_password'].value)