Commit bca16d61f71e76da820257ea976bee1bc4cf321f

Authored by Rohan Rangray
1 parent 94b93b5796
Exists in master

Added the patch method to FlashcardViewSet to edit cards.

Showing 5 changed files with 95 additions and 7 deletions Side-by-side Diff

flashcards/migrations/0011_auto_20150514_0207.py View file @ bca16d6
  1 +# -*- coding: utf-8 -*-
  2 +from __future__ import unicode_literals
  3 +
  4 +from django.db import models, migrations
  5 +import flashcards.fields
  6 +
  7 +
  8 +class Migration(migrations.Migration):
  9 +
  10 + dependencies = [
  11 + ('flashcards', '0010_auto_20150513_1546'),
  12 + ]
  13 +
  14 + operations = [
  15 + migrations.AlterModelOptions(
  16 + name='lectureperiod',
  17 + options={'ordering': ['section', 'week_day']},
  18 + ),
  19 + migrations.AlterField(
  20 + model_name='userflashcard',
  21 + name='mask',
  22 + field=flashcards.fields.MaskField(default=None, blank_sep=b',', range_sep=b'-', max_length=255, blank=True, help_text=b'The user-specific mask on the card', null=True),
  23 + ),
  24 + migrations.AlterField(
  25 + model_name='userflashcard',
  26 + name='pulled',
  27 + field=models.DateTimeField(default=None, help_text=b'When the user pulled the card', null=True, blank=True),
  28 + ),
  29 + migrations.AlterField(
  30 + model_name='userflashcard',
  31 + name='unpulled',
  32 + field=models.DateTimeField(default=None, help_text=b'When the user unpulled this card', null=True, blank=True),
  33 + ),
  34 + ]
flashcards/models.py View file @ bca16d6
... ... @@ -54,10 +54,10 @@
54 54 3. A user has a flashcard hidden from them
55 55 """
56 56 user = ForeignKey('User')
57   - mask = MaskField(max_length=255, null=True, blank=True, help_text="The user-specific mask on the card")
58   - pulled = DateTimeField(blank=True, null=True, help_text="When the user pulled the card")
  57 + mask = MaskField(max_length=255, null=True, blank=True, default=None, help_text="The user-specific mask on the card")
  58 + pulled = DateTimeField(blank=True, null=True, default=None, help_text="When the user pulled the card")
59 59 flashcard = ForeignKey('Flashcard')
60   - unpulled = DateTimeField(blank=True, null=True, help_text="When the user unpulled this card")
  60 + unpulled = DateTimeField(blank=True, null=True, default=None, help_text="When the user unpulled this card")
61 61  
62 62 class Meta:
63 63 # There can be at most one UserFlashcard for each User and Flashcard
flashcards/serializers.py View file @ bca16d6
... ... @@ -153,7 +153,24 @@
153 153 raise serializers.ValidationError("Hide reason limit exceeded")
154 154 return value
155 155  
  156 + def validate_mask(self, value):
  157 + if value.max_offset() >= len(self.text):
  158 + raise serializers.ValidationError("Mask out of bounds")
  159 + return value
  160 +
156 161 class Meta:
157 162 model = Flashcard
158   - exclude = 'author', 'mask',
  163 + exclude = 'author',
  164 +
  165 +
  166 +class FlashcardUpdateSerializer(serializers.Serializer):
  167 + text = CharField(max_length=255)
  168 + material_date = DateTimeField()
  169 + mask = MaskFieldSerializer()
  170 +
  171 + def validate_material_date(self, date):
  172 + quarter_end = datetime(2015, 6, 15)
  173 + if date > quarter_end:
  174 + raise serializers.ValidationError("Invalid material_date for the flashcard")
  175 + return date
flashcards/validators.py View file @ bca16d6
1   -__author__ = 'rray'
2   -
3 1 from collections import Iterable
4 2  
5 3  
... ... @@ -11,6 +9,9 @@
11 9 self._interval_check()
12 10 self._overlap_check()
13 11  
  12 + def max_offset(self):
  13 + return self._end
  14 +
14 15 def _iterable_check(self):
15 16 if not all([isinstance(i, Iterable) for i in self]):
16 17 raise TypeError("Interval not a valid iterable")
... ... @@ -26,6 +27,7 @@
26 27 if not (0 <= beg <= 255) or not (0 <= end <= 255) or not (beg <= end) or not (beg > p_end):
27 28 raise OverlapIntervalException((beg, end), "Invalid interval offsets in the mask")
28 29 p_beg, p_end = beg, end
  30 + self._end = end
29 31  
30 32  
31 33 class OverlapIntervalException(Exception):
flashcards/views.py View file @ bca16d6
... ... @@ -3,7 +3,8 @@
3 3 from flashcards.api import StandardResultsSetPagination
4 4 from flashcards.models import Section, User, Flashcard, FlashcardReport, UserFlashcard
5 5 from flashcards.serializers import SectionSerializer, UserUpdateSerializer, RegistrationSerializer, UserSerializer, \
6   - PasswordResetSerializer, PasswordResetRequestSerializer, EmailPasswordSerializer, FlashcardSerializer
  6 + PasswordResetSerializer, PasswordResetRequestSerializer, EmailPasswordSerializer, FlashcardSerializer, \
  7 + FlashcardUpdateSerializer
7 8 from rest_framework.decorators import detail_route, permission_classes, api_view, list_route
8 9 from rest_framework.generics import ListAPIView, GenericAPIView
9 10 from rest_framework.mixins import CreateModelMixin, RetrieveModelMixin
... ... @@ -16,6 +17,7 @@
16 17 from rest_framework.response import Response
17 18 from rest_framework.exceptions import AuthenticationFailed, NotAuthenticated, ValidationError, PermissionDenied
18 19 from simple_email_confirmation import EmailAddress
  20 +from datetime import datetime
19 21  
20 22  
21 23 class SectionViewSet(ReadOnlyModelViewSet):
... ... @@ -295,4 +297,37 @@
295 297 user_card, created = UserFlashcard.objects.get_or_create(user=user, flashcard=flashcard)
296 298 user_card.save()
297 299 return Response(status=HTTP_204_NO_CONTENT)
  300 +
  301 + @detail_route(methods=['PATCH'], permission_classes=[IsAuthenticated])
  302 + def patch(self, request, pk):
  303 + """
  304 + Edit settings related to a card for the user.
  305 + :param request: The request object.
  306 + :param pk: The primary key of the flashcard.
  307 + :return: A 204 response upon success.
  308 + """
  309 + user = request.user
  310 + flashcard = Flashcard.objects.get(pk=pk)
  311 + mask = flashcard.mask
  312 + data = FlashcardUpdateSerializer(data=request.data)
  313 + data.is_valid(raise_exception=True)
  314 + new_flashcard = data.validated_data
  315 + if ('material_date' in new_flashcard and new_flashcard['material_date'] != flashcard.material_date) \
  316 + or ('text' in new_flashcard and new_flashcard.text != flashcard.text):
  317 + if flashcard.author != user or UserFlashcard.objects.filter(flashcard=flashcard).count() > 1:
  318 + flashcard.pk = None
  319 + flashcard.mask = mask
  320 + if 'material_date' in new_flashcard:
  321 + flashcard.material_date = new_flashcard['material_date']
  322 + if 'text' in new_flashcard:
  323 + flashcard.text = new_flashcard['text']
  324 + if 'mask' in new_flashcard:
  325 + flashcard.mask = new_flashcard['mask']
  326 + flashcard.save()
  327 + user_flashcard, created = UserFlashcard.objects.get_or_create(user=user, flashcard=flashcard)
  328 + if created:
  329 + user_flashcard.pulled = datetime.now()
  330 + user_flashcard.mask = flashcard.mask
  331 + if 'mask' in new_flashcard:
  332 + user_flashcard.mask = new_flashcard['mask']