From 157d4bdc14dc1fd370b605002fb34db488767884 Mon Sep 17 00:00:00 2001 From: Emanuel Schuetze Date: Thu, 17 Oct 2013 11:34:54 +0200 Subject: [PATCH] New poll slides for motion and assignment. (Fixed #862) Move result table from assignment slide to assignment poll slide. Use "result" in singular for motion and election results. --- extras/win32-portable/openslides.exe | Bin 32256 -> 32256 bytes openslides/assignment/models.py | 24 ++++- openslides/assignment/signals.py | 4 +- openslides/assignment/slides.py | 3 +- .../assignment/assignment_detail.html | 24 +++-- ...oll_view.html => assignmentpoll_form.html} | 29 +++--- .../assignment/assignmentpoll_slide.html | 59 ++++++++++++ .../templates/assignment/slide.html | 89 +++--------------- openslides/assignment/urls.py | 18 ++-- openslides/assignment/views.py | 4 +- openslides/core/templates/core/search.html | 2 +- openslides/motion/models.py | 19 ++-- openslides/motion/pdf.py | 4 +- openslides/motion/slides.py | 3 +- openslides/motion/static/css/motion.css | 17 ++-- .../templates/motion/motion_detail.html | 44 +++++---- .../motion/templates/motion/motion_list.html | 2 +- .../{poll_form.html => motionpoll_form.html} | 20 ++-- .../templates/motion/motionpoll_slide.html | 54 +++++++++++ openslides/motion/templates/motion/slide.html | 10 +- openslides/motion/urls.py | 8 +- openslides/motion/views.py | 6 +- openslides/poll/models.py | 2 +- openslides/projector/static/css/projector.css | 16 +++- 24 files changed, 282 insertions(+), 179 deletions(-) rename openslides/assignment/templates/assignment/{poll_view.html => assignmentpoll_form.html} (68%) create mode 100644 openslides/assignment/templates/assignment/assignmentpoll_slide.html rename openslides/motion/templates/motion/{poll_form.html => motionpoll_form.html} (73%) create mode 100644 openslides/motion/templates/motion/motionpoll_slide.html diff --git a/extras/win32-portable/openslides.exe b/extras/win32-portable/openslides.exe index fed0eb63a217dc72f5382c941c100d320bdc6275..96fb7ae1f9a10e02bd1bbb13b0500a7b8be0fece 100644 GIT binary patch delta 16 XcmZqp!`SeLal@BVMw887OSw4!Mra3l delta 16 XcmZqp!`SeLal@BVM&r$2OSw4!Mq&qe diff --git a/openslides/assignment/models.py b/openslides/assignment/models.py index 3f1485ff9..06360cdf3 100644 --- a/openslides/assignment/models.py +++ b/openslides/assignment/models.py @@ -12,6 +12,7 @@ from openslides.config.api import config from openslides.poll.models import (BaseOption, BasePoll, BaseVote, CollectDefaultVotesMixin, PublishPollMixin) +from openslides.projector.api import get_active_object, update_projector from openslides.projector.models import RelatedModelMixin, SlideMixin from openslides.utils.exceptions import OpenSlidesError from openslides.utils.models import AbsoluteUrlMixin @@ -190,6 +191,11 @@ class Assignment(SlideMixin, AbsoluteUrlMixin, models.Model): candidate = self.assignment_candidates.get(person=person) candidate.elected = value candidate.save() + # update projector if assignment or assignmentpoll slide is active + active_object = get_active_object() + if (type(active_object) is type(self) and active_object.pk == self.pk) or \ + (type(active_object) is AssignmentPoll and active_object.assignment_id == self.pk): + update_projector() def is_elected(self, person): return person in self.elected @@ -271,8 +277,12 @@ class AssignmentOption(BaseOption): return unicode(self.candidate) -class AssignmentPoll(RelatedModelMixin, CollectDefaultVotesMixin, +class AssignmentPoll(SlideMixin, RelatedModelMixin, CollectDefaultVotesMixin, PublishPollMixin, AbsoluteUrlMixin, BasePoll): + + slide_callback_name = 'assignmentpoll' + """Name of the callback for the slide-system.""" + option_class = AssignmentOption assignment = models.ForeignKey(Assignment, related_name='poll_set') yesnoabstain = models.NullBooleanField() @@ -284,10 +294,15 @@ class AssignmentPoll(RelatedModelMixin, CollectDefaultVotesMixin, return _("Ballot %d") % self.get_ballot() def get_absolute_url(self, link='update'): + """ + Return an URL for the poll. + + The keyargument 'link' can be 'update' or 'delete'. + """ if link == 'update': - url = reverse('assignment_poll_view', args=[str(self.pk)]) + url = reverse('assignmentpoll_update', args=[str(self.pk)]) elif link == 'delete': - url = reverse('assignment_poll_delete', args=[str(self.pk)]) + url = reverse('assignmentpoll_delete', args=[str(self.pk)]) else: url = super(AssignmentPoll, self).get_absolute_url(link) return url @@ -325,3 +340,6 @@ class AssignmentPoll(RelatedModelMixin, CollectDefaultVotesMixin, def append_pollform_fields(self, fields): fields.append('description') super(AssignmentPoll, self).append_pollform_fields(fields) + + def get_slide_context(self, **context): + return super(AssignmentPoll, self).get_slide_context(poll=self) diff --git a/openslides/assignment/signals.py b/openslides/assignment/signals.py index 642eab5f3..88b4c745f 100644 --- a/openslides/assignment/signals.py +++ b/openslides/assignment/signals.py @@ -59,8 +59,8 @@ def setup_assignment_config(sender, **kwargs): default_value=False, form_field=forms.BooleanField( required=False, - label=ugettext_lazy('Only publish voting results for selected ' - 'winners (Projector view only)'))) + label=ugettext_lazy('Publish election result for elected candidates only ' + '(projector view)'))) group_ballot = ConfigGroup( title=ugettext_lazy('Ballot and ballot papers'), variables=(assignment_poll_vote_values, diff --git a/openslides/assignment/slides.py b/openslides/assignment/slides.py index 9435b25f1..edfa347ef 100644 --- a/openslides/assignment/slides.py +++ b/openslides/assignment/slides.py @@ -2,6 +2,7 @@ from openslides.projector.api import register_slide_model -from .models import Assignment +from .models import Assignment, AssignmentPoll register_slide_model(Assignment, 'assignment/slide.html') +register_slide_model(AssignmentPoll, 'assignment/assignmentpoll_slide.html') diff --git a/openslides/assignment/templates/assignment/assignment_detail.html b/openslides/assignment/templates/assignment/assignment_detail.html index f833af89b..70d14af5e 100644 --- a/openslides/assignment/templates/assignment/assignment_detail.html +++ b/openslides/assignment/templates/assignment/assignment_detail.html @@ -18,6 +18,8 @@ {% block content %}

{{ assignment }} +
+ {% trans "Election" %} {% trans "Back to overview" %} PDF @@ -141,9 +143,9 @@ {% endif %} - + {% if assignment.status != "sea" or polls.exists %} -

{% trans "Election results" %}

+

{% trans "Election result" %}

{% if polls.exists %} @@ -154,8 +156,8 @@ {{ poll.get_ballot|ordinal|safe }} {% trans 'ballot' %} {% if perms.assignment.can_manage_assignment %} + href="{% url 'assignmentpoll_publish_status' poll.id %}" + rel="tooltip" data-original-title="{% trans 'Publish result' %}"> {% if poll.published %} {% else %} @@ -164,11 +166,15 @@

- + + - -

{% endif %} @@ -176,7 +182,7 @@ {% endfor %} {% if assignment.candidates and perms.assignment.can_manage_assignment and assignment.status == "vot" %} @@ -275,7 +281,7 @@ {% trans "No ballots available." %} {% if assignment.candidates and perms.assignment.can_manage_assignment and assignment.status == "vot" %}

- + {% trans 'New ballot' %}

diff --git a/openslides/assignment/templates/assignment/poll_view.html b/openslides/assignment/templates/assignment/assignmentpoll_form.html similarity index 68% rename from openslides/assignment/templates/assignment/poll_view.html rename to openslides/assignment/templates/assignment/assignmentpoll_form.html index 961397612..8ccd879e9 100644 --- a/openslides/assignment/templates/assignment/poll_view.html +++ b/openslides/assignment/templates/assignment/assignmentpoll_form.html @@ -13,15 +13,22 @@ {{ ballotnumber|ordinal|safe }} {% trans "ballot" %} - {% trans "Back to election" %} - - {% if perms.core.can_manage_projector %} - - - - {% endif %} + {% trans "Back to election" %} + + {% if perms.core.can_manage_projector %} + + + + + {% trans "Vote result" %} + {% endif %} + {% if perms.motion.can_manage_motion %} + + {% endif %} @@ -50,7 +57,7 @@ {% endfor %} {% endfor %} - + {% for value in poll.get_vote_values %} {% if forloop.first %} @@ -85,7 +92,7 @@

{% trans "Short description (for ballot paper)" %}:

{{ pollform.description }}

- + {% trans 'Ballot paper as PDF' %}

diff --git a/openslides/assignment/templates/assignment/assignmentpoll_slide.html b/openslides/assignment/templates/assignment/assignmentpoll_slide.html new file mode 100644 index 000000000..ac702cdd9 --- /dev/null +++ b/openslides/assignment/templates/assignment/assignmentpoll_slide.html @@ -0,0 +1,59 @@ +{% load i18n %} +{% load humanize %} +{% load staticfiles %} +{% load tags %} + +

+ {{ poll.assignment }} +
+ + {% trans "Election" %} + {% if poll.get_ballot > 1 %}| {{ poll.get_ballot|ordinal|safe }} {% trans "ballot" %}{% endif %} + +

+ +

+{% if poll.has_votes and poll.published %} +

- + {% trans 'New ballot' %}
{% trans "Valid votes" %}
+ {% for option in poll.get_options %} + + + + + {% endfor %} + {% if poll.votesvalid != None %} + + + + + {% endif %} + {% if poll.votesinvalid != None %} + + + + + {% endif %} + {% if poll.votescast != None %} + + + + + {% endif %} +
{{ option }} + {% if not "assignment_publish_winner_results_only"|get_config or option.candidate in poll.assignment.elected %} + {% if poll.yesnoabstain %} + {% trans 'Yes' %}: + {{ option.Yes }}
+ {% trans 'No' %}: + {{ option.No }}
+ {% trans 'Abstention' %}: + {{ option.Abstain }} + {% else %} + {{ option.Votes }} + {% endif %} + {% endif %} +
{% trans 'Valid votes' %}:{{ poll.print_votesvalid }}
{% trans 'Invalid votes' %}:{{ poll.print_votesinvalid }}
{% trans 'Votes cast' %}:{{ poll.print_votescast }}
+{% else %} + {% trans "No result available." %} +{% endif %} +

diff --git a/openslides/assignment/templates/assignment/slide.html b/openslides/assignment/templates/assignment/slide.html index aec360c7e..96c8dca1b 100644 --- a/openslides/assignment/templates/assignment/slide.html +++ b/openslides/assignment/templates/assignment/slide.html @@ -1,6 +1,7 @@ {% load i18n %} {% load staticfiles %} {% load tags %} +{% load humanize %}

@@ -44,15 +50,15 @@ {{ value.errors }}{{ value }} {% endfor %} - + {% trans "Valid votes" %} {{ pollform.votesvalid.errors }}{{ pollform.votesvalid }} - + {% trans "Invalid votes" %} {{ pollform.votesinvalid.errors }}{{ pollform.votesinvalid }} - + {% trans "Votes cast" %} {{ pollform.votescast.errors }}{{ pollform.votescast }} @@ -62,7 +68,7 @@

- + {% trans 'Ballot paper as PDF' %}

diff --git a/openslides/motion/templates/motion/motionpoll_slide.html b/openslides/motion/templates/motion/motionpoll_slide.html new file mode 100644 index 000000000..cc1fa0977 --- /dev/null +++ b/openslides/motion/templates/motion/motionpoll_slide.html @@ -0,0 +1,54 @@ +{% load i18n %} +{% load humanize %} +{% load staticfiles %} + +

+ {{ poll.motion.active_version.title }} +
+ + {% trans "Motion" %} {{ poll.motion.identifier|default:'' }} + {% if poll.motion.get_active_version.version_number > 1 %} | {% trans 'Version' %} {{ poll.motion.active_version.version_number }}{% endif %} + {% if poll.poll_number > 1 %}| {{ poll.poll_number|ordinal|safe }} {% trans "vote" %}{% endif %} + +

+ +

+ {% if poll.has_votes %} + {% with poll.get_options.0 as option %} + + + + + + + + + + + + + + {% if poll.votesvalid != None %} + + + + + {% endif %} + {% if poll.votesinvalid != None %} + + + + + {% endif %} + {% if poll.votescast != None %} + + + + + {% endif %} +
{% trans 'Yes' %}:{{ option.Yes }}
{% trans 'No' %}:{{ option.No }}
{% trans 'Abstention' %}:{{ option.No }}
{% trans 'Valid votes' %}:{{ poll.print_votesvalid }}
{% trans 'Invalid votes' %}:{{ poll.print_votesinvalid }}
{% trans 'Votes cast' %}:{{ poll.print_votescast }}
+ {% endwith %} + {% else %} + {% trans "No result available." %} + {% endif %} +

diff --git a/openslides/motion/templates/motion/slide.html b/openslides/motion/templates/motion/slide.html index e4f3c4ed2..bd8804385 100644 --- a/openslides/motion/templates/motion/slide.html +++ b/openslides/motion/templates/motion/slide.html @@ -8,7 +8,7 @@

{% trans "Status" %}:

{% trans motion.state.name %} - + {% with motion.polls.all as polls %} {% if polls.exists and polls.0.has_votes %} {% for poll in polls reversed %} @@ -19,12 +19,12 @@

{% trans "Poll result" %}:

{% endif %} {% with poll.get_options.0 as option %} -
+
{{ option.Yes }}
{{ option.No }}
{{ option.Abstain }}
{% if poll.votesvalid != None or poll.votesinvalid != None %} -
+
{% if poll.votesvalid != None %} {{ poll.print_votesvalid }}
{% endif %} @@ -33,14 +33,14 @@ {% endif %} {% endif %} {% if poll.votescast != None %} -
+
{{ poll.print_votescast }} {% endif %}
{% endwith %} {% else %} {% if poll|length == 1 %} - {% trans "No poll results available." %} + {% trans "No result available." %} {% endif %} {% endif %} {% endfor %} diff --git a/openslides/motion/urls.py b/openslides/motion/urls.py index 81d02ed0c..af8085b03 100644 --- a/openslides/motion/urls.py +++ b/openslides/motion/urls.py @@ -52,19 +52,19 @@ urlpatterns = patterns( url(r'^(?P\d+)/create_poll/$', 'poll_create', - name='motion_poll_create'), + name='motionpoll_create'), url(r'^(?P\d+)/poll/(?P\d+)/edit/$', 'poll_update', - name='motion_poll_update'), + name='motionpoll_update'), url(r'^(?P\d+)/poll/(?P\d+)/del/$', 'poll_delete', - name='motion_poll_delete'), + name='motionpoll_delete'), url(r'^(?P\d+)/poll/(?P\d+)/pdf/$', 'poll_pdf', - name='motion_poll_pdf'), + name='motionpoll_pdf'), url(r'^(?P\d+)/set_state/(?P\d+)/$', 'set_state', diff --git a/openslides/motion/views.py b/openslides/motion/views.py index ec547d313..7b1432780 100644 --- a/openslides/motion/views.py +++ b/openslides/motion/views.py @@ -483,7 +483,7 @@ class PollCreateView(SingleObjectMixin, RedirectView): """ permission_required = 'motion.can_manage_motion' model = Motion - url_name = 'motion_poll_detail' + url_name = 'motionpoll_detail' def get(self, request, *args, **kwargs): """ @@ -504,7 +504,7 @@ class PollCreateView(SingleObjectMixin, RedirectView): """ Return the URL to the UpdateView of the poll. """ - return reverse('motion_poll_update', args=[self.object.pk, self.poll.poll_number]) + return reverse('motionpoll_update', args=[self.object.pk, self.poll.poll_number]) poll_create = PollCreateView.as_view() @@ -545,7 +545,7 @@ class PollUpdateView(PollMixin, PollFormView): Poll Class to use for this view. """ - template_name = 'motion/poll_form.html' + template_name = 'motion/motionpoll_form.html' def get_context_data(self, **kwargs): """ diff --git a/openslides/poll/models.py b/openslides/poll/models.py index b5f8014d3..cd5bd7d1b 100644 --- a/openslides/poll/models.py +++ b/openslides/poll/models.py @@ -36,7 +36,7 @@ class BaseOption(models.Model): try: return self.get_votes().get(value=name) except self.get_vote_class().DoesNotExist: - return None + raise KeyError class BaseVote(models.Model): diff --git a/openslides/projector/static/css/projector.css b/openslides/projector/static/css/projector.css index b2370afc0..e1a29d990 100644 --- a/openslides/projector/static/css/projector.css +++ b/openslides/projector/static/css/projector.css @@ -53,7 +53,7 @@ body{ background: url(../img/glyphicons_054_clock_big.png) no-repeat scroll 0px 0px; } -/*** CONTENT with base style elements***/ +/*** CONTENT with base style elements ***/ #content { position: absolute; left: 75px; @@ -104,11 +104,21 @@ li { .well h4.first { margin-top: 0; } -.resultline { +.well .result { + line-height: 30px; +} +.well .result hr { border-top: 1px solid; margin: 5px 0; width: 10em; } +.result.big { + font-size: 120%; + line-height: 40px; +} +.result .bold { + font-weight: bold; +} hr { margin: 10px 0; } @@ -187,6 +197,6 @@ tr.total td { border-top: 1px solid #333333; background-color: #e3e3e3; } -td.elected { +tr.elected td { background-color: #BED4DE !important; }