Browse Source

get voters list endpoint

Rafał Pitoń 8 years ago
parent
commit
41c6a0018c

+ 43 - 3
misago/threads/api/threadpoll.py

@@ -2,7 +2,7 @@ from django.db import transaction
 from django.http import Http404
 from django.http import Http404
 
 
 from rest_framework import viewsets
 from rest_framework import viewsets
-from rest_framework.decorators import detail_route, list_route
+from rest_framework.decorators import detail_route
 from rest_framework.response import Response
 from rest_framework.response import Response
 
 
 from misago.acl import add_acl
 from misago.acl import add_acl
@@ -10,8 +10,8 @@ from misago.core.shortcuts import get_int_or_404
 
 
 from ..models import Poll, PollVote
 from ..models import Poll, PollVote
 from ..permissions.polls import (
 from ..permissions.polls import (
-    allow_start_poll, allow_edit_poll, allow_delete_poll, can_start_poll)
-from ..serializers import PollSerializer, NewPollSerializer, EditPollSerializer
+    allow_see_poll_votes, allow_start_poll, allow_edit_poll, allow_delete_poll, can_start_poll)
+from ..serializers import PollSerializer, PollVoteSerializer, NewPollSerializer, EditPollSerializer
 from ..viewmodels.thread import ForumThread
 from ..viewmodels.thread import ForumThread
 
 
 
 
@@ -97,6 +97,46 @@ class ViewSet(viewsets.ViewSet):
             'can_start_poll': can_start_poll(request.user, thread)
             'can_start_poll': can_start_poll(request.user, thread)
         })
         })
 
 
+    @detail_route(methods=['get', 'post'])
+    def votes(self, request, thread_pk, pk):
+        if request.method == 'POST':
+            return self.post_votes(request, thread_pk, pk)
+        else:
+            return self.get_votes(request, thread_pk, pk)
+
+    @transaction.atomic
+    def post_votes(self, request, thread_pk, pk):
+        pass
+
+    def get_votes(self, request, thread_pk, pk):
+        poll_pk = get_int_or_404(pk)
+
+        try:
+            thread = self.get_thread(request, thread_pk)
+            if thread.poll.pk != poll_pk:
+                raise Http404()
+        except Poll.DoesNotExist:
+            raise Http404()
+
+        allow_see_poll_votes(request.user, thread.poll)
+
+        choices = []
+        voters = {}
+
+        for choice in thread.poll.choices:
+            choice['voters'] = []
+            voters[choice['hash']] = choice['voters']
+
+            choices.append(choice)
+
+        queryset = thread.poll.pollvote_set.values(
+            'voter_id', 'voter_name', 'voter_slug', 'voted_on', 'choice_hash')
+
+        for voter in queryset.order_by('pk').iterator():
+            voters[voter['choice_hash']].append(PollVoteSerializer(voter).data)
+
+        return Response(choices)
+
 
 
 class ThreadPollViewSet(ViewSet):
 class ThreadPollViewSet(ViewSet):
     thread = ForumThread
     thread = ForumThread

+ 7 - 0
misago/threads/permissions/polls.py

@@ -93,6 +93,7 @@ def add_acl_to_poll(user, poll):
     poll.acl.update({
     poll.acl.update({
         'can_edit': can_edit_poll(user, poll),
         'can_edit': can_edit_poll(user, poll),
         'can_delete': can_delete_poll(user, poll),
         'can_delete': can_delete_poll(user, poll),
+        'can_see_votes': can_see_poll_votes(user, poll),
     })
     })
 
 
 
 
@@ -199,6 +200,12 @@ def allow_delete_poll(user, target):
 can_delete_poll = return_boolean(allow_delete_poll)
 can_delete_poll = return_boolean(allow_delete_poll)
 
 
 
 
+def allow_see_poll_votes(user, target):
+    if not target.is_public and not user.acl['can_always_see_poll_voters']:
+        raise PermissionDenied(_("You dont have permission to this poll's voters."))
+can_see_poll_votes = return_boolean(allow_see_poll_votes)
+
+
 def has_time_to_edit_poll(user, target):
 def has_time_to_edit_poll(user, target):
     edit_time = user.acl['poll_edit_time']
     edit_time = user.acl['poll_edit_time']
     if edit_time:
     if edit_time:

+ 34 - 0
misago/threads/serializers/pollvote.py

@@ -0,0 +1,34 @@
+from django.core.urlresolvers import reverse
+
+from rest_framework import serializers
+
+
+class PollVoteSerializer(serializers.Serializer):
+    voted_on = serializers.DateTimeField()
+    username = serializers.SerializerMethodField()
+    slug = serializers.SerializerMethodField()
+
+    url = serializers.SerializerMethodField()
+
+    class Meta:
+        fields = (
+            'voted_on',
+
+            'username',
+            'slug',
+
+            'url',
+        )
+
+    def get_username(self, obj):
+        return obj['voter_name']
+
+    def get_slug(self, obj):
+        return obj['voter_slug']
+
+    def get_url(self, obj):
+        if obj['voter_id']:
+            return reverse('misago:user', kwargs={
+                'pk': obj['voter_id'],
+                'slug': obj['voter_slug'],
+            })

+ 2 - 1
misago/threads/tests/test_thread_poll_api.py

@@ -84,7 +84,8 @@ class ThreadPollApiTestCase(AuthenticatedUserTestCase):
                 }
                 }
             ],
             ],
             allowed_choices=2,
             allowed_choices=2,
-            votes=4
+            votes=4,
+            is_public=True
         )
         )
 
 
         # one user voted for Alpha choice
         # one user voted for Alpha choice

+ 94 - 0
misago/threads/tests/test_thread_pollvotes_api.py

@@ -0,0 +1,94 @@
+from django.contrib.auth import get_user_model
+from django.core.urlresolvers import reverse
+
+from ..models import Poll, PollVote
+from .test_thread_poll_api import ThreadPollApiTestCase
+
+
+class ThreadGetVotesTests(ThreadPollApiTestCase):
+    def setUp(self):
+        super(ThreadGetVotesTests, self).setUp()
+
+        self.mock_poll()
+
+        self.api_link = reverse('misago:api:thread-poll-votes', kwargs={
+            'thread_pk': self.thread.pk,
+            'pk': self.poll.pk
+        })
+
+    def test_anonymous(self):
+        """api allows guests to get poll votes"""
+        self.logout_user()
+
+        response = self.client.get(self.api_link)
+        self.assertEqual(response.status_code, 200)
+
+    def test_no_permission(self):
+        """api chcecks permission to see poll voters"""
+        self.override_acl({
+            'can_always_see_poll_voters': False
+        })
+
+        self.poll.is_public = False
+        self.poll.save()
+
+        response = self.client.get(self.api_link)
+        self.assertEqual(response.status_code, 403)
+
+    def test_nonpublic_poll(self):
+        """api validates that poll is public"""
+        self.logout_user()
+
+        self.poll.is_public = False
+        self.poll.save()
+
+        response = self.client.get(self.api_link)
+        self.assertEqual(response.status_code, 403)
+
+    def test_get_votes(self):
+        """api returns list of votes"""
+        response = self.client.get(self.api_link)
+        self.assertEqual(response.status_code, 200)
+
+        response_json = response.json()
+        self.assertEqual(len(response_json), 4)
+
+        self.assertEqual([c['label'] for c in response_json], ['Alpha', 'Beta', 'Gamma', 'Delta'])
+        self.assertEqual([c['votes'] for c in response_json], [1, 0, 2, 1])
+        self.assertEqual([len(c['voters']) for c in response_json], [1, 0, 2, 1])
+
+        self.assertEqual([[v['username'] for v in c['voters']] for c in response_json], [
+            ['bob'],
+            [],
+            [self.user.username, 'deleted'],
+            [self.user.username]
+        ])
+
+        User = get_user_model()
+        user =  User.objects.get(slug='bob')
+
+        self.assertEqual([[v['url'] for v in c['voters']] for c in response_json], [
+            [user.get_absolute_url()],
+            [],
+            [self.user.get_absolute_url(), None],
+            [self.user.get_absolute_url()]
+        ])
+
+
+class ThreadPostVotesTests(ThreadPollApiTestCase):
+    def setUp(self):
+        super(ThreadPostVotesTests, self).setUp()
+
+        self.mock_poll()
+
+        self.api_link = reverse('misago:api:thread-poll-votes', kwargs={
+            'thread_pk': self.thread.pk,
+            'pk': self.poll.pk
+        })
+
+    def test_anonymous(self):
+        """api requires you to sign in to vote in poll"""
+        self.logout_user()
+
+        response = self.post(self.api_link)
+        self.assertEqual(response.status_code, 403)