Commit f66f9ca7d57d78ac11fbdd127116c521b15c8e68
1 parent
512a58de33
Exists in
master
Fixed SwaggerUI problem with /study/
Showing 7 changed files with 71 additions and 47 deletions Side-by-side Diff
flashcards/serializers.py
View file @
f66f9ca
... | ... | @@ -166,36 +166,16 @@ |
166 | 166 | |
167 | 167 | |
168 | 168 | class QuizRequestSerializer(serializers.Serializer): |
169 | - # sections = PrimaryKeyRelatedField(queryset=Section.objects.all(),required=False, many=True) | |
170 | 169 | sections = ListField(child=IntegerField(min_value=1), required=False) |
171 | 170 | material_date_begin = DateTimeField(default=QUARTER_START) |
172 | 171 | material_date_end = DateTimeField(default=QUARTER_END) |
173 | 172 | |
174 | - def __init__(self, user, instance=None, data=empty, **kwargs): | |
175 | - assert instance is not None or data is not empty | |
176 | - super(QuizRequestSerializer, self).__init__(instance=instance, data=data, **kwargs) | |
177 | - self.user = user | |
178 | - self.user_flashcard = None | |
173 | + def update(self, instance, validated_data): | |
174 | + pass | |
179 | 175 | |
180 | 176 | def create(self, validated_data): |
181 | - return UserFlashcardQuiz.objects.create(user_flashcard=self.user_flashcard) | |
177 | + return validated_data | |
182 | 178 | |
183 | - def update(self, instance, validated_data): | |
184 | - for attr in validated_data: | |
185 | - setattr(instance, attr, validated_data[attr]) | |
186 | - instance.save() | |
187 | - return instance | |
188 | - | |
189 | - def _get_user_flashcard(self, attrs): | |
190 | - user_flashcard_filter = UserFlashcard.objects.filter( | |
191 | - user=self.user, flashcard__section__in=attrs['sections'], | |
192 | - flashcard__material_date__gte=attrs['material_date_begin'], | |
193 | - flashcard__material_date__lte=attrs['material_date_end'] | |
194 | - ) | |
195 | - if not user_flashcard_filter.exists(): | |
196 | - raise serializers.ValidationError("Your deck for that section is empty") | |
197 | - self.user_flashcard = user_flashcard_filter.order_by('?').first() | |
198 | - | |
199 | 179 | def validate_material_date_begin(self, value): |
200 | 180 | if QUARTER_START <= value <= QUARTER_END: |
201 | 181 | return value |
202 | 182 | |
203 | 183 | |
... | ... | @@ -208,18 +188,17 @@ |
208 | 188 | |
209 | 189 | def validate_sections(self, value): |
210 | 190 | if value is None: |
211 | - return self.user.sections | |
191 | + return Section.objects.all() | |
212 | 192 | section_filter = Section.objects.filter(pk__in=value) |
213 | 193 | if not section_filter.exists(): |
214 | - raise serializers.ValidationError("You aren't enrolled in those section(s)") | |
215 | - return section_filter | |
194 | + raise serializers.ValidationError("Those aren't valid sections") | |
195 | + return value | |
216 | 196 | |
217 | 197 | def validate(self, attrs): |
218 | 198 | if attrs['material_date_begin'] > attrs['material_date_end']: |
219 | 199 | raise serializers.ValidationError("Invalid range") |
220 | 200 | if 'sections' not in attrs: |
221 | 201 | attrs['sections'] = self.validate_sections(None) |
222 | - self._get_user_flashcard(attrs) | |
223 | 202 | return attrs |
224 | 203 | |
225 | 204 | |
... | ... | @@ -242,8 +221,8 @@ |
242 | 221 | } |
243 | 222 | |
244 | 223 | def _validate_mask(self, value): |
245 | - if not isinstance(value, tuple) and value is not None: | |
246 | - raise serializers.ValidationError("The selected mask has to be a list") | |
224 | + if not isinstance(value, list) and value is not None: | |
225 | + raise serializers.ValidationError("The selected mask has to be a list " + str(value)) | |
247 | 226 | if value is None or len(value) == 0: |
248 | 227 | return [] |
249 | 228 | if len(value) == 2 and (0 <= value[0] and value[1] <= len(self.instance.user_flashcard.flashcard.text)): |
flashcards/tests/test_api.py
View file @
f66f9ca
... | ... | @@ -3,7 +3,7 @@ |
3 | 3 | from rest_framework.status import HTTP_204_NO_CONTENT, HTTP_201_CREATED, HTTP_200_OK, HTTP_403_FORBIDDEN |
4 | 4 | from rest_framework.test import APITestCase |
5 | 5 | from re import search |
6 | -import datetime | |
6 | +from datetime import datetime | |
7 | 7 | from django.utils.timezone import now |
8 | 8 | from flashcards.validators import FlashcardMask |
9 | 9 | from flashcards.serializers import FlashcardSerializer |
... | ... | @@ -220,7 +220,7 @@ |
220 | 220 | response = self.client.patch(url, data, format='json') |
221 | 221 | self.assertEqual(response.status_code, HTTP_200_OK) |
222 | 222 | self.assertEqual(response.data['text'], data['text']) |
223 | - data = {'material_date': datetime.datetime(2015, 4, 12, 2, 2, 2), | |
223 | + data = {'material_date': datetime(2015, 4, 12, 2, 2, 2), | |
224 | 224 | 'mask': '[[1, 3]]'} |
225 | 225 | user2 = User.objects.create(email='wow@wow.wow', password='wow') |
226 | 226 | user2.sections.add(self.section) |
227 | 227 | |
... | ... | @@ -378,10 +378,23 @@ |
378 | 378 | self.user = User.objects.get(email='none@none.com') |
379 | 379 | self.section = Section.objects.get(pk=1) |
380 | 380 | self.flashcard = Flashcard(text="This is a flashcard", section=self.section, material_date=now(), |
381 | - author=self.user, mask={(0,4), (5,7)}) | |
381 | + author=self.user, mask={(0, 4), (5, 7)}) | |
382 | 382 | self.flashcard.save() |
383 | 383 | self.flashcard.refresh_from_db() |
384 | 384 | self.user_flashcard = UserFlashcard(flashcard=self.flashcard, user=self.user, |
385 | 385 | mask=self.flashcard.mask, pulled=datetime.now()) |
386 | 386 | self.user_flashcard.save() |
387 | + | |
388 | + def test_quiz_create(self): | |
389 | + url = '/api/study/' | |
390 | + data = {'section': 1} | |
391 | + response = self.client.post(url, data, format='json') | |
392 | + self.assertEqual(response.status_code, HTTP_200_OK) | |
393 | + self.assertEqual(response.data['pk'], 1) | |
394 | + self.assertEqual(response.data['section'], self.section.pk) | |
395 | + self.assertEqual(response.data['text'], self.flashcard.text) | |
396 | + self.assertIn(response.data['mask'], [[0, 4], [5, 7]]) | |
397 | + | |
398 | + def test_quiz_response(self): | |
399 | + |
flashcards/tests/test_models.py
View file @
f66f9ca
... | ... | @@ -179,12 +179,23 @@ |
179 | 179 | |
180 | 180 | def test_quiz_request(self): |
181 | 181 | data = {'sections': [1], 'material_date_begin': QUARTER_START, 'material_date_end': QUARTER_END} |
182 | - serializer = QuizRequestSerializer(user=self.user, data=data) | |
182 | + serializer = QuizRequestSerializer(data=data) | |
183 | 183 | serializer.is_valid(raise_exception=True) |
184 | - user_flashcard_quiz = serializer.create(serializer.validated_data) | |
184 | + validated_data = serializer.create(serializer.validated_data) | |
185 | + user_flashcard = UserFlashcard.objects.filter(user=self.user, | |
186 | + flashcard__section__pk__in=validated_data['sections'], | |
187 | + flashcard__material_date__gte=validated_data['material_date_begin'], | |
188 | + flashcard__material_date__lte=validated_data['material_date_end']) | |
189 | + self.assertTrue(user_flashcard.exists()) | |
190 | + user_flashcard = user_flashcard.first() | |
191 | + self.assertEqual(user_flashcard, self.user_flashcard) | |
192 | + mask = user_flashcard.mask.get_random_blank() | |
193 | + word = user_flashcard.flashcard.text[slice(*mask)] | |
194 | + user_flashcard_quiz = UserFlashcardQuiz(user_flashcard=user_flashcard, | |
195 | + blanked_word=word) | |
196 | + user_flashcard_quiz.save() | |
185 | 197 | self.assertTrue(isinstance(user_flashcard_quiz, UserFlashcardQuiz)) |
186 | - mask = user_flashcard_quiz.user_flashcard.mask.get_random_blank() | |
187 | - self.assertIn(mask, [(24, 33), (0, 4)]) | |
198 | + self.assertIn(mask, [[24, 33], [0, 4]]) | |
188 | 199 | user_flashcard_quiz.blanked_word = user_flashcard_quiz.user_flashcard.flashcard.text[slice(*mask)] |
189 | 200 | self.assertIn(user_flashcard_quiz.blanked_word, ["This", "Flashcard"]) |
190 | 201 | user_flashcard_quiz.save() |
flashcards/validators.py
View file @
f66f9ca
... | ... | @@ -17,8 +17,8 @@ |
17 | 17 | |
18 | 18 | def get_random_blank(self): |
19 | 19 | if self.max_offset() > 0: |
20 | - return sample(self, 1)[0] | |
21 | - return () | |
20 | + return list(sample(self, 1)[0]) | |
21 | + return [] | |
22 | 22 | |
23 | 23 | def _iterable_check(self, iterable): |
24 | 24 | if not isinstance(iterable, Iterable) or not all([isinstance(i, Iterable) for i in iterable]): |
flashcards/views.py
View file @
f66f9ca
... | ... | @@ -4,7 +4,7 @@ |
4 | 4 | from django.contrib import auth |
5 | 5 | from django.shortcuts import get_object_or_404 |
6 | 6 | from flashcards.api import StandardResultsSetPagination, IsEnrolledInAssociatedSection, IsFlashcardReviewer |
7 | -from flashcards.models import Section, User, Flashcard, FlashcardHide, UserFlashcardQuiz | |
7 | +from flashcards.models import Section, User, Flashcard, FlashcardHide, UserFlashcard, UserFlashcardQuiz | |
8 | 8 | from flashcards.notifications import notify_new_card |
9 | 9 | from flashcards.serializers import SectionSerializer, UserUpdateSerializer, RegistrationSerializer, UserSerializer, \ |
10 | 10 | PasswordResetSerializer, PasswordResetRequestSerializer, EmailPasswordSerializer, FlashcardSerializer, \ |
11 | 11 | |
12 | 12 | |
13 | 13 | |
14 | 14 | |
15 | 15 | |
16 | 16 | |
... | ... | @@ -343,24 +343,43 @@ |
343 | 343 | |
344 | 344 | |
345 | 345 | class UserFlashcardQuizViewSet(GenericViewSet, CreateModelMixin, UpdateModelMixin): |
346 | - queryset = UserFlashcardQuiz.objects.all() | |
347 | - serializer_class = QuizAnswerRequestSerializer | |
348 | 346 | permission_classes = [IsAuthenticated, IsFlashcardReviewer] |
347 | + queryset = UserFlashcardQuiz.objects.all() | |
349 | 348 | |
349 | + def get_serializer_class(self): | |
350 | + if self.request.method == 'POST': | |
351 | + return QuizRequestSerializer | |
352 | + return QuizAnswerRequestSerializer | |
353 | + | |
350 | 354 | def create(self, request, *args, **kwargs): |
351 | 355 | """ |
352 | 356 | Return a card based on the request params. |
353 | 357 | :param request: A request object. |
354 | 358 | :param format: Format of the request. |
355 | 359 | :return: A response containing |
360 | + request_serializer: serializers.QuizRequestSerializer | |
361 | + response_serializer: serializers.QuizResponseSerializer | |
356 | 362 | """ |
357 | - serializer = QuizRequestSerializer(user=request.user, data=request.data) | |
363 | + serializer = QuizRequestSerializer(data=request.data) | |
358 | 364 | serializer.is_valid(raise_exception=True) |
359 | - user_flashcard_quiz = serializer.create(serializer.validated_data) | |
360 | - mask = sample(user_flashcard_quiz.user_flashcard.mask.get_random_blank(), 1) | |
361 | - user_flashcard_quiz.blanked_word = user_flashcard_quiz.user_flashcard.flashcard.text[slice(*mask)] | |
365 | + data = serializer.validated_data | |
366 | + user_flashcard_filter = UserFlashcard.objects.filter( | |
367 | + user=request.user, flashcard__section__pk__in=data['sections'], | |
368 | + flashcard__material_date__gte=data['material_date_begin'], | |
369 | + flashcard__material_date__lte=data['material_date_end'] | |
370 | + ) | |
371 | + | |
372 | + if not user_flashcard_filter.exists(): | |
373 | + raise ValidationError("No matching flashcard found in your decks") | |
374 | + | |
375 | + user_flashcard = user_flashcard_filter.order_by('?').first() | |
376 | + mask = user_flashcard.mask.get_random_blank() | |
377 | + user_flashcard_quiz = UserFlashcardQuiz(user_flashcard=user_flashcard, | |
378 | + blanked_word=user_flashcard.flashcard.text[slice(*mask)]) | |
362 | 379 | user_flashcard_quiz.save() |
363 | - return Response(QuizResponseSerializer(instance=user_flashcard_quiz, mask=mask).data, status=HTTP_200_OK) | |
380 | + assert user_flashcard_quiz is not None | |
381 | + response = QuizResponseSerializer(instance=user_flashcard_quiz, mask=mask) | |
382 | + return Response(response.data, status=HTTP_200_OK) | |
364 | 383 | |
365 | 384 | def partial_update(self, request, *args, **kwargs): |
366 | 385 | """ |
... | ... | @@ -368,6 +387,7 @@ |
368 | 387 | :param request: A request object. |
369 | 388 | :param format: Format of the request. |
370 | 389 | :return: A response containing |
390 | + request_serializer: serializers.QuizAnswerRequestSerializer | |
371 | 391 | """ |
372 | 392 | user_flashcard_quiz = self.get_object() |
373 | 393 | serializer = QuizAnswerRequestSerializer(instance=user_flashcard_quiz, data=request.data) |
requirements.txt
View file @
f66f9ca
scripts/run_production.sh
View file @
f66f9ca
... | ... | @@ -4,4 +4,5 @@ |
4 | 4 | # newrelic-admin run-program /srv/flashy-backend/venv/bin/gunicorn --pid /run/flashy/gunicorn.pid -w 6 -n flashy -b 127.0.0.1:7002 flashy.wsgi |
5 | 5 | uwsgi /etc/uwsgi/websocket.ini --touch-reload=/etc/uwsgi/websocket.ini & |
6 | 6 | newrelic-admin run-program uwsgi /etc/uwsgi/flashy.ini --touch-reload=/etc/uwsgi/flashy.ini |
7 | +trap 'kill $(jobs -pr)' SIGINT SIGTERM EXIT |