Add Poll-system to the new motion app

This commit is contained in:
Oskar Hahn 2013-02-01 12:51:54 +01:00
parent adeaf248ef
commit da7a7a9d68
5 changed files with 223 additions and 10 deletions

View File

@ -267,6 +267,13 @@ class Motion(SlideMixin, models.Model):
#else: #else:
#self.writelog(_("Supporter: -%s") % (person)) #self.writelog(_("Supporter: -%s") % (person))
def create_poll(self):
# TODO: auto increment the poll_number in the Database
poll_number = self.polls.aggregate(Max('poll_number'))['poll_number__max'] or 0
poll = MotionPoll.objects.create(motion=self, poll_number=poll_number + 1)
poll.set_options()
return poll
class MotionVersion(models.Model): class MotionVersion(models.Model):
title = models.CharField(max_length=255, verbose_name=ugettext_lazy("Title")) title = models.CharField(max_length=255, verbose_name=ugettext_lazy("Title"))
@ -307,3 +314,42 @@ class Comment(models.Model):
text = models.TextField() text = models.TextField()
author = PersonField() author = PersonField()
creation_time = models.DateTimeField(auto_now=True) creation_time = models.DateTimeField(auto_now=True)
class MotionVote(BaseVote):
option = models.ForeignKey('MotionOption')
class MotionOption(BaseOption):
poll = models.ForeignKey('MotionPoll')
vote_class = MotionVote
class MotionPoll(CountInvalid, CountVotesCast, BasePoll):
option_class = MotionOption
vote_values = [
ugettext_noop('Yes'), ugettext_noop('No'), ugettext_noop('Abstain')]
motion = models.ForeignKey(Motion, related_name='polls')
poll_number = models.PositiveIntegerField(default=1)
class Meta:
unique_together = ("motion", "poll_number")
def get_absolute_url(self, link='edit'):
return reverse('motion_poll_edit', args=[str(self.motion.pk),
str(self.poll_number)])
def get_motion(self):
return self.motion
def set_options(self):
#TODO: maybe it is possible with .create() to call this without poll=self
self.get_option_class()(poll=self).save()
def append_pollform_fields(self, fields):
CountInvalid.append_pollform_fields(self, fields)
CountVotesCast.append_pollform_fields(self, fields)
def get_ballot(self):
return self.motion.motionpoll_set.filter(id__lte=self.id).count()

View File

@ -22,4 +22,64 @@
{% endif %} {% endif %}
{% endfor %} {% endfor %}
</ol> </ol>
<h4>{% trans "Vote results" %}:</h4>
{% with motion.polls.all as polls %}
{% if not polls.exists %}
{% if perms.motion.can_manage_motion %}
<a href="{% url 'motion_poll_create' motion.id %}">
<span class="button">
<span class="icon statistics">{% trans 'New vote' %}</span>
</span>
</a>
{% else %}
-
{% endif %}
{% endif %}
<ul class="results">
{% for poll in polls %}
{% if perms.motion.can_manage_motion or poll.has_votes %}
<li>
{% if perms.motion.can_manage_motion %}
<strong>{{ forloop.counter }}. {% trans "Vote" %} </strong>
<a class="icon edit" href="{% model_url poll %}" title="{% trans 'Edit Vote' %}">
<span></span>
</a>
<a class="icon delete" href="{% model_url poll %}" title="{% trans 'Delete Vote' %}">
<span></span>
</a>
{% elif poll.has_votes %}
<strong>{{ forloop.counter }}. {% trans "Vote" %}:</strong>
{% endif %}
<br>
{% if poll.has_votes %}
{% with poll.get_options.0 as option %}
<img src="{% static 'images/icons/voting-yes.png' %}" title="{% trans 'Yes' %}"> {{ option.Yes }}<br>
<img src="{% static 'images/icons/voting-no.png' %}" title="{% trans 'No' %}"> {{ option.No }}<br>
<img src="{% static 'images/icons/voting-abstention.png' %}" title="{% trans 'Abstention' %}"> {{ option.Abstain }}<br>
<img src="{% static 'images/icons/voting-invalid.png' %}" title="{% trans 'Invalid' %}"> {{ poll.print_votesinvalid }}<br>
<div style="border-top: 1px solid; padding-top: 5px; margin: 5px 0; width: 10em;">
<img src="{% static 'images/icons/voting-total.png' %}" title="{% trans 'Votes cast' %}"> {{ poll.print_votescast }}
</div>
{% endwith %}
{% if perms.motion.can_manage_motion %}
{% if forloop.last %}
<a href="{% url 'motion_poll_create' motion.pk %}">
<span class="button"><span class="icon statistics">{% trans 'New vote' %}</span></span>
</a>
{% endif %}
{% endif %}
{% else %}
{% if perms.motion.can_manage_motion %}
<a href="{% model_url poll %}">
<span class="button"><span class="icon statistics">{% trans 'Enter result' %}</span></span>
</a>
{% endif %}
{% endif %}
</li>
{% endif %}
{% endfor %}
</ul>
{% endwith %}
{% endblock %} {% endblock %}

View File

@ -0,0 +1,48 @@
{% extends 'base.html' %}
{% load i18n %}
{% load staticfiles %}
{% load tags %}
{% block content %}
<h1>{{ motion }}</h1>
<i class="helptext">{% trans "Special values" %}: -1 = {% trans 'majority' %}; -2 = {% trans 'undocumented' %}</i>
<form action="" method="post" class="small-form">{% csrf_token %}
{{ pre_form }}
<span id="poll_id" style="display:none">{{ object.pk }}</span>
<table class="table" style="width: auto;">
<tr>
<th>{% trans "Option" %}</th>
<th>{% trans "Votes" %}</th>
</tr>
{% for value in forms.0 %}
<tr>
<td>{{ value.label }}</td>
<td>{{ value.errors }}{{ value }}</td>
</tr>
{% endfor %}
<tr class="total">
<td>{% trans "Invalid votes" %}</td>
<td>{{ pollform.votesinvalid.errors }}{{ pollform.votesinvalid }}</td>
</tr>
<tr class="total">
<td>{% trans "Votes cast" %}</td>
<td>{{ pollform.votescast.errors }}{{ pollform.votescast }}</td>
</tr>
</table>
{{ post_form }}
<p>
<button class="button" type="submit">
<span class="icon ok">{% trans 'Save' %}</span>
</button>
<button class="button" type="submit" name="apply">
<span class="icon apply">{% trans 'Apply' %}</span>
</button>
<a href="{% model_url motion %}">
<span class="icon cancel">{% trans 'Cancel' %}</span>
</a>
</p>
</form>
{% endblock %}

View File

@ -47,4 +47,19 @@ urlpatterns = patterns('openslides.motion.views',
'motion_unsupport', 'motion_unsupport',
name='motion_unsupport', name='motion_unsupport',
), ),
url(r'^(?P<pk>\d+)/create_poll/$',
'poll_create',
name='motion_poll_create',
),
url(r'^(?P<pk>\d+)/poll/(?P<poll_number>\d+)/edit$',
'poll_edit',
name='motion_poll_edit',
),
## url(r'^poll/(?P<poll_id>\d+)/del/$',
## 'delete_poll',
## name='motion_poll_delete',
## ),
) )

View File

@ -22,13 +22,14 @@ from django.http import Http404
from openslides.utils.pdf import stylesheet from openslides.utils.pdf import stylesheet
from openslides.utils.views import ( from openslides.utils.views import (
TemplateView, RedirectView, UpdateView, CreateView, DeleteView, PDFView, TemplateView, RedirectView, UpdateView, CreateView, DeleteView, PDFView,
DetailView, ListView, FormView, QuestionMixin) DetailView, ListView, FormView, QuestionMixin, SingleObjectMixin)
from openslides.utils.template import Tab from openslides.utils.template import Tab
from openslides.utils.utils import html_strong from openslides.utils.utils import html_strong
from openslides.poll.views import PollFormView
from openslides.projector.api import get_active_slide from openslides.projector.api import get_active_slide
from openslides.projector.projector import Widget, SLIDE from openslides.projector.projector import Widget, SLIDE
from openslides.config.models import config from openslides.config.models import config
from .models import Motion, MotionSubmitter, MotionSupporter from .models import Motion, MotionSubmitter, MotionSupporter, MotionPoll
from .forms import (BaseMotionForm, MotionSubmitterMixin, MotionSupporterMixin, from .forms import (BaseMotionForm, MotionSubmitterMixin, MotionSupporterMixin,
MotionCreateNewVersionMixin, ConfigForm) MotionCreateNewVersionMixin, ConfigForm)
@ -81,14 +82,16 @@ class MotionMixin(object):
def post_save(self, form): def post_save(self, form):
super(MotionMixin, self).post_save(form) super(MotionMixin, self).post_save(form)
# TODO: only delete and save neccessary submitters and supporter # TODO: only delete and save neccessary submitters and supporter
self.object.submitter.all().delete() if 'submitter' in form.cleaned_data:
self.object.supporter.all().delete() self.object.submitter.all().delete()
MotionSubmitter.objects.bulk_create( MotionSubmitter.objects.bulk_create(
[MotionSubmitter(motion=self.object, person=person) [MotionSubmitter(motion=self.object, person=person)
for person in form.cleaned_data['submitter']]) for person in form.cleaned_data['submitter']])
MotionSupporter.objects.bulk_create( if 'supporter' in form.cleaned_data:
[MotionSupporter(motion=self.object, person=person) self.object.supporter.all().delete()
for person in form.cleaned_data['supporter']]) MotionSupporter.objects.bulk_create(
[MotionSupporter(motion=self.object, person=person)
for person in form.cleaned_data['supporter']])
def get_form_class(self): def get_form_class(self):
form_classes = [BaseMotionForm] form_classes = [BaseMotionForm]
@ -180,6 +183,47 @@ motion_support = SupportView.as_view(support=True)
motion_unsupport = SupportView.as_view(support=False) motion_unsupport = SupportView.as_view(support=False)
class PollCreateView(SingleObjectMixin, RedirectView):
permission_required = 'motion.can_manage_motion'
model = Motion
def get(self, request, *args, **kwargs):
self.object = self.get_object()
return super(PollCreateView, self).get(request, *args, **kwargs)
def pre_redirect(self, request, *args, **kwargs):
self.poll = self.object.create_poll()
messages.success(request, _("New vote was successfully created."))
def get_redirect_url(self, **kwargs):
return reverse('motion_poll_edit', args=[self.object.pk, self.poll.poll_number])
poll_create = PollCreateView.as_view()
class PollUpdateView(PollFormView):
permission_required = 'motion.can_manage_motion'
poll_class = MotionPoll
template_name = 'motion/poll_form.html'
success_url_name = 'motion_detail'
def get_object(self):
return MotionPoll.objects.filter(
motion=self.kwargs['pk'],
poll_number=self.kwargs['poll_number']).get()
def get_context_data(self, **kwargs):
context = super(PollUpdateView, self).get_context_data(**kwargs)
context.update({
'motion': self.poll.motion})
return context
def get_url_name_args(self):
return [self.poll.motion.pk]
poll_edit = PollUpdateView.as_view()
class Config(FormView): class Config(FormView):
permission_required = 'config.can_manage_config' permission_required = 'config.can_manage_config'
form_class = ConfigForm form_class = ConfigForm