Skip to content
This repository has been archived by the owner on Apr 8, 2023. It is now read-only.

WIP: Manual Publishing for Polls #715

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
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
7 changes: 6 additions & 1 deletion _1327/documents/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,12 @@ def publish(request, title, next_state_id):
raise PermissionDenied()

document.publish(next_state_id)
messages.success(request, _("Minutes document has been published."))
if isinstance(document, MinutesDocument):
messages.success(request, _("Minutes document has been published."))
elif isinstance(document, Poll):
messages.success(request, _("Poll has been published."))
else:
raise SuspiciousOperation

return HttpResponseRedirect(reverse(document.get_view_url_name(), args=[document.url_title]))

Expand Down
2 changes: 1 addition & 1 deletion _1327/polls/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class PollForm(DocumentForm):

class Meta:
model = Poll
fields = ['title_de', 'title_en', 'url_title', 'text_de', 'text_en', 'start_date', 'end_date', 'max_allowed_number_of_answers', 'show_results_immediately', 'comment', 'group', 'vote_groups']
fields = ['title_de', 'title_en', 'url_title', 'text_de', 'text_en', 'start_date', 'end_date', 'max_allowed_number_of_answers', 'state', 'comment', 'group', 'vote_groups']

def __init__(self, *args, **kwargs):
creation = kwargs.get('creation', None)
Expand Down
22 changes: 22 additions & 0 deletions _1327/polls/migrations/0005_auto_20200318_1928.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Generated by Django 2.2.11 on 2020-03-18 18:28

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('polls', '0004_auto_20200302_1915'),
]

operations = [
migrations.RemoveField(
model_name='poll',
name='show_results_immediately',
),
migrations.AddField(
model_name='poll',
name='state',
field=models.IntegerField(choices=[(0, 'Unpublished'), (1, 'Published After End of Poll'), (2, 'Published')], default=0, verbose_name='State'),
),
]
28 changes: 27 additions & 1 deletion _1327/polls/models.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
from datetime import date, datetime

from django.conf import settings
from django.contrib.auth.models import Group
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import SuspiciousOperation
from django.db import models
from django.db.models import Sum
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.template import loader
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
Expand All @@ -21,6 +26,15 @@


class Poll(Document):
UNPUBLISHED = 0
PUBLISHED = 1
# AFTER_END is marked differently so that it does not clash with the numeration scheme minutes
AFTER_END = 5 # TODO: keep this numeration scheme so that it doesn't conflict with the other options?
PUBLISH_CHOICES = (
(UNPUBLISHED, _('Unpublished')),
(AFTER_END, _('Published After End of Poll')),
(PUBLISHED, _('Published')),
)

def can_be_changed_by(self, user):
permission_name = self.edit_permission_name
Expand All @@ -34,7 +48,8 @@ def can_be_reverted(self):
end_date = models.DateField(default=datetime.now, verbose_name=_("End Date"))
max_allowed_number_of_answers = models.PositiveIntegerField(default=1)
participants = models.ManyToManyField(UserProfile, related_name="polls", blank=True)
show_results_immediately = models.BooleanField(default=True, verbose_name=_("show results immediately after vote"))
# show_results_immediately = models.BooleanField(default=True, verbose_name=_("show results immediately after vote"))
state = models.IntegerField(choices=PUBLISH_CHOICES, default=UNPUBLISHED, verbose_name=_("State"))

VIEW_PERMISSION_NAME = POLL_VIEW_PERMISSION_NAME
VOTE_PERMISSION_NAME = POLL_VOTE_PERMISSION_NAME
Expand Down Expand Up @@ -131,6 +146,17 @@ def has_choice_descriptions(self):
return True
return False

def show_publish_button(self):
return not self.is_in_creation and self.state == Poll.UNPUBLISHED

# TODO: publish button might be a bit ugly because it is close to permission button
def publish(self, next_state_id):
if next_state_id == Poll.PUBLISHED:
self.state = int(next_state_id)
self.save()
else:
raise SuspiciousOperation


revisions.register(Poll, follow=["document_ptr"])

Expand Down
13 changes: 11 additions & 2 deletions _1327/polls/templatetags/poll_tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,24 @@

from django import template

from _1327.polls.models import Poll

register = template.Library()


@register.filter
def can_see_results(poll):
if not poll.show_results_immediately and poll.end_date >= datetime.date.today():
if poll.state == Poll.UNPUBLISHED:
return False
else:
elif poll.state == Poll.AFTER_END:
if poll.end_date >= datetime.date.today():
return False
else:
return True
elif poll.state == Poll.PUBLISHED:
return True
else:
return False # TODO: raise Exception?


@register.filter
Expand Down
72 changes: 58 additions & 14 deletions _1327/polls/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -255,10 +255,12 @@ def test_edit_poll_delete_choice(self):
def test_edit_poll_user_has_no_permission(self):
user = baker.make(UserProfile)

response = self.app.get(reverse(self.poll.get_edit_url_name(), args=[self.poll.url_title]), user=user, expect_errors=True)
response = self.app.get(reverse(self.poll.get_edit_url_name(), args=[self.poll.url_title]), user=user,
expect_errors=True)
self.assertEqual(response.status_code, 403)

response = self.app.post(reverse(self.poll.get_edit_url_name(), args=[self.poll.url_title]), user=user, expect_errors=True)
response = self.app.post(reverse(self.poll.get_edit_url_name(), args=[self.poll.url_title]), user=user,
expect_errors=True)
self.assertEqual(response.status_code, 403)

def test_deletion_no_superuser(self):
Expand Down Expand Up @@ -296,7 +298,8 @@ def test_result_preview_non_superuser(self):
user = baker.make(UserProfile)
assign_perm(self.poll.vote_permission_name, user, self.poll)

response = self.app.get(reverse('polls:results_for_admin', args=[self.poll.url_title]), user=user, expect_errors=True)
response = self.app.get(reverse('polls:results_for_admin', args=[self.poll.url_title]), user=user,
expect_errors=True)
self.assertEqual(response.status_code, 403)

def test_result_preview_superuser(self):
Expand Down Expand Up @@ -410,14 +413,16 @@ def test_view_before_poll_has_started(self):
def test_view_poll_without_vote_permission(self):
self.assign_view_perm(self.user, self.poll)

response = self.app.get(reverse(self.poll.get_view_url_name(), args=[self.poll.url_title]), user=self.user, expect_errors=True)
response = self.app.get(reverse(self.poll.get_view_url_name(), args=[self.poll.url_title]), user=self.user,
expect_errors=True)
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'polls_results.html')

def test_vote_poll_without_vote_permission(self):
self.assign_view_perm(self.user, self.poll)

response = self.app.get(reverse(self.poll.get_view_url_name(), args=[self.poll.url_title]), user=self.user, expect_errors=True)
response = self.app.get(reverse(self.poll.get_view_url_name(), args=[self.poll.url_title]), user=self.user,
expect_errors=True)
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'polls_results.html')

Expand Down Expand Up @@ -456,7 +461,8 @@ def test_vote_with_insufficient_permissions(self):
user = baker.make(UserProfile)
assign_perm(Poll.VIEW_PERMISSION_NAME, user, self.poll)

response = self.app.get(reverse(self.poll.get_view_url_name(), args=[self.poll.url_title]), user=user, expect_errors=True)
response = self.app.get(reverse(self.poll.get_view_url_name(), args=[self.poll.url_title]), user=user,
expect_errors=True)
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'polls_results.html')

Expand Down Expand Up @@ -492,7 +498,8 @@ def test_start_vote_multiple_choice_poll(self):
self.poll.max_allowed_number_of_answers = 2
self.poll.save()

response = self.app.get(reverse(self.poll.get_view_url_name(), args=[self.poll.url_title]) + '/', user=self.user)
response = self.app.get(reverse(self.poll.get_view_url_name(), args=[self.poll.url_title]) + '/',
user=self.user)
self.assertEqual(response.status_code, 301)

response = self.app.get(reverse(self.poll.get_view_url_name(), args=[self.poll.url_title]), user=self.user)
Expand Down Expand Up @@ -525,7 +532,8 @@ def test_vote_single_choice_submitting_more_than_one_choice(self):

data = [('choice', choice.id) for choice in self.poll.choices.all()]

response = self.app.post(reverse(self.poll.get_view_url_name(), args=[self.poll.url_title]), params=data, user=self.user)
response = self.app.post(reverse(self.poll.get_view_url_name(), args=[self.poll.url_title]), params=data,
user=self.user)
self.assertRedirects(response, reverse(self.poll.get_view_url_name(), args=[self.poll.url_title]))

def test_vote_single_choice_correctly(self):
Expand All @@ -536,7 +544,8 @@ def test_vote_single_choice_correctly(self):
data = [('choice', choice.id)]
votes = choice.votes

response = self.app.post(reverse(self.poll.get_view_url_name(), args=[self.poll.url_title]), params=data, user=self.user)
response = self.app.post(reverse(self.poll.get_view_url_name(), args=[self.poll.url_title]), params=data,
user=self.user)
self.assertEqual(response.status_code, 302)

choice = self.poll.choices.first()
Expand All @@ -555,7 +564,8 @@ def test_vote_multiple_choice_correctly(self):
data.append(('choice', choice.id))
votes.append(choice.votes)

response = self.app.post(reverse(self.poll.get_view_url_name(), args=[self.poll.url_title]), params=data, user=self.user)
response = self.app.post(reverse(self.poll.get_view_url_name(), args=[self.poll.url_title]), params=data,
user=self.user)
self.assertEqual(response.status_code, 302)
for i, choice in enumerate(self.poll.choices.all()):
self.assertEqual(choice.votes, votes[i] + 1)
Expand All @@ -572,23 +582,54 @@ def test_view_before_poll_has_started(self):
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'polls_index.html')

# TODO
def test_view_poll_before_end(self):
self.poll.participants.add(self.user)
self.poll.show_results_immediately = True
self.poll.state = Poll.UNPUBLISHED
self.poll.save()

response = self.app.get(reverse(self.poll.get_view_url_name(), args=[self.poll.url_title]), user=self.user)
self.assertRedirects(response, reverse('polls:index'))

self.poll.state = Poll.AFTER_END
self.poll.save()

response = self.app.get(reverse(self.poll.get_view_url_name(), args=[self.poll.url_title]), user=self.user)
self.assertRedirects(response, reverse('polls:index'))

self.poll.state = Poll.PUBLISHED
self.poll.save()

response = self.app.get(reverse(self.poll.get_view_url_name(), args=[self.poll.url_title]), user=self.user)
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'polls_results.html')

self.poll.show_results_immediately = False
# TODO
def test_view_poll_after_end(self):
self.poll.participants.add(self.user)
self.poll.state = Poll.UNPUBLISHED
self.poll.save()

response = self.app.get(reverse(self.poll.get_view_url_name(), args=[self.poll.url_title]), user=self.user)
self.assertRedirects(response, reverse('polls:index'))

self.poll.state = Poll.AFTER_END
self.poll.save()

response = self.app.get(reverse(self.poll.get_view_url_name(), args=[self.poll.url_title]), user=self.user)
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'polls_results.html')

self.poll.state = Poll.PUBLISHED
self.poll.save()

response = self.app.get(reverse(self.poll.get_view_url_name(), args=[self.poll.url_title]), user=self.user)
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'polls_results.html')

# TODO
def test_vote_poll_with_results_that_can_not_be_seen_immediately(self):
self.poll.show_results_immediately = False
self.poll.state = Poll.AFTER_END
self.poll.save()

response = self.app.get(reverse(self.poll.get_view_url_name(), args=[self.poll.url_title]), user=self.user)
Expand All @@ -602,6 +643,8 @@ def test_vote_poll_with_results_that_can_not_be_seen_immediately(self):
self.assertRedirects(response, reverse('polls:index'))


# TODO ?
# - check states when creating
class PollEditTests(WebTest):
csrf_checks = False

Expand Down Expand Up @@ -646,7 +689,8 @@ def test_create_two_polls_without_changing_url_title(self):
self.assertEqual(response.status_code, 200)

def test_submit_with_too_few_forms(self):
response = self.app.get(reverse(self.poll.get_edit_url_name(), args=[self.poll.url_title]) + '/', user=self.user)
response = self.app.get(reverse(self.poll.get_edit_url_name(), args=[self.poll.url_title]) + '/',
user=self.user)
self.assertEqual(response.status_code, 301)

response = self.app.get(reverse(self.poll.get_edit_url_name(), args=[self.poll.url_title]), user=self.user)
Expand Down
4 changes: 2 additions & 2 deletions _1327/polls/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def results(request, poll, url_title):
and poll.end_date >= datetime.date.today():
return vote(request, poll, url_title)

if not poll.show_results_immediately and poll.end_date >= datetime.date.today():
if poll.state == Poll.AFTER_END and poll.end_date >= datetime.date.today():
messages.info(
request,
_("You can not see the results of this poll right now. You have to wait until {} to see the results of this poll.").format(
Expand Down Expand Up @@ -126,7 +126,7 @@ def vote(request, poll, url_title):

poll.participants.add(request.user)
messages.success(request, _("We've received your vote!"))
if not poll.show_results_immediately:
if poll.state == Poll.AFTER_END:
messages.info(request, _("The results of this poll will be available as from {}").format((poll.end_date + datetime.timedelta(days=1)).strftime("%d. %B %Y")))
return HttpResponseRedirect(reverse('polls:index'))
return HttpResponseRedirect(reverse(poll.get_view_url_name(), args=[url_title]))
Expand Down