Commit a2d8c42294272590acc7bf17309aa03bd68f2622

Authored by Andrew Buss
1 parent 033fffbf8e
Exists in master

added feed

Showing 3 changed files with 39 additions and 16 deletions Side-by-side Diff

flashcards/models.py View file @ a2d8c42
  1 +from datetime import datetime
  2 +
1 3 from django.contrib.auth.models import AbstractUser, UserManager
2 4 from django.core.exceptions import PermissionDenied
3 5 from django.db.models import *
4 6 from django.utils.timezone import now
5 7 from simple_email_confirmation import SimpleEmailConfirmationUserMixin
6 8 from fields import MaskField
7   -from datetime import datetime
8 9  
  10 +
9 11 # Hack to fix AbstractUser before subclassing it
10 12 AbstractUser._meta.get_field('email')._unique = True
11 13 AbstractUser._meta.get_field('username')._unique = False
... ... @@ -139,7 +141,7 @@
139 141 # create_new is True iff the user editing this card is the author of this card
140 142 # and there are no other users with this card in their decks
141 143 create_new = user != self.author or \
142   - UserFlashcard.objects.filter(flashcard=self).exclude(user=user).exists()
  144 + UserFlashcard.objects.filter(flashcard=self).exclude(user=user).exists()
143 145  
144 146 if 'material_date' in new_flashcard and self.material_date != new_flashcard['material_date']:
145 147 content_changed |= True
146 148  
... ... @@ -245,11 +247,15 @@
245 247  
246 248 @property
247 249 def long_name(self):
248   - return '%s %s (%s)' % (self.course_title, self.lecture_times, self.instructor)
  250 + return '%s %s (%s)' % (self.course_title, self.lecture_times, self.instructor)
249 251  
250 252 @property
251 253 def short_name(self):
252 254 return '%s %s' % (self.department_abbreviation, self.course_num)
  255 +
  256 + def get_feed_for_user(self, user):
  257 + qs = Flashcard.objects.filter(section=self).exclude(userflashcard__user=user).order_by('pushed')
  258 + return qs
253 259  
254 260 def __unicode__(self):
255 261 return '%s %s: %s (%s %s)' % (
flashcards/tests/test_api.py View file @ a2d8c42
... ... @@ -257,4 +257,10 @@
257 257 def test_section_deck(self):
258 258 response = self.client.get('/api/sections/1/deck/')
259 259 self.assertEqual(response.status_code, HTTP_200_OK)
  260 +
  261 + def test_section_feed(self):
  262 + response = self.client.get('/api/sections/1/feed/')
  263 + self.assertEqual(response.status_code, HTTP_200_OK)
  264 + print response.data
  265 + self.assertEqual(response.data, {})
flashcards/views.py View file @ a2d8c42
... ... @@ -4,9 +4,9 @@
4 4 from flashcards.serializers import SectionSerializer, UserUpdateSerializer, RegistrationSerializer, UserSerializer, \
5 5 PasswordResetSerializer, PasswordResetRequestSerializer, EmailPasswordSerializer, FlashcardSerializer, \
6 6 FlashcardUpdateSerializer
7   -from rest_framework.decorators import detail_route, permission_classes, api_view, list_route
  7 +from rest_framework.decorators import detail_route, permission_classes, api_view
8 8 from rest_framework.generics import ListAPIView, GenericAPIView
9   -from rest_framework.mixins import CreateModelMixin, RetrieveModelMixin
  9 +from rest_framework.mixins import CreateModelMixin, RetrieveModelMixin, UpdateModelMixin
10 10 from rest_framework.permissions import IsAuthenticated
11 11 from rest_framework.viewsets import ReadOnlyModelViewSet, GenericViewSet
12 12 from django.core.mail import send_mail
... ... @@ -24,7 +24,7 @@
24 24 pagination_class = StandardResultsSetPagination
25 25 permission_classes = [IsAuthenticated]
26 26  
27   - @detail_route(methods=['get'], permission_classes=[IsAuthenticated])
  27 + @detail_route(methods=['GET'])
28 28 def flashcards(self, request, pk):
29 29 """
30 30 Gets flashcards for a section, excluding hidden cards.
... ... @@ -33,7 +33,7 @@
33 33 flashcards = Flashcard.cards_visible_to(request.user).filter(section=self.get_object())
34 34 return Response(FlashcardSerializer(flashcards, many=True).data)
35 35  
36   - @detail_route(methods=['post'], permission_classes=[IsAuthenticated])
  36 + @detail_route(methods=['post'])
37 37 def enroll(self, request, pk):
38 38 """
39 39 Add the current user to a specified section
... ... @@ -53,7 +53,7 @@
53 53 request.user.sections.add(section)
54 54 return Response(status=HTTP_204_NO_CONTENT)
55 55  
56   - @detail_route(methods=['post'], permission_classes=[IsAuthenticated])
  56 + @detail_route(methods=['post'])
57 57 def drop(self, request, pk):
58 58 """
59 59 Remove the current user from a specified section
60 60  
61 61  
... ... @@ -71,15 +71,18 @@
71 71 section.user_set.remove(request.user)
72 72 return Response(status=HTTP_204_NO_CONTENT)
73 73  
74   - @list_route(methods=['get'], permission_classes=[IsAuthenticated])
  74 + @detail_route(methods=['GET'])
75 75 def search(self, request):
  76 + """
  77 + Returns a list of sections which match a user's query
  78 + """
76 79 query = request.GET.get('q', None)
77 80 if not query: return Response('[]')
78 81 qs = Section.search(query.split(' '))[:20]
79 82 serializer = SectionSerializer(qs, many=True)
80 83 return Response(serializer.data)
81 84  
82   - @detail_route(methods=['get'], permission_classes=[IsAuthenticated])
  85 + @detail_route(methods=['GET'])
83 86 def deck(self, request, pk):
84 87 """
85 88 Gets the contents of a user's deck for a given section.
86 89  
... ... @@ -90,7 +93,16 @@
90 93 serializer = FlashcardSerializer(qs, many=True)
91 94 return Response(serializer.data)
92 95  
  96 + @detail_route(methods=['GET'])
  97 + def feed(self, request, pk):
  98 + """
  99 + Gets the contents of a user's feed for a section.
  100 + Exclude cards that are already in the user's deck
  101 + """
  102 + serializer = FlashcardSerializer(self.get_object().get_feed_for_user(request.user), many=True)
  103 + return Response(serializer.data)
93 104  
  105 +
94 106 class UserSectionListView(ListAPIView):
95 107 serializer_class = SectionSerializer
96 108 permission_classes = [IsAuthenticated]
... ... @@ -261,7 +273,7 @@
261 273 return Response(status=HTTP_204_NO_CONTENT)
262 274  
263 275  
264   -class FlashcardViewSet(GenericViewSet, CreateModelMixin, RetrieveModelMixin):
  276 +class FlashcardViewSet(GenericViewSet, UpdateModelMixin, CreateModelMixin, RetrieveModelMixin):
265 277 queryset = Flashcard.objects.all()
266 278 serializer_class = FlashcardSerializer
267 279 permission_classes = [IsAuthenticated]
... ... @@ -275,7 +287,7 @@
275 287 headers = self.get_success_headers(serializer.data)
276 288 return Response(serializer.data, status=HTTP_201_CREATED, headers=headers)
277 289  
278   - @detail_route(methods=['post'], permission_classes=[IsAuthenticated])
  290 + @detail_route(methods=['post'])
279 291 def report(self, request, pk):
280 292 """
281 293 Report the given card
... ... @@ -292,7 +304,7 @@
292 304 obj.save()
293 305 return Response(status=HTTP_204_NO_CONTENT)
294 306  
295   - @detail_route(methods=['POST'], permission_classes=[IsAuthenticated])
  307 + @detail_route(methods=['POST'])
296 308 def pull(self, request, pk):
297 309 """
298 310 Pull a card from the live feed into the user's deck.
... ... @@ -305,8 +317,7 @@
305 317 user.pull(flashcard)
306 318 return Response(status=HTTP_204_NO_CONTENT)
307 319  
308   - @detail_route(methods=['PATCH'], permission_classes=[IsAuthenticated])
309   - def patch(self, request, pk):
  320 + def update(self, request, *args, **kwargs):
310 321 """
311 322 Edit settings related to a card for the user.
312 323 :param request: The request object.
... ... @@ -314,7 +325,7 @@
314 325 :return: A 204 response upon success.
315 326 """
316 327 user = request.user
317   - flashcard = Flashcard.objects.get(pk=pk)
  328 + flashcard = self.get_object()
318 329 data = FlashcardUpdateSerializer(data=request.data)
319 330 data.is_valid(raise_exception=True)
320 331 new_flashcard = data.validated_data