Blame view

flashcards/serializers.py 11.6 KB
4ff4160a6   Andrew Buss   include can_enrol...
1
2
  from json import loads
  from collections import Iterable
2958a1827   Andrew Buss   cleaned up some d...
3

72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
4
  from django.utils.datetime_safe import datetime
c1f4d3dea   Andrew Buss   Fix section flash...
5
  from django.utils.timezone import now
a3ae545ef   Andrew Buss   send user's chose...
6
  from flashcards.models import Section, LecturePeriod, User, Flashcard, UserFlashcardQuiz, UserFlashcard
9a6e3424b   Rohan Rangray   Wrote the MaskFie...
7
  from flashcards.validators import FlashcardMask, OverlapIntervalException
ce17f969f   Andrew Buss   Restructured api,...
8
  from rest_framework import serializers
ad724d791   Andrew Buss   is_hidden specifi...
9
  from rest_framework.fields import EmailField, BooleanField, CharField, IntegerField, DateTimeField, empty, \
55c734526   Andrew Buss   send floats for s...
10
      SerializerMethodField, FloatField
ee4104aa2   Rohan Rangray   Added a Study vie...
11
  from rest_framework.serializers import ModelSerializer, Serializer, PrimaryKeyRelatedField, ListField
ce17f969f   Andrew Buss   Restructured api,...
12
  from rest_framework.validators import UniqueValidator
ee4104aa2   Rohan Rangray   Added a Study vie...
13
  from flashy.settings import QUARTER_END, QUARTER_START
ce17f969f   Andrew Buss   Restructured api,...
14
15
16
17
  
  
  class EmailSerializer(Serializer):
      email = EmailField(required=True)
a7099dd7a   Andrew Buss   password minimum ...
18
  class LoginSerializer(EmailSerializer):
ce17f969f   Andrew Buss   Restructured api,...
19
      password = CharField(required=True)
a7099dd7a   Andrew Buss   password minimum ...
20
  class RegistrationSerializer(Serializer):
ce17f969f   Andrew Buss   Restructured api,...
21
      email = EmailField(required=True, validators=[UniqueValidator(queryset=User.objects.all())])
a7099dd7a   Andrew Buss   password minimum ...
22
      password = CharField(required=True, min_length=8)
ce17f969f   Andrew Buss   Restructured api,...
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   Andrew Buss   password minimum ...
35
      new_password = CharField(required=True, allow_blank=False, min_length=8)
ce17f969f   Andrew Buss   Restructured api,...
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   Andrew Buss   remove hide_reaso...
45

fe546f43f   Andrew Buss   move verify_email...
46
47
  class EmailVerificationSerializer(Serializer):
      confirmation_key = CharField()
72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
48

aebf1936d   Andrew Buss   remove hide_reaso...
49

ce17f969f   Andrew Buss   Restructured api,...
50
51
  class UserUpdateSerializer(Serializer):
      old_password = CharField(required=False)
a7099dd7a   Andrew Buss   password minimum ...
52
      new_password = CharField(required=False, allow_blank=False, min_length=8)
ce17f969f   Andrew Buss   Restructured api,...
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   Andrew Buss   remove hide_reaso...
58

d38c8b394   Andrew Buss   Made lecture sect...
59
  class LecturePeriodSerializer(ModelSerializer):
491577131   Andrew Buss   Class -> section....
60
      class Meta:
d38c8b394   Andrew Buss   Made lecture sect...
61
62
          model = LecturePeriod
          exclude = 'id', 'section'
491577131   Andrew Buss   Class -> section....
63

72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
64
  class SectionSerializer(ModelSerializer):
fe0d37016   Andrew Buss   changed some thin...
65
      lecture_times = CharField()
0794ea949   Andrew Buss   Improved search f...
66
67
      short_name = CharField()
      long_name = CharField()
4ff4160a6   Andrew Buss   include can_enrol...
68
      can_enroll = SerializerMethodField()
83e4896b0   Andrew Buss   include is_enroll...
69
      is_enrolled = SerializerMethodField()
d38c8b394   Andrew Buss   Made lecture sect...
70

491577131   Andrew Buss   Class -> section....
71
      class Meta:
d38c8b394   Andrew Buss   Made lecture sect...
72
          model = Section
2dc11d15d   Laura Hawkins   password and user...
73

4ff4160a6   Andrew Buss   include can_enrol...
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   Andrew Buss   include is_enroll...
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   Andrew Buss   websockets notifi...
81

bd04c9af5   Andrew Buss   Only retrieve lec...
82
83
  class DeepSectionSerializer(SectionSerializer):
      lectures = LecturePeriodSerializer(source='lectureperiod_set', many=True, read_only=True)
901ee2fa9   Rohan Rangray   Added pagination ...
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   Andrew Buss   Falcon puuuuuuuus...
91
  class UserSerializer(ModelSerializer):
18095ed46   Andrew Buss   Integrated django...
92
      email = EmailField(required=False)
cec534fd3   Andrew Buss   moved more view l...
93
      sections = SectionSerializer(many=True)
9367afe1f   Andrew Buss   Added queryset fo...
94

2dc11d15d   Laura Hawkins   password and user...
95
      class Meta:
18095ed46   Andrew Buss   Integrated django...
96
          model = User
05b5a910c   Andrew Buss   include locked fi...
97
          fields = ("sections", "email", "is_confirmed", "last_login", "date_joined", 'locked')
2a9edd990   Chung Wang   Flashcard detail ...
98

72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
99

be6cc9169   Rohan Rangray   Fixed a bug in th...
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   Rohan Rangray   Attempted fix for...
108
          return map(list, self._make_mask(value))
be6cc9169   Rohan Rangray   Fixed a bug in th...
109

9a6e3424b   Rohan Rangray   Wrote the MaskFie...
110
      def to_internal_value(self, value):
34586f534   Rohan Rangray   Fixed MaskFieldSe...
111
112
113
          if not isinstance(value, list):
              value = loads(value)
          return self._make_mask(value)
9a6e3424b   Rohan Rangray   Wrote the MaskFie...
114
115
  
      def _make_mask(self, data):
be6cc9169   Rohan Rangray   Fixed a bug in th...
116
          try:
cb2c2f3fc   Rohan Rangray   Fixed FlashcardMa...
117
              mask = FlashcardMask(data)
be6cc9169   Rohan Rangray   Fixed a bug in th...
118
119
          except ValueError:
              raise serializers.ValidationError("Invalid JSON for MaskField")
9a6e3424b   Rohan Rangray   Wrote the MaskFie...
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   Rohan Rangray   Fixed a bug in th...
127

72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
128
  class FlashcardSerializer(ModelSerializer):
ad724d791   Andrew Buss   is_hidden specifi...
129
      is_hidden = SerializerMethodField()
19f62c6f7   Andrew Buss   add is_in_deck fi...
130
      is_in_deck = SerializerMethodField()
4263a9ac9   Andrew Buss   send is_authored_...
131
      is_authored_by_user = SerializerMethodField()
1f74d60b2   Andrew Buss   add material week...
132
      material_week_num = IntegerField(read_only=True)
c1f4d3dea   Andrew Buss   Fix section flash...
133
      material_date = DateTimeField(default=now)
5d861cbfb   Rohan Rangray   Wrote tests for F...
134
      mask = MaskFieldSerializer(allow_null=True)
a3ae545ef   Andrew Buss   send user's chose...
135
      display_mask = SerializerMethodField()
55c734526   Andrew Buss   send floats for s...
136
      score = FloatField(read_only=True)
19f62c6f7   Andrew Buss   add is_in_deck fi...
137

72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
138
139
      def validate_material_date(self, value):
          # TODO: make this dynamic
ee4104aa2   Rohan Rangray   Added a Study vie...
140
          if QUARTER_START <= value <= QUARTER_END:
be6cc9169   Rohan Rangray   Fixed a bug in th...
141
              return value
72bf5f00c   Andrew Buss   Falcon puuuuuuuus...
142
143
          else:
              raise serializers.ValidationError("Material date is outside allowed range for this quarter")
be6cc9169   Rohan Rangray   Fixed a bug in th...
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   Chung Wang   Flashcard detail ...
148

bca16d61f   Rohan Rangray   Added the patch m...
149
      def validate_mask(self, value):
5d861cbfb   Rohan Rangray   Wrote tests for F...
150
151
          if value is None:
              return None
8964ffa26   Rohan Rangray   Corrected Flashca...
152
          if len(self.initial_data['text']) < value.max_offset():
bca16d61f   Rohan Rangray   Added the patch m...
153
154
              raise serializers.ValidationError("Mask out of bounds")
          return value
ad724d791   Andrew Buss   is_hidden specifi...
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   Andrew Buss   add is_in_deck fi...
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   Andrew Buss   send is_authored_...
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   Andrew Buss   send user's chose...
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   Andrew Buss   send is_authored_...
170

be6cc9169   Rohan Rangray   Fixed a bug in th...
171
172
      class Meta:
          model = Flashcard
aebf1936d   Andrew Buss   remove hide_reaso...
173
          exclude = 'author', 'previous', 'hide_reason'
bca16d61f   Rohan Rangray   Added the patch m...
174
175
176
  
  
  class FlashcardUpdateSerializer(serializers.Serializer):
8964ffa26   Rohan Rangray   Corrected Flashca...
177
178
179
      text = CharField(max_length=255, required=False)
      material_date = DateTimeField(required=False)
      mask = MaskFieldSerializer(required=False)
bca16d61f   Rohan Rangray   Added the patch m...
180
181
  
      def validate_material_date(self, date):
ee4104aa2   Rohan Rangray   Added a Study vie...
182
          if date > QUARTER_END:
bca16d61f   Rohan Rangray   Added the patch m...
183
184
              raise serializers.ValidationError("Invalid material_date for the flashcard")
          return date
f0b284adb   Rohan Rangray   Refactored Flashc...
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   Rohan Rangray   Added a Study vie...
190
191
192
193
          return attrs
  
  
  class QuizRequestSerializer(serializers.Serializer):
ff1c4d11d   Rohan Rangray   Fixed bug in by_r...
194
      sections = ListField(child=IntegerField(min_value=1), required=False, default=[])
ee4104aa2   Rohan Rangray   Added a Study vie...
195
196
      material_date_begin = DateTimeField(default=QUARTER_START)
      material_date_end = DateTimeField(default=QUARTER_END)
f66f9ca7d   Rohan Rangray   Fixed SwaggerUI p...
197
198
      def update(self, instance, validated_data):
          pass
ee4104aa2   Rohan Rangray   Added a Study vie...
199
200
  
      def create(self, validated_data):
f66f9ca7d   Rohan Rangray   Fixed SwaggerUI p...
201
          return validated_data
ee4104aa2   Rohan Rangray   Added a Study vie...
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   Rohan Rangray   Added better erro...
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   Rohan Rangray   Fixed SwaggerUI p...
217
              return Section.objects.all()
ee4104aa2   Rohan Rangray   Added a Study vie...
218
219
          section_filter = Section.objects.filter(pk__in=value)
          if not section_filter.exists():
f66f9ca7d   Rohan Rangray   Fixed SwaggerUI p...
220
221
              raise serializers.ValidationError("Those aren't valid sections")
          return value
ee4104aa2   Rohan Rangray   Added a Study vie...
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   Rohan Rangray   Added a Study vie...
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   Rohan Rangray   Fixed the Study-r...
236
      def __init__(self, instance=None, mask=[], data=empty, **kwargs):
ee4104aa2   Rohan Rangray   Added a Study vie...
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   Rohan Rangray   Fixed stupid bug :(
242
              'pk': instance.pk,
ee4104aa2   Rohan Rangray   Added a Study vie...
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   Rohan Rangray   Fixed SwaggerUI p...
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   Rohan Rangray   Added a Study vie...
251
252
          if value is None or len(value) == 0:
              return []
177681149   Rohan Rangray   Fixed the Study-r...
253
          if len(value) == 2 and (0 <= value[0] and value[1] <= len(self.instance.user_flashcard.flashcard.text)):
ee4104aa2   Rohan Rangray   Added a Study vie...
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   Rohan Rangray   Fixed QuizRequest...
264
      def __init__(self, instance=None, data=empty, **kwargs):
34586f534   Rohan Rangray   Fixed MaskFieldSe...
265
          assert isinstance(instance, UserFlashcardQuiz) or instance is None
1eb9b97ca   Rohan Rangray   Fixed QuizRequest...
266
          super(QuizAnswerRequestSerializer, self).__init__(instance=instance, data=data, **kwargs)
ee4104aa2   Rohan Rangray   Added a Study vie...
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   Rohan Rangray   Added things to c...
288
289
290
  
  
  class SubscribeViewSerializer(Serializer):
25c6d6973   Rohan Rangray   Changed registrat...
291
      registration_id = CharField(allow_blank=False, allow_null=False)