diff --git a/flashcards/models.py b/flashcards/models.py index a8e3cd9..ef408f8 100644 --- a/flashcards/models.py +++ b/flashcards/models.py @@ -12,11 +12,12 @@ from django.db import IntegrityError from django.db.models import * from django.utils.log import getLogger from django.utils.timezone import now, make_aware -from flashy.settings import QUARTER_START +from flashy.settings import QUARTER_START, QUARTER_END from simple_email_confirmation import SimpleEmailConfirmationUserMixin from fields import MaskField from cached_property import cached_property from flashy.settings import IN_PRODUCTION +from math import e # Hack to fix AbstractUser before subclassing it @@ -138,6 +139,30 @@ class User(AbstractUser, SimpleEmailConfirmationUserMixin): self.confirmed_email = True self.save() + def by_retention(self, sections, material_date_begin, material_date_end): + section_pks = map(lambda i: i['pk'], sections.values('pk')) + user_flashcard_filter = UserFlashcard.objects.filter( + user=self, flashcard__section__pk__in=section_pks, + flashcard__material_date__gte=material_date_begin, + flashcard__material_date__lte=material_date_end + ) + + if not user_flashcard_filter.exists(): + raise ValidationError("No matching flashcard found in your decks") + + return user_flashcard_filter.prefetch_related('userflashcardquiz_set').annotate( + study_count=Count('pk'), + days_since=Case( + When(userflashcardquiz__when=None, then=interval_days(Now(), F('pulled'))), + default=interval_days(Now(), Max('userflashcardquiz__when')), + output_field=FloatField() + ), + retention_score=Case( + default=Value(e, output_field=FloatField()) ** (F('days_since')*(-0.1/(F('study_count')+1))), + output_field=FloatField() + ) + ).order_by('retention_score') + class UserFlashcard(Model): """ diff --git a/flashcards/views.py b/flashcards/views.py index 17e04ec..82d4dbe 100644 --- a/flashcards/views.py +++ b/flashcards/views.py @@ -19,13 +19,10 @@ from rest_framework.viewsets import ReadOnlyModelViewSet, GenericViewSet from django.core.mail import send_mail from django.contrib.auth import authenticate from django.contrib.auth.tokens import default_token_generator -from django.db.models import Count, Max, F, Value, When, Case, DateTimeField, FloatField from rest_framework.status import HTTP_204_NO_CONTENT, HTTP_201_CREATED, HTTP_200_OK from rest_framework.response import Response from rest_framework.exceptions import AuthenticationFailed, NotAuthenticated, ValidationError, PermissionDenied from simple_email_confirmation import EmailAddress -from math import e -from django.utils.timezone import now def log_event(request, event=''): @@ -394,38 +391,16 @@ class UserFlashcardQuizViewSet(GenericViewSet, CreateModelMixin, UpdateModelMixi serializer = QuizRequestSerializer(data=request.data) serializer.is_valid(raise_exception=True) data = serializer.validated_data - user_flashcard_filter = UserFlashcard.objects.filter( - user=request.user, flashcard__section__pk__in=data['sections'], - flashcard__material_date__gte=data['material_date_begin'], - flashcard__material_date__lte=data['material_date_end'] - ) - - if not user_flashcard_filter.exists(): - raise ValidationError("No matching flashcard found in your decks") - - quiz_filter = user_flashcard_filter.prefetch_related('userflashcardquiz_set').annotate( - study_count=Count('pk'), - days_since=Case( - When(userflashcardquiz__when=None, then=interval_days(Now(), F('pulled'))), - default=interval_days(Now(), Max('userflashcardquiz__when')), - output_field=FloatField() - ), - retention_score=Case( - default=Value(e, output_field=FloatField()) ** (F('days_since')*(-0.1/(F('study_count')+1))), - output_field=FloatField() - ) - ).order_by('retention_score') - - user_flashcard = quiz_filter.first() + + user_flashcard = request.user.by_retention(**data).first() mask = user_flashcard.get_mask().get_random_blank() - if not mask: - blanked_word = "" - log_event(request, mask) - else: + blanked_word = "" + if mask: blanked_word = user_flashcard.flashcard.text[slice(*mask)] - user_flashcard_quiz = UserFlashcardQuiz(user_flashcard=user_flashcard, - blanked_word=blanked_word) + + user_flashcard_quiz = UserFlashcardQuiz(user_flashcard=user_flashcard, blanked_word=blanked_word) user_flashcard_quiz.save() + response = QuizResponseSerializer(instance=user_flashcard_quiz, mask=mask) log_event(request, response) return Response(response.data, status=HTTP_200_OK)