Add Poll-system to the new motion app
This commit is contained in:
parent
adeaf248ef
commit
da7a7a9d68
@ -267,6 +267,13 @@ class Motion(SlideMixin, models.Model):
|
||||
#else:
|
||||
#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):
|
||||
title = models.CharField(max_length=255, verbose_name=ugettext_lazy("Title"))
|
||||
@ -307,3 +314,42 @@ class Comment(models.Model):
|
||||
text = models.TextField()
|
||||
author = PersonField()
|
||||
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()
|
||||
|
@ -22,4 +22,64 @@
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</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 %}
|
||||
|
48
openslides/motion/templates/motion/poll_form.html
Normal file
48
openslides/motion/templates/motion/poll_form.html
Normal 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 %}
|
@ -47,4 +47,19 @@ urlpatterns = patterns('openslides.motion.views',
|
||||
'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',
|
||||
## ),
|
||||
)
|
||||
|
@ -22,13 +22,14 @@ from django.http import Http404
|
||||
from openslides.utils.pdf import stylesheet
|
||||
from openslides.utils.views import (
|
||||
TemplateView, RedirectView, UpdateView, CreateView, DeleteView, PDFView,
|
||||
DetailView, ListView, FormView, QuestionMixin)
|
||||
DetailView, ListView, FormView, QuestionMixin, SingleObjectMixin)
|
||||
from openslides.utils.template import Tab
|
||||
from openslides.utils.utils import html_strong
|
||||
from openslides.poll.views import PollFormView
|
||||
from openslides.projector.api import get_active_slide
|
||||
from openslides.projector.projector import Widget, SLIDE
|
||||
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,
|
||||
MotionCreateNewVersionMixin, ConfigForm)
|
||||
|
||||
@ -81,14 +82,16 @@ class MotionMixin(object):
|
||||
def post_save(self, form):
|
||||
super(MotionMixin, self).post_save(form)
|
||||
# TODO: only delete and save neccessary submitters and supporter
|
||||
self.object.submitter.all().delete()
|
||||
self.object.supporter.all().delete()
|
||||
MotionSubmitter.objects.bulk_create(
|
||||
[MotionSubmitter(motion=self.object, person=person)
|
||||
for person in form.cleaned_data['submitter']])
|
||||
MotionSupporter.objects.bulk_create(
|
||||
[MotionSupporter(motion=self.object, person=person)
|
||||
for person in form.cleaned_data['supporter']])
|
||||
if 'submitter' in form.cleaned_data:
|
||||
self.object.submitter.all().delete()
|
||||
MotionSubmitter.objects.bulk_create(
|
||||
[MotionSubmitter(motion=self.object, person=person)
|
||||
for person in form.cleaned_data['submitter']])
|
||||
if 'supporter' in form.cleaned_data:
|
||||
self.object.supporter.all().delete()
|
||||
MotionSupporter.objects.bulk_create(
|
||||
[MotionSupporter(motion=self.object, person=person)
|
||||
for person in form.cleaned_data['supporter']])
|
||||
|
||||
def get_form_class(self):
|
||||
form_classes = [BaseMotionForm]
|
||||
@ -180,6 +183,47 @@ motion_support = SupportView.as_view(support=True)
|
||||
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):
|
||||
permission_required = 'config.can_manage_config'
|
||||
form_class = ConfigForm
|
||||
|
Loading…
Reference in New Issue
Block a user