Commit 1eebdbcc41fe5aff04c6fb4b0bf96bed29643c3e

Authored by Andrew Buss
Exists in master

merge

Showing 2 changed files Side-by-side Diff

flashcards/models.py View file @ 1eebdbc
... ... @@ -11,11 +11,12 @@
11 11 from django.db import IntegrityError
12 12 from django.db.models import *
13 13 from django.utils.timezone import now, make_aware
14   -from flashy.settings import QUARTER_START
  14 +from flashy.settings import QUARTER_START, QUARTER_END
15 15 from simple_email_confirmation import SimpleEmailConfirmationUserMixin
16 16 from fields import MaskField
17 17 from cached_property import cached_property
18 18 from flashy.settings import IN_PRODUCTION
  19 +from math import e
19 20  
20 21  
21 22 # Hack to fix AbstractUser before subclassing it
... ... @@ -137,6 +138,30 @@
137 138 self.email_address_set.confirm(confirmation_key, save=True)
138 139 self.confirmed_email = True
139 140 self.save()
  141 +
  142 + def by_retention(self, sections, material_date_begin, material_date_end):
  143 + section_pks = map(lambda i: i['pk'], sections.values('pk'))
  144 + user_flashcard_filter = UserFlashcard.objects.filter(
  145 + user=self, flashcard__section__pk__in=section_pks,
  146 + flashcard__material_date__gte=material_date_begin,
  147 + flashcard__material_date__lte=material_date_end
  148 + )
  149 +
  150 + if not user_flashcard_filter.exists():
  151 + raise ValidationError("No matching flashcard found in your decks")
  152 +
  153 + return user_flashcard_filter.prefetch_related('userflashcardquiz_set').annotate(
  154 + study_count=Count('pk'),
  155 + days_since=Case(
  156 + When(userflashcardquiz__when=None, then=interval_days(Now(), F('pulled'))),
  157 + default=interval_days(Now(), Max('userflashcardquiz__when')),
  158 + output_field=FloatField()
  159 + ),
  160 + retention_score=Case(
  161 + default=Value(e, output_field=FloatField()) ** (F('days_since')*(-0.1/(F('study_count')+1))),
  162 + output_field=FloatField()
  163 + )
  164 + ).order_by('retention_score')
140 165  
141 166  
142 167 class UserFlashcard(Model):
flashcards/views.py View file @ 1eebdbc
1   -from math import e
2   -
3 1 import django
4 2 from django.contrib import auth
5 3 from django.shortcuts import get_object_or_404
6 4 from django.utils.log import getLogger
7 5 from flashcards.api import StandardResultsSetPagination, IsEnrolledInAssociatedSection, IsFlashcardReviewer, \
8 6 IsAuthenticatedAndConfirmed
9   -from flashcards.models import Section, User, Flashcard, FlashcardHide, UserFlashcard, UserFlashcardQuiz, \
10   - FlashcardAlreadyPulledException, FlashcardNotInDeckException, Now, interval_days
  7 +from flashcards.models import Section, User, Flashcard, FlashcardHide, UserFlashcardQuiz, \
  8 + FlashcardAlreadyPulledException, FlashcardNotInDeckException
11 9 from flashcards.notifications import notify_new_card
12 10 from flashcards.serializers import SectionSerializer, UserUpdateSerializer, RegistrationSerializer, UserSerializer, \
13 11 PasswordResetSerializer, PasswordResetRequestSerializer, EmailPasswordSerializer, FlashcardSerializer, \
... ... @@ -21,7 +19,6 @@
21 19 from django.core.mail import send_mail
22 20 from django.contrib.auth import authenticate
23 21 from django.contrib.auth.tokens import default_token_generator
24   -from django.db.models import Count, Max, F, Value, When, Case, FloatField
25 22 from rest_framework.status import HTTP_204_NO_CONTENT, HTTP_201_CREATED, HTTP_200_OK
26 23 from rest_framework.response import Response
27 24 from rest_framework.exceptions import AuthenticationFailed, NotAuthenticated, ValidationError, PermissionDenied
28 25  
29 26  
30 27  
31 28  
... ... @@ -393,38 +390,16 @@
393 390 serializer = QuizRequestSerializer(data=request.data)
394 391 serializer.is_valid(raise_exception=True)
395 392 data = serializer.validated_data
396   - user_flashcard_filter = UserFlashcard.objects.filter(
397   - user=request.user, flashcard__section__pk__in=data['sections'],
398   - flashcard__material_date__gte=data['material_date_begin'],
399   - flashcard__material_date__lte=data['material_date_end']
400   - )
  393 + user_flashcard = request.user.by_retention(**data).first()
401 394  
402   - if not user_flashcard_filter.exists():
403   - raise ValidationError("No matching flashcard found in your decks")
404   -
405   - quiz_filter = user_flashcard_filter.prefetch_related('userflashcardquiz_set').annotate(
406   - study_count=Count('pk'),
407   - days_since=Case(
408   - When(userflashcardquiz__when=None, then=interval_days(Now(), F('pulled'))),
409   - default=interval_days(Now(), Max('userflashcardquiz__when')),
410   - output_field=FloatField()
411   - ),
412   - retention_score=Case(
413   - default=Value(e, output_field=FloatField()) ** (F('days_since') * (-0.1 / (F('study_count') + 1))),
414   - output_field=FloatField()
415   - )
416   - ).order_by('retention_score')
417   -
418   - user_flashcard = quiz_filter.first()
419 395 mask = user_flashcard.get_mask().get_random_blank()
420   - if not mask:
421   - blanked_word = ""
422   - log_event(request, mask)
423   - else:
  396 + blanked_word = ""
  397 + if mask:
424 398 blanked_word = user_flashcard.flashcard.text[slice(*mask)]
425   - user_flashcard_quiz = UserFlashcardQuiz(user_flashcard=user_flashcard,
426   - blanked_word=blanked_word)
  399 +
  400 + user_flashcard_quiz = UserFlashcardQuiz(user_flashcard=user_flashcard, blanked_word=blanked_word)
427 401 user_flashcard_quiz.save()
  402 +
428 403 response = QuizResponseSerializer(instance=user_flashcard_quiz, mask=mask)
429 404 log_event(request, response.data)
430 405 return Response(response.data, status=HTTP_200_OK)