Commit 776577266a0d1426343db9613ab50e013e0daee1
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): |