Commit 21835759b7626ba09aba61b217aaca4fe23d1cdf
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'] |