Skip to content

Commit

Permalink
Merge pull request #33 from City-of-Turku/feature/polls-enabled-for-u…
Browse files Browse the repository at this point in the history
…nauth-users

unauth users can answer polls if open commenting
  • Loading branch information
hienous authored Jun 23, 2021
2 parents 8c52efa + 650c282 commit fe595d3
Show file tree
Hide file tree
Showing 3 changed files with 126 additions and 14 deletions.
38 changes: 38 additions & 0 deletions democracy/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,44 @@ def default_hearing(john_doe, contact_person, default_organization, default_proj

return hearing

@pytest.fixture()
def hearing__with_4_different_commenting(john_doe, contact_person, default_organization, default_project):
"""
Fixture for a "default" hearing with four sections (one main, three other sections).
All objects will have the 3 default images attached.
All objects will have commenting_map_tools all.
Each section will have a different commenting rule.
"""
commenting_restrictions = [Commenting.NONE, Commenting.OPEN, Commenting.REGISTERED, Commenting.STRONG]
hearing = Hearing.objects.create(
title='Default test hearing One',
open_at=now() - datetime.timedelta(days=1),
close_at=now() + datetime.timedelta(days=1),
slug='default-hearing-slug',
organization=default_organization,
project_phase=default_project.phases.all()[0],
)
for x in range(1, 5):
section_type = (InitialSectionType.MAIN if x == 1 else InitialSectionType.SCENARIO)
section = Section.objects.create(
abstract='Section %d abstract' % x,
hearing=hearing,
type=SectionType.objects.get(identifier=section_type),
commenting=commenting_restrictions[x-1],
commenting_map_tools=CommentingMapTools.ALL
)
create_default_images(section)
create_default_files(section)
section.comments.create(created_by=john_doe, content=default_comment_content[::-1])
section.comments.create(created_by=john_doe, content=red_comment_content[::-1])
section.comments.create(created_by=john_doe, content=green_comment_content[::-1])

assert_ascending_sequence([s.ordering for s in hearing.sections.all()])

hearing.contact_persons.add(contact_person)

return hearing

@pytest.fixture()
def hearing_without_comments(contact_person, default_organization, default_project):
"""
Expand Down
76 changes: 70 additions & 6 deletions democracy/tests/test_section_poll.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from democracy.tests.test_comment import get_comment_data
from democracy.tests.test_hearing import valid_hearing_json
from democracy.tests.utils import get_data_from_response, assert_common_keys_equal
from democracy.enums import Commenting

from sys import platform

Expand Down Expand Up @@ -170,17 +171,43 @@ def test_update_poll_having_answers(valid_hearing_json_with_poll, john_doe_api_c
update_response = john_smith_api_client.put('/v1/hearing/%s/' % data['id'], data=data, format='json')
assert update_response.status_code == 400


@pytest.mark.parametrize("commenting_restriction", (Commenting.NONE, Commenting.REGISTERED, Commenting.STRONG))
@pytest.mark.django_db
def test_post_section_poll_answer_unauthenticated(api_client, default_hearing, geojson_feature):
section = default_hearing.sections.first()
SectionPollFactory(section=section, option_count=3)
url = '/v1/hearing/%s/sections/%s/comments/' % (default_hearing.id, section.id)
def test_post_section_poll_answer_unauthenticated_but_not_allowed(api_client, hearing__with_4_different_commenting, geojson_feature, commenting_restriction):
section = hearing__with_4_different_commenting.sections.filter(commenting=commenting_restriction).first()
poll = SectionPollFactory(section=section, option_count=3, type=SectionPoll.TYPE_SINGLE_CHOICE)
option = poll.options.all().first()
url = '/v1/hearing/%s/sections/%s/comments/' % (hearing__with_4_different_commenting.id, section.id)
data = get_comment_data()
data['answers'] = [{}]
data['answers'] = [{
'question': poll.id,
'type': SectionPoll.TYPE_SINGLE_CHOICE,
'answers': [option.id],
}]
response = api_client.post(url, data=data)
assert response.status_code == 403

@pytest.mark.django_db
def test_post_section_poll_answer_unauthenticated_single_choice(api_client, hearing__with_4_different_commenting, geojson_feature):
section = hearing__with_4_different_commenting.sections.filter(commenting=Commenting.OPEN).first()
poll = SectionPollFactory(section=section, option_count=3, type=SectionPoll.TYPE_SINGLE_CHOICE)
option = poll.options.all().first()

url = '/v1/hearing/%s/sections/%s/comments/' % (hearing__with_4_different_commenting.id, section.id)
data = get_comment_data()
data['answers'] = [{
'question': poll.id,
'type': SectionPoll.TYPE_SINGLE_CHOICE,
'answers': [option.id],
}]
response = api_client.post(url, data=data)
created_data = get_data_from_response(response, status_code=201)
poll.refresh_from_db(fields=['n_answers'])
option.refresh_from_db(fields=['n_answers'])
assert poll.n_answers == 1
assert option.n_answers == 1
assert created_data['answers'][0]['type'] == data['answers'][0]['type']
assert set(created_data['answers'][0]['answers']) == set(data['answers'][0]['answers'])

@pytest.mark.django_db
def test_post_section_poll_answer_single_choice(john_doe_api_client, default_hearing, geojson_feature):
Expand All @@ -204,6 +231,43 @@ def test_post_section_poll_answer_single_choice(john_doe_api_client, default_hea
assert created_data['answers'][0]['type'] == data['answers'][0]['type']
assert set(created_data['answers'][0]['answers']) == set(data['answers'][0]['answers'])

@pytest.mark.parametrize("commenting_restriction", (Commenting.NONE, Commenting.STRONG))
@pytest.mark.django_db
def test_post_section_poll_answer_authenticated_but_not_allowed(john_doe_api_client, hearing__with_4_different_commenting, geojson_feature, commenting_restriction):
section = hearing__with_4_different_commenting.sections.filter(commenting=commenting_restriction).first()
poll = SectionPollFactory(section=section, option_count=3, type=SectionPoll.TYPE_SINGLE_CHOICE)
option = poll.options.all().first()
url = '/v1/hearing/%s/sections/%s/comments/' % (hearing__with_4_different_commenting.id, section.id)
data = get_comment_data()
data['answers'] = [{
'question': poll.id,
'type': SectionPoll.TYPE_SINGLE_CHOICE,
'answers': [option.id],
}]
response = john_doe_api_client.post(url, data=data)
assert response.status_code == 403

@pytest.mark.django_db
def test_post_section_poll_answer_authenticated_open_commenting(john_doe_api_client, hearing__with_4_different_commenting, geojson_feature):
section = hearing__with_4_different_commenting.sections.filter(commenting=Commenting.OPEN).first()
poll = SectionPollFactory(section=section, option_count=3, type=SectionPoll.TYPE_SINGLE_CHOICE)
option = poll.options.all().first()

url = '/v1/hearing/%s/sections/%s/comments/' % (hearing__with_4_different_commenting.id, section.id)
data = get_comment_data()
data['answers'] = [{
'question': poll.id,
'type': SectionPoll.TYPE_SINGLE_CHOICE,
'answers': [option.id],
}]
response = john_doe_api_client.post(url, data=data)
created_data = get_data_from_response(response, status_code=201)
poll.refresh_from_db(fields=['n_answers'])
option.refresh_from_db(fields=['n_answers'])
assert poll.n_answers == 1
assert option.n_answers == 1
assert created_data['answers'][0]['type'] == data['answers'][0]['type']
assert set(created_data['answers'][0]['answers']) == set(data['answers'][0]['answers'])

@pytest.mark.django_db
def test_post_section_poll_answer_multiple_choice(john_doe_api_client, default_hearing, geojson_feature):
Expand Down
26 changes: 18 additions & 8 deletions democracy/views/section_comment.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from rest_framework.settings import api_settings

from democracy.models import SectionComment, Label, Section, SectionPoll, SectionPollOption, SectionPollAnswer
from democracy.enums import Commenting
from democracy.models.section import CommentImage
from democracy.views.comment import COMMENT_FIELDS, BaseCommentViewSet, BaseCommentSerializer
from democracy.views.label import LabelSerializer
Expand Down Expand Up @@ -178,12 +179,15 @@ def _check_single_choice_poll(self, answer):
def _check_can_vote(self, answer):
if not answer['answers']:
return None
poll_answers = SectionPollAnswer.objects.filter(
option__poll=answer['question'],
comment__created_by=self.request.user
)
if poll_answers:
raise ValidationError({'answers': [_('You have already voted.')]})

# Authenticated users can only have one answer per poll.
if self.request.user.is_authenticated:
poll_answers = SectionPollAnswer.objects.filter(
option__poll=answer['question'],
comment__created_by=self.request.user
)
if poll_answers:
raise ValidationError({'answers': [_('You have already voted.')]})

def create_related(self, request, instance=None, *args, **kwargs):
answers = request.data.pop('answers', [])
Expand Down Expand Up @@ -270,9 +274,15 @@ def _check_may_comment(self, request):
raise ValidationError({'section': [
_('The comment section has to be specified in URL or by JSON section or comment field.')
]})
if len(request.data.get('answers', [])) > 0 and not request.user.is_authenticated:

'''
Unauthenticated user can answer polls in hearings that have open commenting.
The following if statement should never be true as unauthenticated users can't post comments if
the hearing doesn't have open commenting.
'''
if len(request.data.get('answers', [])) > 0 and not request.user.is_authenticated and parent.commenting != Commenting.OPEN:
return response.Response(
{'status': 'Unauthenticated users cannot answer polls.'},
{'status': 'Unauthenticated users cannot answer polls in hearings that do not have open commenting.'},
status=status.HTTP_403_FORBIDDEN
)
return super()._check_may_comment(request)
Expand Down

0 comments on commit fe595d3

Please sign in to comment.