Blame view
flashcards/serializers.py
11.6 KB
4ff4160a6
|
1 2 |
from json import loads from collections import Iterable |
2958a1827
|
3 |
|
72bf5f00c
|
4 |
from django.utils.datetime_safe import datetime |
c1f4d3dea
|
5 |
from django.utils.timezone import now |
a3ae545ef
|
6 |
from flashcards.models import Section, LecturePeriod, User, Flashcard, UserFlashcardQuiz, UserFlashcard |
9a6e3424b
|
7 |
from flashcards.validators import FlashcardMask, OverlapIntervalException |
ce17f969f
|
8 |
from rest_framework import serializers |
ad724d791
|
9 |
from rest_framework.fields import EmailField, BooleanField, CharField, IntegerField, DateTimeField, empty, \ |
55c734526
|
10 |
SerializerMethodField, FloatField |
ee4104aa2
|
11 |
from rest_framework.serializers import ModelSerializer, Serializer, PrimaryKeyRelatedField, ListField |
ce17f969f
|
12 |
from rest_framework.validators import UniqueValidator |
ee4104aa2
|
13 |
from flashy.settings import QUARTER_END, QUARTER_START |
ce17f969f
|
14 15 16 17 |
class EmailSerializer(Serializer): email = EmailField(required=True) |
a7099dd7a
|
18 |
class LoginSerializer(EmailSerializer): |
ce17f969f
|
19 |
password = CharField(required=True) |
a7099dd7a
|
20 |
class RegistrationSerializer(Serializer): |
ce17f969f
|
21 |
email = EmailField(required=True, validators=[UniqueValidator(queryset=User.objects.all())]) |
a7099dd7a
|
22 |
password = CharField(required=True, min_length=8) |
ce17f969f
|
23 24 25 26 27 28 29 30 31 32 33 34 |
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): |
a7099dd7a
|
35 |
new_password = CharField(required=True, allow_blank=False, min_length=8) |
ce17f969f
|
36 37 38 39 40 41 42 43 44 |
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') |
aebf1936d
|
45 |
|
fe546f43f
|
46 47 |
class EmailVerificationSerializer(Serializer): confirmation_key = CharField() |
72bf5f00c
|
48 |
|
aebf1936d
|
49 |
|
ce17f969f
|
50 51 |
class UserUpdateSerializer(Serializer): old_password = CharField(required=False) |
a7099dd7a
|
52 |
new_password = CharField(required=False, allow_blank=False, min_length=8) |
ce17f969f
|
53 54 55 56 57 |
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 |
aebf1936d
|
58 |
|
d38c8b394
|
59 |
class LecturePeriodSerializer(ModelSerializer): |
491577131
|
60 |
class Meta: |
d38c8b394
|
61 62 |
model = LecturePeriod exclude = 'id', 'section' |
491577131
|
63 |
|
72bf5f00c
|
64 |
class SectionSerializer(ModelSerializer): |
fe0d37016
|
65 |
lecture_times = CharField() |
0794ea949
|
66 67 |
short_name = CharField() long_name = CharField() |
4ff4160a6
|
68 |
can_enroll = SerializerMethodField() |
83e4896b0
|
69 |
is_enrolled = SerializerMethodField() |
d38c8b394
|
70 |
|
491577131
|
71 |
class Meta: |
d38c8b394
|
72 |
model = Section |
2dc11d15d
|
73 |
|
4ff4160a6
|
74 75 76 77 |
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']) |
83e4896b0
|
78 79 80 |
def get_is_enrolled(self, obj): if 'user' not in self.context: return False return obj.is_user_enrolled(self.context['user']) |
776577266
|
81 |
|
bd04c9af5
|
82 83 |
class DeepSectionSerializer(SectionSerializer): lectures = LecturePeriodSerializer(source='lectureperiod_set', many=True, read_only=True) |
901ee2fa9
|
84 85 86 87 88 89 90 |
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 |
72bf5f00c
|
91 |
class UserSerializer(ModelSerializer): |
18095ed46
|
92 |
email = EmailField(required=False) |
cec534fd3
|
93 |
sections = SectionSerializer(many=True) |
9367afe1f
|
94 |
|
2dc11d15d
|
95 |
class Meta: |
18095ed46
|
96 |
model = User |
05b5a910c
|
97 |
fields = ("sections", "email", "is_confirmed", "last_login", "date_joined", 'locked') |
2a9edd990
|
98 |
|
72bf5f00c
|
99 |
|
be6cc9169
|
100 101 102 103 104 105 106 107 |
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): |
057d2cc3f
|
108 |
return map(list, self._make_mask(value)) |
be6cc9169
|
109 |
|
9a6e3424b
|
110 |
def to_internal_value(self, value): |
34586f534
|
111 112 113 |
if not isinstance(value, list): value = loads(value) return self._make_mask(value) |
9a6e3424b
|
114 115 |
def _make_mask(self, data): |
be6cc9169
|
116 |
try: |
cb2c2f3fc
|
117 |
mask = FlashcardMask(data) |
be6cc9169
|
118 119 |
except ValueError: raise serializers.ValidationError("Invalid JSON for MaskField") |
9a6e3424b
|
120 121 122 123 124 125 126 |
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 |
be6cc9169
|
127 |
|
72bf5f00c
|
128 |
class FlashcardSerializer(ModelSerializer): |
ad724d791
|
129 |
is_hidden = SerializerMethodField() |
19f62c6f7
|
130 |
is_in_deck = SerializerMethodField() |
4263a9ac9
|
131 |
is_authored_by_user = SerializerMethodField() |
1f74d60b2
|
132 |
material_week_num = IntegerField(read_only=True) |
c1f4d3dea
|
133 |
material_date = DateTimeField(default=now) |
5d861cbfb
|
134 |
mask = MaskFieldSerializer(allow_null=True) |
a3ae545ef
|
135 |
display_mask = SerializerMethodField() |
55c734526
|
136 |
score = FloatField(read_only=True) |
19f62c6f7
|
137 |
|
72bf5f00c
|
138 139 |
def validate_material_date(self, value): # TODO: make this dynamic |
ee4104aa2
|
140 |
if QUARTER_START <= value <= QUARTER_END: |
be6cc9169
|
141 |
return value |
72bf5f00c
|
142 143 |
else: raise serializers.ValidationError("Material date is outside allowed range for this quarter") |
be6cc9169
|
144 145 146 147 |
def validate_pushed(self, value): if value > datetime.now(): raise serializers.ValidationError("Invalid creation date for the Flashcard") return value |
2a9edd990
|
148 |
|
bca16d61f
|
149 |
def validate_mask(self, value): |
5d861cbfb
|
150 151 |
if value is None: return None |
8964ffa26
|
152 |
if len(self.initial_data['text']) < value.max_offset(): |
bca16d61f
|
153 154 |
raise serializers.ValidationError("Mask out of bounds") return value |
ad724d791
|
155 156 157 |
def get_is_hidden(self, obj): if 'user' not in self.context: return False return obj.is_hidden_from(self.context['user']) |
19f62c6f7
|
158 159 160 |
def get_is_in_deck(self, obj): if 'user' not in self.context: return False return obj.is_in_deck(self.context['user']) |
4263a9ac9
|
161 162 163 |
def get_is_authored_by_user(self, obj): if 'user' not in self.context: return False return obj.author == self.context['user'] |
a3ae545ef
|
164 165 166 167 168 169 |
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) |
4263a9ac9
|
170 |
|
be6cc9169
|
171 172 |
class Meta: model = Flashcard |
aebf1936d
|
173 |
exclude = 'author', 'previous', 'hide_reason' |
bca16d61f
|
174 175 176 |
class FlashcardUpdateSerializer(serializers.Serializer): |
8964ffa26
|
177 178 179 |
text = CharField(max_length=255, required=False) material_date = DateTimeField(required=False) mask = MaskFieldSerializer(required=False) |
bca16d61f
|
180 181 |
def validate_material_date(self, date): |
ee4104aa2
|
182 |
if date > QUARTER_END: |
bca16d61f
|
183 184 |
raise serializers.ValidationError("Invalid material_date for the flashcard") return date |
f0b284adb
|
185 186 187 188 189 |
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") |
ee4104aa2
|
190 191 192 193 |
return attrs class QuizRequestSerializer(serializers.Serializer): |
ff1c4d11d
|
194 |
sections = ListField(child=IntegerField(min_value=1), required=False, default=[]) |
ee4104aa2
|
195 196 |
material_date_begin = DateTimeField(default=QUARTER_START) material_date_end = DateTimeField(default=QUARTER_END) |
f66f9ca7d
|
197 198 |
def update(self, instance, validated_data): pass |
ee4104aa2
|
199 200 |
def create(self, validated_data): |
f66f9ca7d
|
201 |
return validated_data |
ee4104aa2
|
202 203 204 205 206 207 208 209 210 211 212 213 |
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): |
d1312d97d
|
214 215 216 |
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: |
f66f9ca7d
|
217 |
return Section.objects.all() |
ee4104aa2
|
218 219 |
section_filter = Section.objects.filter(pk__in=value) if not section_filter.exists(): |
f66f9ca7d
|
220 221 |
raise serializers.ValidationError("Those aren't valid sections") return value |
ee4104aa2
|
222 223 224 225 226 227 |
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) |
ee4104aa2
|
228 229 230 231 232 233 234 235 |
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()) |
177681149
|
236 |
def __init__(self, instance=None, mask=[], data=empty, **kwargs): |
ee4104aa2
|
237 238 239 240 241 |
super(QuizResponseSerializer, self).__init__(instance=instance, data=data, **kwargs) self.mask = self._validate_mask(mask) def to_representation(self, instance): return { |
8806ae51b
|
242 |
'pk': instance.pk, |
ee4104aa2
|
243 244 245 246 247 248 |
'section': instance.user_flashcard.flashcard.section.pk, 'text': instance.user_flashcard.flashcard.text, 'mask': self.mask } def _validate_mask(self, value): |
f66f9ca7d
|
249 250 |
if not isinstance(value, list) and value is not None: raise serializers.ValidationError("The selected mask has to be a list " + str(value)) |
ee4104aa2
|
251 252 |
if value is None or len(value) == 0: return [] |
177681149
|
253 |
if len(value) == 2 and (0 <= value[0] and value[1] <= len(self.instance.user_flashcard.flashcard.text)): |
ee4104aa2
|
254 255 256 257 258 259 260 261 262 263 |
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") |
1eb9b97ca
|
264 |
def __init__(self, instance=None, data=empty, **kwargs): |
34586f534
|
265 |
assert isinstance(instance, UserFlashcardQuiz) or instance is None |
1eb9b97ca
|
266 |
super(QuizAnswerRequestSerializer, self).__init__(instance=instance, data=data, **kwargs) |
ee4104aa2
|
267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 |
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' |
f6900af2c
|
288 289 290 |
class SubscribeViewSerializer(Serializer): |
25c6d6973
|
291 |
registration_id = CharField(allow_blank=False, allow_null=False) |