Commit d818aecbc5ea37e00222ddb9b34930dba4870439

Authored by Rohan Rangray
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'
requirements.txt View file @ d818aec
1 1 #beautifulsoup4
2 2 Django>=1.8
3   -#django-websocket-redis==0.4.3
  3 +django-websocket-redis
4 4 #gevent==1.0.1
5 5 #greenlet==0.4.5
6 6 #redis==2.10.3