Commit 776577266a0d1426343db9613ab50e013e0daee1

Authored by Andrew Buss
1 parent 6713d6dc9f
Exists in master

websockets notifications on new cards and score updates

Showing 4 changed files with 40 additions and 12 deletions Side-by-side Diff

flashcards/models.py View file @ 7765772
... ... @@ -7,11 +7,13 @@
7 7 from django.db import IntegrityError
8 8 from django.db.models import *
9 9 from django.utils.timezone import now
  10 +
10 11 from simple_email_confirmation import SimpleEmailConfirmationUserMixin
11 12 from fields import MaskField
12 13  
13 14  
14 15 # Hack to fix AbstractUser before subclassing it
  16 +
15 17 AbstractUser._meta.get_field('email')._unique = True
16 18 AbstractUser._meta.get_field('username')._unique = False
17 19  
18 20  
19 21  
20 22  
... ... @@ -73,18 +75,21 @@
73 75 user_card = UserFlashcard.objects.create(user=self, flashcard=flashcard)
74 76 user_card.pulled = now()
75 77 user_card.save()
  78 + import flashcards.notifications
  79 + flashcards.notifications.notify_score_change(flashcard)
76 80  
77 81 def unpull(self, flashcard):
78 82 if not self.is_in_section(flashcard.section):
79 83 raise ValueError("User not in the section this flashcard belongs to")
80 84  
81 85 try:
  86 + import flashcards.notifications
82 87 user_card = UserFlashcard.objects.get(user=self, flashcard=flashcard)
  88 + user_card.delete()
  89 + flashcards.notifications.notify_score_change(flashcard)
83 90 except UserFlashcard.DoesNotExist:
84 91 raise ValueError('Cannot unpull card that is not pulled.')
85 92  
86   - user_card.delete()
87   -
88 93 def get_deck(self, section):
89 94 if not self.is_in_section(section):
90 95 raise ObjectDoesNotExist("User not enrolled in section")
... ... @@ -230,6 +235,10 @@
230 235 obj, created = FlashcardHide.objects.get_or_create(user=user, flashcard=self)
231 236 obj.reason = reason
232 237 obj.save()
  238 +
  239 + @property
  240 + def score(self):
  241 + return self.userflashcard_set.count()
233 242  
234 243 @classmethod
235 244 def cards_visible_to(cls, user):
flashcards/notifications.py View file @ 7765772
  1 +import serializers
  2 +from rest_framework.renderers import JSONRenderer
  3 +from ws4redis.publisher import RedisPublisher
  4 +from ws4redis.redis_store import RedisMessage
  5 +
  6 +
  7 +def notify_score_change(flashcard):
  8 + redis_publisher = RedisPublisher(facility='feed/%d' % flashcard.section_id, broadcast=True)
  9 + ws_message = JSONRenderer().render(
  10 + {'event_type': 'score_change', 'new_score': flashcard.score, 'flashcard_id': flashcard.pk})
  11 + message = RedisMessage(ws_message)
  12 + redis_publisher.publish_message(message)
  13 +
  14 +
  15 +def notify_new_card(flashcard):
  16 + redis_publisher = RedisPublisher(facility='feed/%d' % flashcard.section_id, broadcast=True)
  17 + ws_message = JSONRenderer().render(
  18 + {'event_type': 'new_card', 'flashcard': serializers.FlashcardSerializer(flashcard).data})
  19 + message = RedisMessage(ws_message)
  20 + redis_publisher.publish_message(message)
flashcards/serializers.py View file @ 7765772
... ... @@ -2,7 +2,6 @@
2 2  
3 3 from django.utils.datetime_safe import datetime
4 4 from django.utils.timezone import now
5   -import pytz
6 5 from flashcards.models import Section, LecturePeriod, User, Flashcard, UserFlashcard, UserFlashcardQuiz
7 6 from flashcards.validators import FlashcardMask, OverlapIntervalException
8 7 from rest_framework import serializers
... ... @@ -10,7 +9,6 @@
10 9 from rest_framework.serializers import ModelSerializer, Serializer, PrimaryKeyRelatedField, ListField
11 10 from rest_framework.validators import UniqueValidator
12 11 from flashy.settings import QUARTER_END, QUARTER_START
13   -from random import sample
14 12  
15 13  
16 14 class EmailSerializer(Serializer):
17 15  
... ... @@ -78,9 +76,9 @@
78 76 class Meta:
79 77 model = Section
80 78  
  79 +
81 80 class DeepSectionSerializer(SectionSerializer):
82 81 lectures = LecturePeriodSerializer(source='lectureperiod_set', many=True, read_only=True)
83   -
84 82  
85 83  
86 84 class UserSerializer(ModelSerializer):
flashcards/views.py View file @ 7765772
1   -import django
  1 +from random import sample
2 2  
  3 +import django
3 4 from django.contrib import auth
4   -from django.core.cache import cache
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, UserFlashcard, UserFlashcardQuiz
  7 +from flashcards.models import Section, User, Flashcard, FlashcardHide, UserFlashcardQuiz
  8 +from flashcards.notifications import notify_new_card
8 9 from flashcards.serializers import SectionSerializer, UserUpdateSerializer, RegistrationSerializer, UserSerializer, \
9 10 PasswordResetSerializer, PasswordResetRequestSerializer, EmailPasswordSerializer, FlashcardSerializer, \
10 11 FlashcardUpdateSerializer, QuizRequestSerializer, QuizResponseSerializer, \
... ... @@ -21,7 +22,6 @@
21 22 from rest_framework.response import Response
22 23 from rest_framework.exceptions import AuthenticationFailed, NotAuthenticated, ValidationError, PermissionDenied
23 24 from simple_email_confirmation import EmailAddress
24   -from random import sample
25 25  
26 26  
27 27 class SectionViewSet(ReadOnlyModelViewSet):
... ... @@ -212,7 +212,7 @@
212 212  
213 213  
214 214 @api_view(['POST'])
215   -@permission_classes((IsAuthenticated, ))
  215 +@permission_classes((IsAuthenticated,))
216 216 def logout(request, format=None):
217 217 """
218 218 Logs the authenticated user out.
219 219  
220 220  
... ... @@ -270,10 +270,11 @@
270 270 data['author'] = request.user
271 271 flashcard = Flashcard.objects.create(**data)
272 272 self.perform_create(flashcard)
  273 + notify_new_card(flashcard)
273 274 headers = self.get_success_headers(data)
274   - response_data = FlashcardSerializer(flashcard)
275   - return Response(response_data.data, status=HTTP_201_CREATED, headers=headers)
  275 + response_data = FlashcardSerializer(flashcard).data
276 276  
  277 + return Response(response_data, status=HTTP_201_CREATED, headers=headers)
277 278  
278 279 @detail_route(methods=['POST'])
279 280 def unhide(self, request, pk):