Commit 21835759b7626ba09aba61b217aaca4fe23d1cdf

Authored by Andrew Buss
Exists in master

merge

Showing 5 changed files Side-by-side Diff

flashcards/migrations/0011_auto_20150514_0207.py View file @ 2183575
  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 @ 2183575
... ... @@ -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
... ... @@ -207,9 +207,6 @@
207 207 def __unicode__(self):
208 208 return '%s %s: %s (%s %s)' % (
209 209 self.department_abbreviation, self.course_num, self.course_title, self.instructor, self.quarter)
210   -
211   -
212   -
213 210  
214 211 class LecturePeriod(Model):
215 212 """
flashcards/serializers.py View file @ 2183575
... ... @@ -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 @ 2183575
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 @ 2183575
... ... @@ -2,7 +2,8 @@
2 2 from flashcards.api import StandardResultsSetPagination
3 3 from flashcards.models import Section, User, Flashcard, FlashcardReport, UserFlashcard
4 4 from flashcards.serializers import SectionSerializer, UserUpdateSerializer, RegistrationSerializer, UserSerializer, \
5   - PasswordResetSerializer, PasswordResetRequestSerializer, EmailPasswordSerializer, FlashcardSerializer
  5 + PasswordResetSerializer, PasswordResetRequestSerializer, EmailPasswordSerializer, FlashcardSerializer, \
  6 + FlashcardUpdateSerializer
6 7 from rest_framework.decorators import detail_route, permission_classes, api_view, list_route
7 8 from rest_framework.generics import ListAPIView, GenericAPIView
8 9 from rest_framework.mixins import CreateModelMixin, RetrieveModelMixin
... ... @@ -15,6 +16,7 @@
15 16 from rest_framework.response import Response
16 17 from rest_framework.exceptions import AuthenticationFailed, NotAuthenticated, ValidationError, PermissionDenied
17 18 from simple_email_confirmation import EmailAddress
  19 +from datetime import datetime
18 20  
19 21  
20 22 class SectionViewSet(ReadOnlyModelViewSet):
... ... @@ -87,6 +89,7 @@
87 89 """
88 90 qs = Flashcard.objects.all()
89 91 qs = qs.filter(userflashcard__user=request.user)
  92 + qs = qs.filter(section = self.get_object())
90 93 serializer = FlashcardSerializer(qs, many=True)
91 94 return Response(serializer.data)
92 95  
... ... @@ -304,4 +307,37 @@
304 307 user_card, created = UserFlashcard.objects.get_or_create(user=user, flashcard=flashcard)
305 308 user_card.save()
306 309 return Response(status=HTTP_204_NO_CONTENT)
  310 +
  311 + @detail_route(methods=['PATCH'], permission_classes=[IsAuthenticated])
  312 + def patch(self, request, pk):
  313 + """
  314 + Edit settings related to a card for the user.
  315 + :param request: The request object.
  316 + :param pk: The primary key of the flashcard.
  317 + :return: A 204 response upon success.
  318 + """
  319 + user = request.user
  320 + flashcard = Flashcard.objects.get(pk=pk)
  321 + mask = flashcard.mask
  322 + data = FlashcardUpdateSerializer(data=request.data)
  323 + data.is_valid(raise_exception=True)
  324 + new_flashcard = data.validated_data
  325 + if ('material_date' in new_flashcard and new_flashcard['material_date'] != flashcard.material_date) \
  326 + or ('text' in new_flashcard and new_flashcard.text != flashcard.text):
  327 + if flashcard.author != user or UserFlashcard.objects.filter(flashcard=flashcard).count() > 1:
  328 + flashcard.pk = None
  329 + flashcard.mask = mask
  330 + if 'material_date' in new_flashcard:
  331 + flashcard.material_date = new_flashcard['material_date']
  332 + if 'text' in new_flashcard:
  333 + flashcard.text = new_flashcard['text']
  334 + if 'mask' in new_flashcard:
  335 + flashcard.mask = new_flashcard['mask']
  336 + flashcard.save()
  337 + user_flashcard, created = UserFlashcard.objects.get_or_create(user=user, flashcard=flashcard)
  338 + if created:
  339 + user_flashcard.pulled = datetime.now()
  340 + user_flashcard.mask = flashcard.mask
  341 + if 'mask' in new_flashcard:
  342 + user_flashcard.mask = new_flashcard['mask']