Commit d1312d97db3c1c68be7919a7a6b656f59a1d9786

Authored by Rohan Rangray
1 parent 1c22f48d88
Exists in master

Added better error checking in QuizRequestSerializer.validate_sections

Showing 1 changed file with 4 additions and 1 deletions Inline Diff

flashcards/serializers.py View file @ d1312d9
from json import dumps, loads 1 1 from json import dumps, loads
2 2
from django.utils.datetime_safe import datetime 3 3 from django.utils.datetime_safe import datetime
from django.utils.timezone import now 4 4 from django.utils.timezone import now
from flashcards.models import Section, LecturePeriod, User, Flashcard, UserFlashcard, UserFlashcardQuiz 5 5 from flashcards.models import Section, LecturePeriod, User, Flashcard, UserFlashcard, UserFlashcardQuiz
from flashcards.validators import FlashcardMask, OverlapIntervalException 6 6 from flashcards.validators import FlashcardMask, OverlapIntervalException
from rest_framework import serializers 7 7 from rest_framework import serializers
from rest_framework.fields import EmailField, BooleanField, CharField, IntegerField, DateTimeField, empty, \ 8 8 from rest_framework.fields import EmailField, BooleanField, CharField, IntegerField, DateTimeField, empty, \
SerializerMethodField, FloatField 9 9 SerializerMethodField, FloatField
from rest_framework.serializers import ModelSerializer, Serializer, PrimaryKeyRelatedField, ListField 10 10 from rest_framework.serializers import ModelSerializer, Serializer, PrimaryKeyRelatedField, ListField
from rest_framework.validators import UniqueValidator 11 11 from rest_framework.validators import UniqueValidator
from flashy.settings import QUARTER_END, QUARTER_START 12 12 from flashy.settings import QUARTER_END, QUARTER_START
13 from collections import Iterable
13 14
14 15
class EmailSerializer(Serializer): 15 16 class EmailSerializer(Serializer):
email = EmailField(required=True) 16 17 email = EmailField(required=True)
17 18
18 19
class EmailPasswordSerializer(EmailSerializer): 19 20 class EmailPasswordSerializer(EmailSerializer):
password = CharField(required=True) 20 21 password = CharField(required=True)
21 22
22 23
class RegistrationSerializer(EmailPasswordSerializer): 23 24 class RegistrationSerializer(EmailPasswordSerializer):
email = EmailField(required=True, validators=[UniqueValidator(queryset=User.objects.all())]) 24 25 email = EmailField(required=True, validators=[UniqueValidator(queryset=User.objects.all())])
25 26
26 27
class PasswordResetRequestSerializer(EmailSerializer): 27 28 class PasswordResetRequestSerializer(EmailSerializer):
def validate_email(self, value): 28 29 def validate_email(self, value):
try: 29 30 try:
User.objects.get(email=value) 30 31 User.objects.get(email=value)
return value 31 32 return value
except User.DoesNotExist: 32 33 except User.DoesNotExist:
raise serializers.ValidationError('No user exists with that email') 33 34 raise serializers.ValidationError('No user exists with that email')
34 35
35 36
class PasswordResetSerializer(Serializer): 36 37 class PasswordResetSerializer(Serializer):
new_password = CharField(required=True, allow_blank=False) 37 38 new_password = CharField(required=True, allow_blank=False)
uid = IntegerField(required=True) 38 39 uid = IntegerField(required=True)
token = CharField(required=True) 39 40 token = CharField(required=True)
40 41
def validate_uid(self, value): 41 42 def validate_uid(self, value):
try: 42 43 try:
User.objects.get(id=value) 43 44 User.objects.get(id=value)
return value 44 45 return value
except User.DoesNotExist: 45 46 except User.DoesNotExist:
raise serializers.ValidationError('Could not verify reset token') 46 47 raise serializers.ValidationError('Could not verify reset token')
47 48
48 49
class UserUpdateSerializer(Serializer): 49 50 class UserUpdateSerializer(Serializer):
old_password = CharField(required=False) 50 51 old_password = CharField(required=False)
new_password = CharField(required=False, allow_blank=False) 51 52 new_password = CharField(required=False, allow_blank=False)
confirmation_key = CharField(required=False) 52 53 confirmation_key = CharField(required=False)
# reset_token = CharField(required=False) 53 54 # reset_token = CharField(required=False)
54 55
def validate(self, data): 55 56 def validate(self, data):
if 'new_password' in data and 'old_password' not in data: 56 57 if 'new_password' in data and 'old_password' not in data:
raise serializers.ValidationError('old_password is required to set a new_password') 57 58 raise serializers.ValidationError('old_password is required to set a new_password')
return data 58 59 return data
59 60
60 61
class Password(Serializer): 61 62 class Password(Serializer):
email = EmailField(required=True) 62 63 email = EmailField(required=True)
password = CharField(required=True) 63 64 password = CharField(required=True)
64 65
65 66
class LecturePeriodSerializer(ModelSerializer): 66 67 class LecturePeriodSerializer(ModelSerializer):
class Meta: 67 68 class Meta:
model = LecturePeriod 68 69 model = LecturePeriod
exclude = 'id', 'section' 69 70 exclude = 'id', 'section'
70 71
71 72
class SectionSerializer(ModelSerializer): 72 73 class SectionSerializer(ModelSerializer):
lecture_times = CharField() 73 74 lecture_times = CharField()
short_name = CharField() 74 75 short_name = CharField()
long_name = CharField() 75 76 long_name = CharField()
76 77
class Meta: 77 78 class Meta:
model = Section 78 79 model = Section
79 80
80 81
class DeepSectionSerializer(SectionSerializer): 81 82 class DeepSectionSerializer(SectionSerializer):
lectures = LecturePeriodSerializer(source='lectureperiod_set', many=True, read_only=True) 82 83 lectures = LecturePeriodSerializer(source='lectureperiod_set', many=True, read_only=True)
83 84
84 85
class UserSerializer(ModelSerializer): 85 86 class UserSerializer(ModelSerializer):
email = EmailField(required=False) 86 87 email = EmailField(required=False)
sections = SectionSerializer(many=True) 87 88 sections = SectionSerializer(many=True)
is_confirmed = BooleanField() 88 89 is_confirmed = BooleanField()
89 90
class Meta: 90 91 class Meta:
model = User 91 92 model = User
fields = ("sections", "email", "is_confirmed", "last_login", "date_joined") 92 93 fields = ("sections", "email", "is_confirmed", "last_login", "date_joined")
93 94
94 95
class MaskFieldSerializer(serializers.Field): 95 96 class MaskFieldSerializer(serializers.Field):
default_error_messages = { 96 97 default_error_messages = {
'max_length': 'Ensure this field has no more than {max_length} characters.', 97 98 'max_length': 'Ensure this field has no more than {max_length} characters.',
'interval': 'Ensure this field has valid intervals.', 98 99 'interval': 'Ensure this field has valid intervals.',
'overlap': 'Ensure this field does not have overlapping intervals.' 99 100 'overlap': 'Ensure this field does not have overlapping intervals.'
} 100 101 }
101 102
def to_representation(self, value): 102 103 def to_representation(self, value):
return map(list, self._make_mask(value)) 103 104 return map(list, self._make_mask(value))
104 105
def to_internal_value(self, value): 105 106 def to_internal_value(self, value):
if not isinstance(value, list): 106 107 if not isinstance(value, list):
value = loads(value) 107 108 value = loads(value)
return self._make_mask(value) 108 109 return self._make_mask(value)
109 110
def _make_mask(self, data): 110 111 def _make_mask(self, data):
try: 111 112 try:
mask = FlashcardMask(data) 112 113 mask = FlashcardMask(data)
except ValueError: 113 114 except ValueError:
raise serializers.ValidationError("Invalid JSON for MaskField") 114 115 raise serializers.ValidationError("Invalid JSON for MaskField")
except TypeError: 115 116 except TypeError:
raise serializers.ValidationError("Invalid data for MaskField.") 116 117 raise serializers.ValidationError("Invalid data for MaskField.")
except OverlapIntervalException: 117 118 except OverlapIntervalException:
raise serializers.ValidationError("Invalid intervals for MaskField data.") 118 119 raise serializers.ValidationError("Invalid intervals for MaskField data.")
if len(mask) > 32: 119 120 if len(mask) > 32:
raise serializers.ValidationError("Too many intervals in the mask.") 120 121 raise serializers.ValidationError("Too many intervals in the mask.")
return mask 121 122 return mask
122 123
123 124
class FlashcardSerializer(ModelSerializer): 124 125 class FlashcardSerializer(ModelSerializer):
is_hidden = SerializerMethodField() 125 126 is_hidden = SerializerMethodField()
is_in_deck = SerializerMethodField() 126 127 is_in_deck = SerializerMethodField()
# hide_reason = CharField(read_only=True) 127 128 # hide_reason = CharField(read_only=True)
material_week_num = IntegerField(read_only=True) 128 129 material_week_num = IntegerField(read_only=True)
material_date = DateTimeField(default=now) 129 130 material_date = DateTimeField(default=now)
mask = MaskFieldSerializer(allow_null=True) 130 131 mask = MaskFieldSerializer(allow_null=True)
score = FloatField(read_only=True) 131 132 score = FloatField(read_only=True)
132 133
def validate_material_date(self, value): 133 134 def validate_material_date(self, value):
# TODO: make this dynamic 134 135 # TODO: make this dynamic
if QUARTER_START <= value <= QUARTER_END: 135 136 if QUARTER_START <= value <= QUARTER_END:
return value 136 137 return value
else: 137 138 else:
raise serializers.ValidationError("Material date is outside allowed range for this quarter") 138 139 raise serializers.ValidationError("Material date is outside allowed range for this quarter")
139 140
def validate_pushed(self, value): 140 141 def validate_pushed(self, value):
if value > datetime.now(): 141 142 if value > datetime.now():
raise serializers.ValidationError("Invalid creation date for the Flashcard") 142 143 raise serializers.ValidationError("Invalid creation date for the Flashcard")
return value 143 144 return value
144 145
def validate_mask(self, value): 145 146 def validate_mask(self, value):
if value is None: 146 147 if value is None:
return None 147 148 return None
if len(self.initial_data['text']) < value.max_offset(): 148 149 if len(self.initial_data['text']) < value.max_offset():
raise serializers.ValidationError("Mask out of bounds") 149 150 raise serializers.ValidationError("Mask out of bounds")
return value 150 151 return value
151 152
def get_is_hidden(self, obj): 152 153 def get_is_hidden(self, obj):
if 'user' not in self.context: return False 153 154 if 'user' not in self.context: return False
return obj.is_hidden_from(self.context['user']) 154 155 return obj.is_hidden_from(self.context['user'])
155 156
def get_is_in_deck(self, obj): 156 157 def get_is_in_deck(self, obj):
if 'user' not in self.context: return False 157 158 if 'user' not in self.context: return False
return obj.is_in_deck(self.context['user']) 158 159 return obj.is_in_deck(self.context['user'])
159 160
class Meta: 160 161 class Meta:
model = Flashcard 161 162 model = Flashcard
exclude = 'author', 'previous' 162 163 exclude = 'author', 'previous'
163 164
164 165
class FlashcardUpdateSerializer(serializers.Serializer): 165 166 class FlashcardUpdateSerializer(serializers.Serializer):
text = CharField(max_length=255, required=False) 166 167 text = CharField(max_length=255, required=False)
material_date = DateTimeField(required=False) 167 168 material_date = DateTimeField(required=False)
mask = MaskFieldSerializer(required=False) 168 169 mask = MaskFieldSerializer(required=False)
169 170
def validate_material_date(self, date): 170 171 def validate_material_date(self, date):
if date > QUARTER_END: 171 172 if date > QUARTER_END:
raise serializers.ValidationError("Invalid material_date for the flashcard") 172 173 raise serializers.ValidationError("Invalid material_date for the flashcard")
return date 173 174 return date
174 175
def validate(self, attrs): 175 176 def validate(self, attrs):
# Make sure that at least one of the attributes was passed in 176 177 # Make sure that at least one of the attributes was passed in
if not any(i in attrs for i in ['material_date', 'text', 'mask']): 177 178 if not any(i in attrs for i in ['material_date', 'text', 'mask']):
raise serializers.ValidationError("No new value passed in") 178 179 raise serializers.ValidationError("No new value passed in")
return attrs 179 180 return attrs
180 181
181 182
class QuizRequestSerializer(serializers.Serializer): 182 183 class QuizRequestSerializer(serializers.Serializer):
sections = ListField(child=IntegerField(min_value=1), required=False) 183 184 sections = ListField(child=IntegerField(min_value=1), required=False)
material_date_begin = DateTimeField(default=QUARTER_START) 184 185 material_date_begin = DateTimeField(default=QUARTER_START)
material_date_end = DateTimeField(default=QUARTER_END) 185 186 material_date_end = DateTimeField(default=QUARTER_END)
186 187
def update(self, instance, validated_data): 187 188 def update(self, instance, validated_data):
pass 188 189 pass
189 190
def create(self, validated_data): 190 191 def create(self, validated_data):
return validated_data 191 192 return validated_data
192 193
def validate_material_date_begin(self, value): 193 194 def validate_material_date_begin(self, value):
if QUARTER_START <= value <= QUARTER_END: 194 195 if QUARTER_START <= value <= QUARTER_END:
return value 195 196 return value
raise serializers.ValidationError("Invalid begin date for the flashcard range") 196 197 raise serializers.ValidationError("Invalid begin date for the flashcard range")
197 198
def validate_material_date_end(self, value): 198 199 def validate_material_date_end(self, value):
if QUARTER_START <= value <= QUARTER_END: 199 200 if QUARTER_START <= value <= QUARTER_END:
return value 200 201 return value
raise serializers.ValidationError("Invalid end date for the flashcard range") 201 202 raise serializers.ValidationError("Invalid end date for the flashcard range")
202 203
def validate_sections(self, value): 203 204 def validate_sections(self, value):
if value is None: 204 205 if value is not None and not isinstance(value, Iterable):
206 raise serializers.ValidationError("Invalid section format. Expecting a list or no value.")
207 if value is None or len(value) == 0:
return Section.objects.all() 205 208 return Section.objects.all()
section_filter = Section.objects.filter(pk__in=value) 206 209 section_filter = Section.objects.filter(pk__in=value)
if not section_filter.exists(): 207 210 if not section_filter.exists():
raise serializers.ValidationError("Those aren't valid sections") 208 211 raise serializers.ValidationError("Those aren't valid sections")
return value 209 212 return value
210 213
def validate(self, attrs): 211 214 def validate(self, attrs):
if attrs['material_date_begin'] > attrs['material_date_end']: 212 215 if attrs['material_date_begin'] > attrs['material_date_end']:
raise serializers.ValidationError("Invalid range") 213 216 raise serializers.ValidationError("Invalid range")
if 'sections' not in attrs: 214 217 if 'sections' not in attrs: