Commit d818aecbc5ea37e00222ddb9b34930dba4870439
Exists in
master
Merged.
Showing 7 changed files Side-by-side Diff
flashcards/api.py
View file @
d818aec
1 | -from flashcards.models import Flashcard | |
1 | +from flashcards.models import Flashcard, UserFlashcardQuiz | |
2 | 2 | from rest_framework.pagination import PageNumberPagination |
3 | 3 | from rest_framework.permissions import BasePermission |
4 | 4 | |
5 | 5 | |
... | ... | @@ -24,6 +24,16 @@ |
24 | 24 | |
25 | 25 | class IsEnrolledInAssociatedSection(BasePermission): |
26 | 26 | def has_object_permission(self, request, view, obj): |
27 | + if obj is None: | |
28 | + return True | |
27 | 29 | assert type(obj) is Flashcard |
28 | 30 | return request.user.is_in_section(obj.section) |
31 | + | |
32 | + | |
33 | +class IsFlashcardReviewer(BasePermission): | |
34 | + def has_object_permission(self, request, view, obj): | |
35 | + if obj is None: | |
36 | + return True | |
37 | + assert type(obj) is UserFlashcardQuiz | |
38 | + return request.user == obj.user_flashcard.user |
flashcards/models.py
View file @
d818aec
1 | 1 | from django.contrib.auth.models import AbstractUser, UserManager |
2 | 2 | from django.contrib.auth.tokens import default_token_generator |
3 | +from django.core.cache import cache | |
3 | 4 | from django.core.exceptions import ValidationError |
4 | 5 | from django.core.exceptions import PermissionDenied, SuspiciousOperation |
5 | 6 | from django.core.mail import send_mail |
... | ... | @@ -53,8 +54,6 @@ |
53 | 54 | return self._create_user(email, password, True, True, **extra_fields) |
54 | 55 | |
55 | 56 | |
56 | - | |
57 | - | |
58 | 57 | class User(AbstractUser, SimpleEmailConfirmationUserMixin): |
59 | 58 | """ |
60 | 59 | An extension of Django's default user model. |
61 | 60 | |
... | ... | @@ -328,13 +327,21 @@ |
328 | 327 | self.user_set.remove(user) |
329 | 328 | |
330 | 329 | class Meta: |
331 | - ordering = ['-course_title'] | |
330 | + ordering = ['department_abbreviation', 'course_num'] | |
332 | 331 | |
333 | 332 | @property |
334 | 333 | def lecture_times(self): |
335 | - lecture_periods = self.lectureperiod_set.all() | |
336 | - if not lecture_periods.exists(): return '' | |
337 | - return ''.join(map(lambda x: x.weekday_letter, lecture_periods)) + ' ' + lecture_periods[0].short_start_time | |
334 | + data = cache.get("section_%d_lecture_times" % self.pk) | |
335 | + if not data: | |
336 | + lecture_periods = self.lectureperiod_set.all() | |
337 | + if lecture_periods.exists(): | |
338 | + data = ''.join(map(lambda x: x.weekday_letter, lecture_periods)) + ' ' + lecture_periods[ | |
339 | + 0].short_start_time | |
340 | + else: | |
341 | + data = '' | |
342 | + cache.set("section_%d_lecture_times" % self.pk, data, 24*60*60) | |
343 | + return data | |
344 | + | |
338 | 345 | |
339 | 346 | @property |
340 | 347 | def long_name(self): |
flashcards/serializers.py
View file @
d818aec
... | ... | @@ -71,13 +71,16 @@ |
71 | 71 | |
72 | 72 | |
73 | 73 | class SectionSerializer(ModelSerializer): |
74 | - lectures = LecturePeriodSerializer(source='lectureperiod_set', many=True, read_only=True) | |
75 | 74 | lecture_times = CharField() |
76 | 75 | short_name = CharField() |
77 | 76 | long_name = CharField() |
78 | 77 | |
79 | 78 | class Meta: |
80 | 79 | model = Section |
80 | + | |
81 | +class DeepSectionSerializer(SectionSerializer): | |
82 | + lectures = LecturePeriodSerializer(source='lectureperiod_set', many=True, read_only=True) | |
83 | + | |
81 | 84 | |
82 | 85 | |
83 | 86 | class UserSerializer(ModelSerializer): |
flashcards/validators.py
View file @
d818aec
1 | 1 | from collections import Iterable |
2 | +from random import sample | |
2 | 3 | |
3 | 4 | |
4 | 5 | class FlashcardMask(set): |
... | ... | @@ -13,6 +14,11 @@ |
13 | 14 | |
14 | 15 | def max_offset(self): |
15 | 16 | return self._end |
17 | + | |
18 | + def get_random_blank(self): | |
19 | + if self.max_offset() > 0: | |
20 | + return sample(self, 1) | |
21 | + return () | |
16 | 22 | |
17 | 23 | def _iterable_check(self, iterable): |
18 | 24 | if not isinstance(iterable, Iterable) or not all([isinstance(i, Iterable) for i in iterable]): |
flashcards/views.py
View file @
d818aec
1 | 1 | import django |
2 | 2 | |
3 | 3 | from django.contrib import auth |
4 | +from django.core.cache import cache | |
4 | 5 | from django.shortcuts import get_object_or_404 |
5 | 6 | from flashcards.api import StandardResultsSetPagination, IsEnrolledInAssociatedSection, IsFlashcardReviewer |
6 | 7 | from flashcards.models import Section, User, Flashcard, FlashcardHide, UserFlashcard, UserFlashcardQuiz |
7 | 8 | from flashcards.serializers import SectionSerializer, UserUpdateSerializer, RegistrationSerializer, UserSerializer, \ |
8 | 9 | PasswordResetSerializer, PasswordResetRequestSerializer, EmailPasswordSerializer, FlashcardSerializer, \ |
9 | - FlashcardUpdateSerializer, QuizRequestSerializer, QuizResponseSerializer, QuizAnswerRequestSerializer | |
10 | + FlashcardUpdateSerializer, QuizRequestSerializer, QuizResponseSerializer, \ | |
11 | + QuizAnswerRequestSerializer, DeepSectionSerializer | |
10 | 12 | from rest_framework.decorators import detail_route, permission_classes, api_view, list_route |
11 | 13 | from rest_framework.generics import ListAPIView, GenericAPIView |
12 | 14 | from rest_framework.mixins import CreateModelMixin, RetrieveModelMixin, UpdateModelMixin |
... | ... | @@ -24,7 +26,7 @@ |
24 | 26 | |
25 | 27 | class SectionViewSet(ReadOnlyModelViewSet): |
26 | 28 | queryset = Section.objects.all() |
27 | - serializer_class = SectionSerializer | |
29 | + serializer_class = DeepSectionSerializer | |
28 | 30 | pagination_class = StandardResultsSetPagination |
29 | 31 | permission_classes = [IsAuthenticated] |
30 | 32 | |
31 | 33 | |
... | ... | @@ -75,12 +77,13 @@ |
75 | 77 | description: space-separated list of terms |
76 | 78 | required: true |
77 | 79 | type: form |
80 | + response_serializer: SectionSerializer | |
78 | 81 | """ |
79 | 82 | query = request.GET.get('q', None) |
80 | 83 | if not query: return Response('[]') |
81 | 84 | qs = Section.search(query.split(' '))[:20] |
82 | - serializer = SectionSerializer(qs, many=True) | |
83 | - return Response(serializer.data) | |
85 | + data = SectionSerializer(qs, many=True).data | |
86 | + return Response(data) | |
84 | 87 | |
85 | 88 | @detail_route(methods=['GET']) |
86 | 89 | def deck(self, request, pk): |
... | ... | @@ -112,7 +115,7 @@ |
112 | 115 | |
113 | 116 | |
114 | 117 | class UserSectionListView(ListAPIView): |
115 | - serializer_class = SectionSerializer | |
118 | + serializer_class = DeepSectionSerializer | |
116 | 119 | permission_classes = [IsAuthenticated] |
117 | 120 | |
118 | 121 | def get_queryset(self): |
flashy/settings.py
View file @
d818aec
... | ... | @@ -26,11 +26,14 @@ |
26 | 26 | 'django.contrib.sessions', |
27 | 27 | 'django.contrib.messages', |
28 | 28 | 'django.contrib.staticfiles', |
29 | - | |
29 | + 'ws4redis', | |
30 | 30 | 'rest_framework_swagger', |
31 | 31 | 'rest_framework', |
32 | 32 | ] |
33 | 33 | |
34 | +WEBSOCKET_URL = '/ws/' | |
35 | + | |
36 | + | |
34 | 37 | MIDDLEWARE_CLASSES = ( |
35 | 38 | 'django.contrib.sessions.middleware.SessionMiddleware', |
36 | 39 | 'django.middleware.common.CommonMiddleware', |
37 | 40 | |
... | ... | @@ -59,12 +62,14 @@ |
59 | 62 | 'django.template.context_processors.request', |
60 | 63 | 'django.contrib.auth.context_processors.auth', |
61 | 64 | 'django.contrib.messages.context_processors.messages', |
65 | + 'django.core.context_processors.static', | |
66 | + 'ws4redis.context_processors.default', | |
62 | 67 | ], |
63 | 68 | }, |
64 | 69 | }, |
65 | 70 | ] |
66 | 71 | |
67 | -WSGI_APPLICATION = 'flashy.wsgi.application' | |
72 | +WSGI_APPLICATION = 'ws4redis.django_runserver.application' | |
68 | 73 | |
69 | 74 | DATABASES = { |
70 | 75 | 'default': { |
... | ... | @@ -136,6 +141,12 @@ |
136 | 141 | } |
137 | 142 | |
138 | 143 | SECRET_KEY = os.environ.get('SECRET_KEY', 'LOL DEFAULT SECRET KEY') |
144 | + | |
145 | +CACHES = { | |
146 | + 'default': { | |
147 | + 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache' | |
148 | + } | |
149 | +} | |
139 | 150 | |
140 | 151 | SWAGGER_SETTINGS = { |
141 | 152 | 'doc_expansion': 'list' |