Commit cb2c2f3fc331b3892fb4fed69babe668e287e99d

Authored by Rohan Rangray
1 parent 9b28c5c0eb
Exists in master

Fixed FlashcardMask JSON deserialization.

Showing 2 changed files with 5 additions and 4 deletions Inline Diff

flashcards/serializers.py View file @ cb2c2f3
from django.utils.datetime_safe import datetime 1 1 from django.utils.datetime_safe import datetime
import pytz 2 2 import pytz
from flashcards.models import Section, LecturePeriod, User, Flashcard 3 3 from flashcards.models import Section, LecturePeriod, User, Flashcard
from flashcards.validators import FlashcardMask, OverlapIntervalException 4 4 from flashcards.validators import FlashcardMask, OverlapIntervalException
from rest_framework import serializers 5 5 from rest_framework import serializers
from rest_framework.fields import EmailField, BooleanField, CharField, IntegerField 6 6 from rest_framework.fields import EmailField, BooleanField, CharField, IntegerField
from rest_framework.relations import HyperlinkedRelatedField 7 7 from rest_framework.relations import HyperlinkedRelatedField
from rest_framework.serializers import ModelSerializer, Serializer 8 8 from rest_framework.serializers import ModelSerializer, Serializer
from rest_framework.validators import UniqueValidator 9 9 from rest_framework.validators import UniqueValidator
from json import dumps, loads 10 10 from json import dumps, loads
11 11
12 12
class EmailSerializer(Serializer): 13 13 class EmailSerializer(Serializer):
email = EmailField(required=True) 14 14 email = EmailField(required=True)
15 15
16 16
class EmailPasswordSerializer(EmailSerializer): 17 17 class EmailPasswordSerializer(EmailSerializer):
password = CharField(required=True) 18 18 password = CharField(required=True)
19 19
20 20
class RegistrationSerializer(EmailPasswordSerializer): 21 21 class RegistrationSerializer(EmailPasswordSerializer):
email = EmailField(required=True, validators=[UniqueValidator(queryset=User.objects.all())]) 22 22 email = EmailField(required=True, validators=[UniqueValidator(queryset=User.objects.all())])
23 23
24 24
class PasswordResetRequestSerializer(EmailSerializer): 25 25 class PasswordResetRequestSerializer(EmailSerializer):
def validate_email(self, value): 26 26 def validate_email(self, value):
try: 27 27 try:
User.objects.get(email=value) 28 28 User.objects.get(email=value)
return value 29 29 return value
except User.DoesNotExist: 30 30 except User.DoesNotExist:
raise serializers.ValidationError('No user exists with that email') 31 31 raise serializers.ValidationError('No user exists with that email')
32 32
33 33
class PasswordResetSerializer(Serializer): 34 34 class PasswordResetSerializer(Serializer):
new_password = CharField(required=True, allow_blank=False) 35 35 new_password = CharField(required=True, allow_blank=False)
uid = IntegerField(required=True) 36 36 uid = IntegerField(required=True)
token = CharField(required=True) 37 37 token = CharField(required=True)
38 38
def validate_uid(self, value): 39 39 def validate_uid(self, value):
try: 40 40 try:
User.objects.get(id=value) 41 41 User.objects.get(id=value)
return value 42 42 return value
except User.DoesNotExist: 43 43 except User.DoesNotExist:
raise serializers.ValidationError('Could not verify reset token') 44 44 raise serializers.ValidationError('Could not verify reset token')
45 45
46 46
class UserUpdateSerializer(Serializer): 47 47 class UserUpdateSerializer(Serializer):
old_password = CharField(required=False) 48 48 old_password = CharField(required=False)
new_password = CharField(required=False, allow_blank=False) 49 49 new_password = CharField(required=False, allow_blank=False)
confirmation_key = CharField(required=False) 50 50 confirmation_key = CharField(required=False)
# reset_token = CharField(required=False) 51 51 # reset_token = CharField(required=False)
52 52
def validate(self, data): 53 53 def validate(self, data):
if 'new_password' in data and 'old_password' not in data: 54 54 if 'new_password' in data and 'old_password' not in data:
raise serializers.ValidationError('old_password is required to set a new_password') 55 55 raise serializers.ValidationError('old_password is required to set a new_password')
return data 56 56 return data
57 57
58 58
class Password(Serializer): 59 59 class Password(Serializer):
email = EmailField(required=True) 60 60 email = EmailField(required=True)
password = CharField(required=True) 61 61 password = CharField(required=True)
62 62
63 63
class LecturePeriodSerializer(ModelSerializer): 64 64 class LecturePeriodSerializer(ModelSerializer):
class Meta: 65 65 class Meta:
model = LecturePeriod 66 66 model = LecturePeriod
exclude = 'id', 'section' 67 67 exclude = 'id', 'section'
68 68
69 69
class SectionSerializer(ModelSerializer): 70 70 class SectionSerializer(ModelSerializer):
lectures = LecturePeriodSerializer(source='lectureperiod_set', many=True, read_only=True) 71 71 lectures = LecturePeriodSerializer(source='lectureperiod_set', many=True, read_only=True)
72 72
class Meta: 73 73 class Meta:
model = Section 74 74 model = Section
75 75
76 76
class UserSerializer(ModelSerializer): 77 77 class UserSerializer(ModelSerializer):
email = EmailField(required=False) 78 78 email = EmailField(required=False)
sections = HyperlinkedRelatedField(queryset=Section.objects.all(), many=True, view_name='section-detail') 79 79 sections = HyperlinkedRelatedField(queryset=Section.objects.all(), many=True, view_name='section-detail')
is_confirmed = BooleanField() 80 80 is_confirmed = BooleanField()
81 81
class Meta: 82 82 class Meta:
model = User 83 83 model = User
fields = ("sections", "email", "is_confirmed", "last_login", "date_joined") 84 84 fields = ("sections", "email", "is_confirmed", "last_login", "date_joined")
85 85
86 86
class MaskFieldSerializer(serializers.Field): 87 87 class MaskFieldSerializer(serializers.Field):
default_error_messages = { 88 88 default_error_messages = {
'max_length': 'Ensure this field has no more than {max_length} characters.', 89 89 'max_length': 'Ensure this field has no more than {max_length} characters.',
'interval': 'Ensure this field has valid intervals.', 90 90 'interval': 'Ensure this field has valid intervals.',
'overlap': 'Ensure this field does not have overlapping intervals.' 91 91 'overlap': 'Ensure this field does not have overlapping intervals.'
} 92 92 }
93 93
def to_representation(self, value): 94 94 def to_representation(self, value):
return dumps(list(self._make_mask(value))) 95 95 return dumps(list(self._make_mask(value)))
96 96
def to_internal_value(self, value): 97 97 def to_internal_value(self, value):
return self._make_mask(value) 98 98 return self._make_mask(loads(value))
99 99
def _make_mask(self, data): 100 100 def _make_mask(self, data):
try: 101 101 try:
mask = FlashcardMask(loads(data)) 102 102 mask = FlashcardMask(data)
except ValueError: 103 103 except ValueError:
raise serializers.ValidationError("Invalid JSON for MaskField") 104 104 raise serializers.ValidationError("Invalid JSON for MaskField")
except TypeError: 105 105 except TypeError:
raise serializers.ValidationError("Invalid data for MaskField.") 106 106 raise serializers.ValidationError("Invalid data for MaskField.")
except OverlapIntervalException: 107 107 except OverlapIntervalException:
raise serializers.ValidationError("Invalid intervals for MaskField data.") 108 108 raise serializers.ValidationError("Invalid intervals for MaskField data.")
if len(mask) > 32: 109 109 if len(mask) > 32:
raise serializers.ValidationError("Too many intervals in the mask.") 110 110 raise serializers.ValidationError("Too many intervals in the mask.")
return mask 111 111 return mask
112 112
113 113
class FlashcardSerializer(ModelSerializer): 114 114 class FlashcardSerializer(ModelSerializer):
is_hidden = BooleanField(read_only=True) 115 115 is_hidden = BooleanField(read_only=True)
hide_reason = CharField(read_only=True) 116 116 hide_reason = CharField(read_only=True)
mask = MaskFieldSerializer() 117 117 mask = MaskFieldSerializer()
118 118
flashcards/validators.py View file @ cb2c2f3
__author__ = 'rray' 1 1 __author__ = 'rray'
2 2
from collections import Iterable 3 3 from collections import Iterable
4 4
5 5
class FlashcardMask(set): 6 6 class FlashcardMask(set):
def __init__(self, *args, **kwargs): 7 7 def __init__(self, iterable, *args, **kwargs):
super(FlashcardMask, self).__init__(*args, **kwargs) 8 8 iterable = map(tuple, iterable)
9 super(FlashcardMask, self).__init__(iterable, *args, **kwargs)
self._iterable_check() 9 10 self._iterable_check()
self._interval_check() 10 11 self._interval_check()
self._overlap_check() 11 12 self._overlap_check()
12 13
def _iterable_check(self): 13 14 def _iterable_check(self):
if not all([isinstance(i, Iterable) for i in self]): 14 15 if not all([isinstance(i, Iterable) for i in self]):
raise TypeError("Interval not a valid iterable") 15 16 raise TypeError("Interval not a valid iterable")
16 17
def _interval_check(self): 17 18 def _interval_check(self):
if not all([len(i) == 2 for i in self]): 18 19 if not all([len(i) == 2 for i in self]):
raise TypeError("Intervals must have exactly 2 elements, begin and end") 19 20 raise TypeError("Intervals must have exactly 2 elements, begin and end")
20 21
def _overlap_check(self): 21 22 def _overlap_check(self):
p_beg, p_end = -1, -1 22 23 p_beg, p_end = -1, -1
for interval in sorted(self): 23 24 for interval in sorted(self):
beg, end = map(int, interval) 24 25 beg, end = map(int, interval)
if not (0 <= beg <= 255) or not (0 <= end <= 255) or not (beg <= end) or not (beg > p_end): 25 26 if not (0 <= beg <= 255) or not (0 <= end <= 255) or not (beg <= end) or not (beg > p_end):
raise OverlapIntervalException((beg, end), "Invalid interval offsets in the mask") 26 27 raise OverlapIntervalException((beg, end), "Invalid interval offsets in the mask")
p_beg, p_end = beg, end 27 28 p_beg, p_end = beg, end
28 29
29 30