serializers.py
11.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
from json import loads
from collections import Iterable
from django.utils.datetime_safe import datetime
from django.utils.timezone import now
from flashcards.models import Section, LecturePeriod, User, Flashcard, UserFlashcardQuiz, UserFlashcard
from flashcards.validators import FlashcardMask, OverlapIntervalException
from rest_framework import serializers
from rest_framework.fields import EmailField, BooleanField, CharField, IntegerField, DateTimeField, empty, \
SerializerMethodField, FloatField
from rest_framework.serializers import ModelSerializer, Serializer, PrimaryKeyRelatedField, ListField
from rest_framework.validators import UniqueValidator
from flashy.settings import QUARTER_END, QUARTER_START
class EmailSerializer(Serializer):
email = EmailField(required=True)
class LoginSerializer(EmailSerializer):
password = CharField(required=True)
class RegistrationSerializer(Serializer):
email = EmailField(required=True, validators=[UniqueValidator(queryset=User.objects.all())])
password = CharField(required=True, min_length=8)
class PasswordResetRequestSerializer(EmailSerializer):
def validate_email(self, value):
try:
User.objects.get(email=value)
return value
except User.DoesNotExist:
raise serializers.ValidationError('No user exists with that email')
class PasswordResetSerializer(Serializer):
new_password = CharField(required=True, allow_blank=False, min_length=8)
uid = IntegerField(required=True)
token = CharField(required=True)
def validate_uid(self, value):
try:
User.objects.get(id=value)
return value
except User.DoesNotExist:
raise serializers.ValidationError('Could not verify reset token')
class EmailVerificationSerializer(Serializer):
confirmation_key = CharField()
class UserUpdateSerializer(Serializer):
old_password = CharField(required=False)
new_password = CharField(required=False, allow_blank=False, min_length=8)
def validate(self, data):
if 'new_password' in data and 'old_password' not in data:
raise serializers.ValidationError('old_password is required to set a new_password')
return data
class LecturePeriodSerializer(ModelSerializer):
class Meta:
model = LecturePeriod
exclude = 'id', 'section'
class SectionSerializer(ModelSerializer):
lecture_times = CharField()
short_name = CharField()
long_name = CharField()
can_enroll = SerializerMethodField()
is_enrolled = SerializerMethodField()
class Meta:
model = Section
def get_can_enroll(self, obj):
if 'user' not in self.context: return False
if not obj.is_whitelisted: return True
return obj.is_user_on_whitelist(self.context['user'])
def get_is_enrolled(self, obj):
if 'user' not in self.context: return False
return obj.is_user_enrolled(self.context['user'])
class DeepSectionSerializer(SectionSerializer):
lectures = LecturePeriodSerializer(source='lectureperiod_set', many=True, read_only=True)
class FeedRequestSerializer(Serializer):
page = IntegerField(min_value=1, default=1, required=False)
def validate(self, attrs):
if not isinstance(attrs['page'], int):
raise serializers.ValidationError("Invalid page number")
return attrs
class UserSerializer(ModelSerializer):
email = EmailField(required=False)
sections = SectionSerializer(many=True)
class Meta:
model = User
fields = ("sections", "email", "is_confirmed", "last_login", "date_joined", 'locked')
class MaskFieldSerializer(serializers.Field):
default_error_messages = {
'max_length': 'Ensure this field has no more than {max_length} characters.',
'interval': 'Ensure this field has valid intervals.',
'overlap': 'Ensure this field does not have overlapping intervals.'
}
def to_representation(self, value):
return map(list, self._make_mask(value))
def to_internal_value(self, value):
if not isinstance(value, list):
value = loads(value)
return self._make_mask(value)
def _make_mask(self, data):
try:
mask = FlashcardMask(data)
except ValueError:
raise serializers.ValidationError("Invalid JSON for MaskField")
except TypeError:
raise serializers.ValidationError("Invalid data for MaskField.")
except OverlapIntervalException:
raise serializers.ValidationError("Invalid intervals for MaskField data.")
if len(mask) > 32:
raise serializers.ValidationError("Too many intervals in the mask.")
return mask
class FlashcardSerializer(ModelSerializer):
is_hidden = SerializerMethodField()
is_in_deck = SerializerMethodField()
is_authored_by_user = SerializerMethodField()
material_week_num = IntegerField(read_only=True)
material_date = DateTimeField(default=now)
mask = MaskFieldSerializer(allow_null=True)
display_mask = SerializerMethodField()
score = FloatField(read_only=True)
def validate_material_date(self, value):
# TODO: make this dynamic
if QUARTER_START <= value <= QUARTER_END:
return value
else:
raise serializers.ValidationError("Material date is outside allowed range for this quarter")
def validate_pushed(self, value):
if value > datetime.now():
raise serializers.ValidationError("Invalid creation date for the Flashcard")
return value
def validate_mask(self, value):
if value is None:
return None
if len(self.initial_data['text']) < value.max_offset():
raise serializers.ValidationError("Mask out of bounds")
return value
def get_is_hidden(self, obj):
if 'user' not in self.context: return False
return obj.is_hidden_from(self.context['user'])
def get_is_in_deck(self, obj):
if 'user' not in self.context: return False
return obj.is_in_deck(self.context['user'])
def get_is_authored_by_user(self, obj):
if 'user' not in self.context: return False
return obj.author == self.context['user']
def get_display_mask(self, obj):
if 'user' in self.context:
userflashcard = UserFlashcard.objects.filter(flashcard=obj, user=self.context['user'])
if userflashcard.exists() and userflashcard.get().mask:
return MaskFieldSerializer().to_representation(userflashcard.get().mask)
return MaskFieldSerializer().to_representation(obj.mask)
class Meta:
model = Flashcard
exclude = 'author', 'previous', 'hide_reason'
class FlashcardUpdateSerializer(serializers.Serializer):
text = CharField(max_length=255, required=False)
material_date = DateTimeField(required=False)
mask = MaskFieldSerializer(required=False)
def validate_material_date(self, date):
if date > QUARTER_END:
raise serializers.ValidationError("Invalid material_date for the flashcard")
return date
def validate(self, attrs):
# 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']):
raise serializers.ValidationError("No new value passed in")
return attrs
class QuizRequestSerializer(serializers.Serializer):
sections = ListField(child=IntegerField(min_value=1), required=False, default=[])
material_date_begin = DateTimeField(default=QUARTER_START)
material_date_end = DateTimeField(default=QUARTER_END)
def update(self, instance, validated_data):
pass
def create(self, validated_data):
return validated_data
def validate_material_date_begin(self, value):
if QUARTER_START <= value <= QUARTER_END:
return value
raise serializers.ValidationError("Invalid begin date for the flashcard range")
def validate_material_date_end(self, value):
if QUARTER_START <= value <= QUARTER_END:
return value
raise serializers.ValidationError("Invalid end date for the flashcard range")
def validate_sections(self, value):
if value is not None and not isinstance(value, Iterable):
raise serializers.ValidationError("Invalid section format. Expecting a list or no value.")
if value is None or len(value) == 0:
return Section.objects.all()
section_filter = Section.objects.filter(pk__in=value)
if not section_filter.exists():
raise serializers.ValidationError("Those aren't valid sections")
return value
def validate(self, attrs):
if attrs['material_date_begin'] > attrs['material_date_end']:
raise serializers.ValidationError("Invalid range")
if 'sections' not in attrs:
attrs['sections'] = self.validate_sections(None)
return attrs
class QuizResponseSerializer(ModelSerializer):
pk = PrimaryKeyRelatedField(queryset=UserFlashcardQuiz.objects.all(), many=True)
section = PrimaryKeyRelatedField(queryset=Section.objects.all())
text = CharField(max_length=255)
mask = ListField(child=IntegerField())
def __init__(self, instance=None, mask=[], data=empty, **kwargs):
super(QuizResponseSerializer, self).__init__(instance=instance, data=data, **kwargs)
self.mask = self._validate_mask(mask)
def to_representation(self, instance):
return {
'pk': instance.pk,
'section': instance.user_flashcard.flashcard.section.pk,
'text': instance.user_flashcard.flashcard.text,
'mask': self.mask
}
def _validate_mask(self, value):
if not isinstance(value, list) and value is not None:
raise serializers.ValidationError("The selected mask has to be a list " + str(value))
if value is None or len(value) == 0:
return []
if len(value) == 2 and (0 <= value[0] and value[1] <= len(self.instance.user_flashcard.flashcard.text)):
return value
raise serializers.ValidationError("Invalid mask for the flashcard")
class Meta:
model = UserFlashcardQuiz
class QuizAnswerRequestSerializer(ModelSerializer):
response = CharField(required=False, max_length=255, help_text="The user's response")
correct = BooleanField(required=False, help_text="The user's self-evaluation of their response")
def __init__(self, instance=None, data=empty, **kwargs):
assert isinstance(instance, UserFlashcardQuiz) or instance is None
super(QuizAnswerRequestSerializer, self).__init__(instance=instance, data=data, **kwargs)
def validate_response(self, response):
if response is None:
return ""
return response
def validate(self, attrs):
if not any(i in attrs for i in ('correct', 'response')):
raise serializers.ValidationError("No data passed in")
if 'response' in attrs and self.instance.response is not None:
raise serializers.ValidationError("You have already sent in a response for this quiz")
if 'correct' in attrs:
if 'response' not in attrs and self.instance.response is None:
raise serializers.ValidationError("You haven't sent in a response yet")
if self.instance.correct is not None:
raise serializers.ValidationError("You have already sent in the user's evaluation")
return attrs
class Meta:
model = UserFlashcardQuiz
exclude = 'blanked_word', 'user_flashcard', 'when'
class SubscribeViewSerializer(Serializer):
registration_id = CharField(allow_blank=False, allow_null=False)