Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

polls: add new signal which is called after a user voted on a poll #1688

Merged
merged 1 commit into from
Nov 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions adhocracy4/polls/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
from .models import Question
from .models import Vote
from .serializers import PollSerializer
from .signals import poll_voted


class PollViewSet(
Expand Down Expand Up @@ -178,6 +179,9 @@ def vote(self, request, pk):
self.save_vote(question, vote_data, creator, content_id)

poll = self.get_object()
poll_voted.send(
sender=self.__class__, poll=poll, creator=creator, content_id=content_id
)
poll_serializer = self.get_serializer(poll)
poll_data = self.add_terms_of_use_info(request, poll_serializer.data)
if not self.request.user.is_authenticated:
Expand Down
9 changes: 9 additions & 0 deletions adhocracy4/polls/signals.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from django.dispatch import Signal

"""Signal which is called after a user participated in a poll. The signal
receives the following arguments:
poll: Poll
creator: User
content_id: uuid4
"""
poll_voted = Signal()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add here the arguments that are needed for the signal.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added a docstring

2 changes: 1 addition & 1 deletion changelog/8381.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
- add option to allow unregistered users to vote in a poll:
- the feature is controlled via a new django setting `A4_POLL_ENABLE_UNREGISTERED_USERS` to enable or disable it
- add a new captcha react component to integrate the captcha in the poll

- add a poll_voted signal which is sent when a user has voted on a poll.
69 changes: 69 additions & 0 deletions tests/polls/test_vote_api.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
from unittest.mock import MagicMock

import pytest
from django.urls import reverse
from rest_framework import status

from adhocracy4.polls.api import PollViewSet
from adhocracy4.polls.models import Answer
from adhocracy4.polls.models import OtherVote
from adhocracy4.polls.models import Vote
from adhocracy4.polls.phases import VotingPhase
from adhocracy4.polls.signals import poll_voted
from adhocracy4.projects.enums import Access
from tests.helpers import active_phase

Expand Down Expand Up @@ -700,3 +704,68 @@ def test_validate_question_belongs_to_poll(
"Question has to belong to the poll set in the url."
in response.content.decode()
)


@pytest.mark.django_db
def test_poll_voted_signal_is_dispatched_on_vote(
user, apiclient, poll_factory, question_factory, choice_factory
):
signal_handler = MagicMock()
poll_voted.connect(signal_handler)

poll = poll_factory()
poll.allow_unregistered_users = True
poll.save()
question = question_factory(poll=poll)
choice1 = choice_factory(question=question)
choice_factory(question=question)
open_question = question_factory(poll=poll, is_open=True)

assert Vote.objects.count() == 0
goapunk marked this conversation as resolved.
Show resolved Hide resolved

apiclient.force_authenticate(user=user)

url = reverse("polls-vote", kwargs={"pk": poll.pk})

data = {
"votes": {
question.pk: {
"choices": [choice1.pk],
"other_choice_answer": "",
"open_answer": "",
},
open_question.pk: {
"choices": [],
"other_choice_answer": "",
"open_answer": "an open answer",
},
}
}

with active_phase(poll.module, VotingPhase):
response = apiclient.post(url, data, format="json")
assert response.status_code == status.HTTP_201_CREATED
signal_handler.assert_called_once_with(
signal=poll_voted,
sender=PollViewSet,
poll=poll,
creator=user,
content_id=None,
)
assert Vote.objects.count() == 1

# test for unregistered user
signal_handler.reset_mock()
apiclient.logout()
data["captcha"] = "testpass:0"
response = apiclient.post(url, data, format="json")
assert response.status_code == status.HTTP_201_CREATED
assert Vote.objects.count() == 2
content_id = Vote.objects.filter(content_id__isnull=False).first().content_id
signal_handler.assert_called_once_with(
signal=poll_voted,
sender=PollViewSet,
poll=poll,
creator=None,
content_id=content_id,
)