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.
This commit is contained in:
Emanuel Schuetze 2013-10-17 11:34:54 +02:00 committed by Emanuel Schütze
parent 6521d6b095
commit 157d4bdc14
24 changed files with 282 additions and 179 deletions

Binary file not shown.

View File

@ -12,6 +12,7 @@ from openslides.config.api import config
from openslides.poll.models import (BaseOption, BasePoll, BaseVote, from openslides.poll.models import (BaseOption, BasePoll, BaseVote,
CollectDefaultVotesMixin, CollectDefaultVotesMixin,
PublishPollMixin) PublishPollMixin)
from openslides.projector.api import get_active_object, update_projector
from openslides.projector.models import RelatedModelMixin, SlideMixin from openslides.projector.models import RelatedModelMixin, SlideMixin
from openslides.utils.exceptions import OpenSlidesError from openslides.utils.exceptions import OpenSlidesError
from openslides.utils.models import AbsoluteUrlMixin from openslides.utils.models import AbsoluteUrlMixin
@ -190,6 +191,11 @@ class Assignment(SlideMixin, AbsoluteUrlMixin, models.Model):
candidate = self.assignment_candidates.get(person=person) candidate = self.assignment_candidates.get(person=person)
candidate.elected = value candidate.elected = value
candidate.save() 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): def is_elected(self, person):
return person in self.elected return person in self.elected
@ -271,8 +277,12 @@ class AssignmentOption(BaseOption):
return unicode(self.candidate) return unicode(self.candidate)
class AssignmentPoll(RelatedModelMixin, CollectDefaultVotesMixin, class AssignmentPoll(SlideMixin, RelatedModelMixin, CollectDefaultVotesMixin,
PublishPollMixin, AbsoluteUrlMixin, BasePoll): PublishPollMixin, AbsoluteUrlMixin, BasePoll):
slide_callback_name = 'assignmentpoll'
"""Name of the callback for the slide-system."""
option_class = AssignmentOption option_class = AssignmentOption
assignment = models.ForeignKey(Assignment, related_name='poll_set') assignment = models.ForeignKey(Assignment, related_name='poll_set')
yesnoabstain = models.NullBooleanField() yesnoabstain = models.NullBooleanField()
@ -284,10 +294,15 @@ class AssignmentPoll(RelatedModelMixin, CollectDefaultVotesMixin,
return _("Ballot %d") % self.get_ballot() return _("Ballot %d") % self.get_ballot()
def get_absolute_url(self, link='update'): def get_absolute_url(self, link='update'):
"""
Return an URL for the poll.
The keyargument 'link' can be 'update' or 'delete'.
"""
if link == 'update': if link == 'update':
url = reverse('assignment_poll_view', args=[str(self.pk)]) url = reverse('assignmentpoll_update', args=[str(self.pk)])
elif link == 'delete': elif link == 'delete':
url = reverse('assignment_poll_delete', args=[str(self.pk)]) url = reverse('assignmentpoll_delete', args=[str(self.pk)])
else: else:
url = super(AssignmentPoll, self).get_absolute_url(link) url = super(AssignmentPoll, self).get_absolute_url(link)
return url return url
@ -325,3 +340,6 @@ class AssignmentPoll(RelatedModelMixin, CollectDefaultVotesMixin,
def append_pollform_fields(self, fields): def append_pollform_fields(self, fields):
fields.append('description') fields.append('description')
super(AssignmentPoll, self).append_pollform_fields(fields) super(AssignmentPoll, self).append_pollform_fields(fields)
def get_slide_context(self, **context):
return super(AssignmentPoll, self).get_slide_context(poll=self)

View File

@ -59,8 +59,8 @@ def setup_assignment_config(sender, **kwargs):
default_value=False, default_value=False,
form_field=forms.BooleanField( form_field=forms.BooleanField(
required=False, required=False,
label=ugettext_lazy('Only publish voting results for selected ' label=ugettext_lazy('Publish election result for elected candidates only '
'winners (Projector view only)'))) '(projector view)')))
group_ballot = ConfigGroup( group_ballot = ConfigGroup(
title=ugettext_lazy('Ballot and ballot papers'), title=ugettext_lazy('Ballot and ballot papers'),
variables=(assignment_poll_vote_values, variables=(assignment_poll_vote_values,

View File

@ -2,6 +2,7 @@
from openslides.projector.api import register_slide_model 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(Assignment, 'assignment/slide.html')
register_slide_model(AssignmentPoll, 'assignment/assignmentpoll_slide.html')

View File

@ -18,6 +18,8 @@
{% block content %} {% block content %}
<h1> <h1>
{{ assignment }} {{ assignment }}
<br>
<small>{% trans "Election" %}</small>
<small class="pull-right"> <small class="pull-right">
<a href="{% url 'assignment_list' %}" class="btn btn-mini"><i class="icon-chevron-left"></i> {% trans "Back to overview" %}</a> <a href="{% url 'assignment_list' %}" class="btn btn-mini"><i class="icon-chevron-left"></i> {% trans "Back to overview" %}</a>
<a href="{% url 'assignment_pdf' assignment.id %}" class="btn btn-mini" rel="tooltip" data-original-title="{% trans 'Print election as PDF' %}" target="_blank"><i class="icon-print"></i> PDF</a> <a href="{% url 'assignment_pdf' assignment.id %}" class="btn btn-mini" rel="tooltip" data-original-title="{% trans 'Print election as PDF' %}" target="_blank"><i class="icon-print"></i> PDF</a>
@ -141,9 +143,9 @@
{% endif %} {% endif %}
<!-- Results --> <!-- Election result -->
{% if assignment.status != "sea" or polls.exists %} {% if assignment.status != "sea" or polls.exists %}
<h4>{% trans "Election results" %}</h4> <h4>{% trans "Election result" %}</h4>
{% if polls.exists %} {% if polls.exists %}
<table class="table table-striped table-bordered"> <table class="table table-striped table-bordered">
<tr> <tr>
@ -154,8 +156,8 @@
{{ poll.get_ballot|ordinal|safe }} {% trans 'ballot' %} {{ poll.get_ballot|ordinal|safe }} {% trans 'ballot' %}
{% if perms.assignment.can_manage_assignment %} {% if perms.assignment.can_manage_assignment %}
<a class="publish_link btn btn-mini btn-danger {% if poll.published %}btn-primary{% endif %}" <a class="publish_link btn btn-mini btn-danger {% if poll.published %}btn-primary{% endif %}"
href="{% url 'assignment_poll_publish_status' poll.id %}" href="{% url 'assignmentpoll_publish_status' poll.id %}"
rel="tooltip" data-original-title="{% trans 'Publish results' %}"> rel="tooltip" data-original-title="{% trans 'Publish result' %}">
{% if poll.published %} {% if poll.published %}
<i class="icon-checked-new_white"></i> <i class="icon-checked-new_white"></i>
{% else %} {% else %}
@ -164,11 +166,15 @@
</a> </a>
</p> </p>
<p class="text-center"> <p class="text-center">
<a href="{% url 'assignment_poll_pdf' poll.id %}" class="btn btn-mini" target="_blank" <a href="{{ poll|absolute_url:'projector' }}"
class="btn btn-mini activate_link {% if poll.is_active_slide %}btn-primary{% endif %}"
rel="tooltip" data-original-title="{% trans 'Show election result' %}">
<i class="icon-facetime-video {% if poll.is_active_slide %}icon-white{% endif %}"></i></a>
<a href="{% url 'assignmentpoll_pdf' poll.id %}" class="btn btn-mini" target="_blank"
rel="tooltip" data-original-title="{% trans 'Ballot paper as PDF' %}"><i class="icon-print"></i></a> rel="tooltip" data-original-title="{% trans 'Ballot paper as PDF' %}"><i class="icon-print"></i></a>
<a href="{% url 'assignment_poll_view' poll.id %}" class="btn btn-mini" <a href="{% url 'assignmentpoll_update' poll.id %}" class="btn btn-mini"
rel="tooltip" data-original-title="{% trans 'Edit' %}"><i class="icon-pencil"></i></a> rel="tooltip" data-original-title="{% trans 'Edit' %}"><i class="icon-pencil"></i></a>
<a href="{% url 'assignment_poll_delete' poll.id %}" class="btn btn-mini" <a href="{% url 'assignmentpoll_delete' poll.id %}" class="btn btn-mini"
rel="tooltip" data-original-title="{% trans 'Delete' %}"><i class="icon-remove"></i></a> rel="tooltip" data-original-title="{% trans 'Delete' %}"><i class="icon-remove"></i></a>
</p> </p>
{% endif %} {% endif %}
@ -176,7 +182,7 @@
{% endfor %} {% endfor %}
{% if assignment.candidates and perms.assignment.can_manage_assignment and assignment.status == "vot" %} {% if assignment.candidates and perms.assignment.can_manage_assignment and assignment.status == "vot" %}
<th class="span1 nobr"> <th class="span1 nobr">
<a href="{% url 'assignment_poll_create' assignment.pk %}" class="btn btn-mini"> <a href="{% url 'assignmentpoll_create' assignment.pk %}" class="btn btn-mini">
<i class="icon-plus"></i> {% trans 'New ballot' %} <i class="icon-plus"></i> {% trans 'New ballot' %}
</a> </a>
</th> </th>
@ -275,7 +281,7 @@
<i>{% trans "No ballots available." %}</i> <i>{% trans "No ballots available." %}</i>
{% if assignment.candidates and perms.assignment.can_manage_assignment and assignment.status == "vot" %} {% if assignment.candidates and perms.assignment.can_manage_assignment and assignment.status == "vot" %}
<p> <p>
<a href='{% url 'assignment_poll_create' assignment.id %}' class="btn"> <a href='{% url 'assignmentpoll_create' assignment.id %}' class="btn">
<i class="icon-plus"></i> {% trans 'New ballot' %} <i class="icon-plus"></i> {% trans 'New ballot' %}
</a> </a>
</p> </p>

View File

@ -21,6 +21,13 @@
rel="tooltip" data-original-title="{% trans 'Show election' %}"> rel="tooltip" data-original-title="{% trans 'Show election' %}">
<i class="icon-facetime-video {% if assignment.is_active_slide %}icon-white{% endif %}"></i> <i class="icon-facetime-video {% if assignment.is_active_slide %}icon-white{% endif %}"></i>
</a> </a>
<a class="btn btn-mini activate_link {% if poll.is_active_slide %}btn-primary{% endif %}" href="{{ poll|absolute_url:'projector' }}"
rel="tooltip" data-original-title="{% trans 'Show election result' %}">
<i class="icon-facetime-video {% if poll.is_active_slide %}icon-white{% endif %}"></i> {% trans "Vote result" %}</a>
{% endif %}
{% if perms.motion.can_manage_motion %}
<a class="btn btn-mini" href="{{ poll|absolute_url:'delete' }}"
rel="tooltip" data-original-title="{% trans 'Delete ballot' %}"><i class="icon-remove"></i></a>
{% endif %} {% endif %}
</small> </small>
</h1> </h1>
@ -50,7 +57,7 @@
{% endfor %} {% endfor %}
</tr> </tr>
{% endfor %} {% endfor %}
<tr> <tr class="total warning">
<td>{% trans "Valid votes" %}</td> <td>{% trans "Valid votes" %}</td>
{% for value in poll.get_vote_values %} {% for value in poll.get_vote_values %}
{% if forloop.first %} {% if forloop.first %}
@ -85,7 +92,7 @@
<p><strong>{% trans "Short description (for ballot paper)" %}:</strong></p> <p><strong>{% trans "Short description (for ballot paper)" %}:</strong></p>
<p class="normal-form">{{ pollform.description }}</p> <p class="normal-form">{{ pollform.description }}</p>
<p> <p>
<a href="{% url 'assignment_poll_pdf' poll.id %}" class="btn" target="_blank"> <a href="{% url 'assignmentpoll_pdf' poll.id %}" class="btn" target="_blank">
<i class="icon-print"></i> {% trans 'Ballot paper as PDF' %} <i class="icon-print"></i> {% trans 'Ballot paper as PDF' %}
</a> </a>
</p> </p>

View File

@ -0,0 +1,59 @@
{% load i18n %}
{% load humanize %}
{% load staticfiles %}
{% load tags %}
<h1>
{{ poll.assignment }}
<br>
<small>
{% trans "Election" %}
{% if poll.get_ballot > 1 %}| {{ poll.get_ballot|ordinal|safe }} {% trans "ballot" %}{% endif %}
</small>
</h1>
<p>
{% if poll.has_votes and poll.published %}
<table class="result big nobr">
{% for option in poll.get_options %}
<tr {% if option.candidate in poll.assignment.elected %}class="elected"{% endif %}>
<td>{{ option }}</td>
<td class="bold">
{% if not "assignment_publish_winner_results_only"|get_config or option.candidate in poll.assignment.elected %}
{% if poll.yesnoabstain %}
<img src="{% static 'img/voting-yes.png' %}"> {% trans 'Yes' %}:
{{ option.Yes }}<br>
<img src="{% static 'img/voting-no.png' %}"> {% trans 'No' %}:
{{ option.No }}<br>
<img src="{% static 'img/voting-abstention.png' %}"> {% trans 'Abstention' %}:
{{ option.Abstain }}
{% else %}
{{ option.Votes }}
{% endif %}
{% endif %}
</td>
</tr>
{% endfor %}
{% if poll.votesvalid != None %}
<tr class="total">
<td><img src="{% static 'img/voting-yes-grey.png' %}"> {% trans 'Valid votes' %}:</td>
<td class="bold">{{ poll.print_votesvalid }}</td>
</tr>
{% endif %}
{% if poll.votesinvalid != None %}
<tr class="total">
<td><img src="{% static 'img/voting-invalid.png' %}"> {% trans 'Invalid votes' %}:</td>
<td class="bold">{{ poll.print_votesinvalid }}</td>
</tr>
{% endif %}
{% if poll.votescast != None %}
<tr class="total">
<td><img src="{% static 'img/voting-total.png' %}"> {% trans 'Votes cast' %}:</td>
<td class="bold">{{ poll.print_votescast }}</td>
</tr>
{% endif %}
</table>
{% else %}
<i>{% trans "No result available." %}</i>
{% endif %}
</p>

View File

@ -1,6 +1,7 @@
{% load i18n %} {% load i18n %}
{% load staticfiles %} {% load staticfiles %}
{% load tags %} {% load tags %}
{% load humanize %}
<div id="sidebar"> <div id="sidebar">
<div class="well"> <div class="well">
@ -38,83 +39,15 @@
<p><br></p> <p><br></p>
{% endif %} {% endif %}
{% if polls.exists %} {% if assignment.status == "fin" %}
<h3>{% trans "Election results" %}</h3> <h3>{% trans "Elected candidates" %}</h3>
<table class="table-striped table-bordered"> <ol>
<tr> {% for person in assignment.elected %}
<th>{% trans "Candidates" %}</th> <li>{{ person }}</li>
{% for poll in polls %} {% empty %}
<th> <li style="list-style: none outside none;">
<nobr>{{ poll.get_ballot }}. {% trans "ballot" %}</nobr> <i>{% trans "No candidates elected." %}</i>
</th> </li>
{% endfor %} {% endfor %}
</tr> </ol>
{% for candidate, poll_list in vote_results.items %}
<tr>
<td class="{% if candidate in assignment.elected %} elected{% endif %}">
{% if candidate in assignment.elected %}
<a class="elected">
<img src="{% static 'img/voting-yes.png' %}" title="{% trans 'Candidate is elected' %}">
</a>
{% endif %}
{{ candidate }}
</td>
{% for vote in poll_list %}
<td style="white-space:nowrap;"{% if candidate in assignment.elected %} class="elected"{% endif %}>
{% if not "assignment_publish_winner_results_only"|get_config or candidate in assignment.elected %}
{% if 'Yes' in vote and 'No' in vote and 'Abstain' in vote %}
<img src="{% static 'img/voting-yes.png' %}" title="{% trans 'Yes' %}"> {{ vote.Yes }}<br>
<img src="{% static 'img/voting-no.png' %}" title="{% trans 'No' %}"> {{ vote.No }}<br>
<img src="{% static 'img/voting-abstention.png' %}" title="{% trans 'Abstention' %}"> {{ vote.Abstain }}<br>
{% elif 'Votes' in vote %}
<img src="{% static 'img/voting-yes.png' %}" title="{% trans 'Yes' %}"> {{ vote.Votes }}
{% elif vote == None %}
{% trans 'was not a <br> candidate'%}
{% else %}
&nbsp;
{% endif %}
{% else %}
&nbsp;
{% endif %}
</td>
{% endfor %}
</tr>
{% endfor %}
<tr class="total">
<td>{% trans 'Valid votes' %}</td>
{% for poll in polls %}
<td style="white-space:nowrap;">
{% if poll.has_votes %}
<img src="{% static 'img/voting-yes-grey.png' %}" title="{% trans 'Valid votes' %}">
{{ poll.print_votesvalid }}
{% endif %}
</td>
{% endfor %}
</tr>
<tr>
<td>{% trans 'Invalid votes' %}</td>
{% for poll in polls %}
<td style="white-space:nowrap;">
{% if poll.has_votes %}
<img src="{% static 'img/voting-invalid.png' %}" title="{% trans 'Invalid votes' %}">
{{ poll.print_votesinvalid }}
{% endif %}
</td>
{% endfor %}
</tr>
<tr class="total">
<td>
{% trans 'Votes cast' %}
</td>
{% for poll in polls %}
<td style="white-space:nowrap;">
{% if poll.has_votes %}
<img src="{% static 'img/voting-total.png' %}" title="{% trans 'Votes cast' %}">
{{ poll.print_votescast }}
{% endif %}
</td>
{% endfor %}
</tr>
</table>
{% endif %} {% endif %}

View File

@ -42,10 +42,6 @@ urlpatterns = patterns(
views.AssignmentRunOtherDeleteView.as_view(), views.AssignmentRunOtherDeleteView.as_view(),
name='assignment_delother'), name='assignment_delother'),
url(r'^poll/(?P<poll_id>\d+)/print/$',
views.AssignmentPollPDF.as_view(),
name='assignment_poll_pdf'),
url(r'^(?P<pk>\d+)/agenda/$', url(r'^(?P<pk>\d+)/agenda/$',
views.CreateRelatedAgendaItemView.as_view(), views.CreateRelatedAgendaItemView.as_view(),
name='assignment_create_agenda'), name='assignment_create_agenda'),
@ -60,21 +56,25 @@ urlpatterns = patterns(
url(r'^(?P<pk>\d+)/gen_poll/$', url(r'^(?P<pk>\d+)/gen_poll/$',
views.PollCreateView.as_view(), views.PollCreateView.as_view(),
name='assignment_poll_create'), name='assignmentpoll_create'),
url(r'^poll/(?P<poll_id>\d+)/$', url(r'^poll/(?P<poll_id>\d+)/edit/$',
views.PollUpdateView.as_view(), views.PollUpdateView.as_view(),
name='assignment_poll_view'), name='assignmentpoll_update'),
url(r'^poll/(?P<pk>\d+)/del/$', url(r'^poll/(?P<pk>\d+)/del/$',
views.AssignmentPollDeleteView.as_view(), views.AssignmentPollDeleteView.as_view(),
name='assignment_poll_delete'), name='assignmentpoll_delete'),
url(r'^poll/(?P<poll_id>\d+)/print/$',
views.AssignmentPollPDF.as_view(),
name='assignmentpoll_pdf'),
# TODO: use seperate urls to publish and unpublish the poll # TODO: use seperate urls to publish and unpublish the poll
# see assignment_user_elected # see assignment_user_elected
url(r'^poll/(?P<pk>\d+)/pub/$', url(r'^poll/(?P<pk>\d+)/pub/$',
views.SetPublishStatusView.as_view(), views.SetPublishStatusView.as_view(),
name='assignment_poll_publish_status'), name='assignmentpoll_publish_status'),
url(r'^(?P<pk>\d+)/elected/(?P<user_id>[^/]+)/$', url(r'^(?P<pk>\d+)/elected/(?P<user_id>[^/]+)/$',
views.SetElectedView.as_view(), views.SetElectedView.as_view(),

View File

@ -204,7 +204,7 @@ class PollCreateView(SingleObjectMixin, RedirectView):
class PollUpdateView(PollFormView): class PollUpdateView(PollFormView):
poll_class = AssignmentPoll poll_class = AssignmentPoll
template_name = 'assignment/poll_view.html' template_name = 'assignment/assignmentpoll_form.html'
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(PollUpdateView, self).get_context_data(**kwargs) context = super(PollUpdateView, self).get_context_data(**kwargs)
@ -382,7 +382,7 @@ class AssignmentPDF(PDFView):
# Left side # Left side
cell3a = [] cell3a = []
cell3a.append(Paragraph( cell3a.append(Paragraph(
"%s:" % (_("Vote results")), stylesheet['Heading4'])) "%s:" % (_("Vote result")), stylesheet['Heading4']))
if polls.count() == 1: if polls.count() == 1:
cell3a.append(Paragraph( cell3a.append(Paragraph(

View File

@ -12,7 +12,7 @@
{% if field.name == "q" %} {% if field.name == "q" %}
<label for="id_q">{% trans 'Search' %}:</label> <label for="id_q">{% trans 'Search' %}:</label>
<div class="input-append"> <div class="input-append">
<input type="text" id="id_q" name="q" class="" value="{{ field.value }}"> <input type="text" id="id_q" name="q" value="{{ field.value }}">
<button type="submit" class="btn">{% trans "Search" %}</button> <button type="submit" class="btn">{% trans "Search" %}</button>
</div> </div>
{% elif field.name == "models" %} {% elif field.name == "models" %}

View File

@ -693,8 +693,12 @@ class MotionOption(BaseOption):
"""The VoteClass, to witch this Class links.""" """The VoteClass, to witch this Class links."""
class MotionPoll(RelatedModelMixin, CollectDefaultVotesMixin, AbsoluteUrlMixin, BasePoll): class MotionPoll(SlideMixin, RelatedModelMixin, CollectDefaultVotesMixin,
"""The Class to saves the poll results for a motion poll.""" AbsoluteUrlMixin, BasePoll):
"""The Class to saves the vote result for a motion poll."""
slide_callback_name = 'motionpoll'
"""Name of the callback for the slide-system."""
motion = models.ForeignKey(Motion, related_name='polls') motion = models.ForeignKey(Motion, related_name='polls')
"""The motion to witch the object belongs.""" """The motion to witch the object belongs."""
@ -726,10 +730,10 @@ class MotionPoll(RelatedModelMixin, CollectDefaultVotesMixin, AbsoluteUrlMixin,
The keyargument 'link' can be 'update' or 'delete'. The keyargument 'link' can be 'update' or 'delete'.
""" """
if link == 'update': if link == 'update':
url = reverse('motion_poll_update', args=[str(self.motion.pk), url = reverse('motionpoll_update', args=[str(self.motion.pk),
str(self.poll_number)]) str(self.poll_number)])
elif link == 'delete': elif link == 'delete':
url = reverse('motion_poll_delete', args=[str(self.motion.pk), url = reverse('motionpoll_delete', args=[str(self.motion.pk),
str(self.poll_number)]) str(self.poll_number)])
else: else:
url = super(MotionPoll, self).get_absolute_url(link) url = super(MotionPoll, self).get_absolute_url(link)
@ -747,6 +751,9 @@ class MotionPoll(RelatedModelMixin, CollectDefaultVotesMixin, AbsoluteUrlMixin,
def get_percent_base_choice(self): def get_percent_base_choice(self):
return config['motion_poll_100_percent_base'] return config['motion_poll_100_percent_base']
def get_slide_context(self, **context):
return super(MotionPoll, self).get_slide_context(poll=self)
class State(models.Model): class State(models.Model):
""" """

View File

@ -97,7 +97,7 @@ def motion_to_pdf(pdf, motion):
cell5b.append(Paragraph("%s" % version.version_number, stylesheet['Normal'])) cell5b.append(Paragraph("%s" % version.version_number, stylesheet['Normal']))
motion_data.append([cell5a, cell5b]) motion_data.append([cell5a, cell5b])
# voting results # voting result
polls = [] polls = []
for poll in motion.polls.all(): for poll in motion.polls.all():
if not poll.has_votes(): if not poll.has_votes():
@ -108,7 +108,7 @@ def motion_to_pdf(pdf, motion):
cell6a = [] cell6a = []
cell6b = [] cell6b = []
cell6a.append(Paragraph("<font name='Ubuntu-Bold'>%s:</font>" % cell6a.append(Paragraph("<font name='Ubuntu-Bold'>%s:</font>" %
_("Vote results"), stylesheet['Heading4'])) _("Vote result"), stylesheet['Heading4']))
ballotcounter = 0 ballotcounter = 0
for poll in polls: for poll in polls:
ballotcounter += 1 ballotcounter += 1

View File

@ -2,6 +2,7 @@
from openslides.projector.api import register_slide_model from openslides.projector.api import register_slide_model
from .models import Motion from .models import Motion, MotionPoll
register_slide_model(Motion, 'motion/slide.html') register_slide_model(Motion, 'motion/slide.html')
register_slide_model(MotionPoll, 'motion/motionpoll_slide.html')

View File

@ -39,20 +39,15 @@ td.diff_header {
} }
/*** motion vote results list ***/ /*** motion vote result ***/
#motion-vote-results { .well .result img {
list-style: none;
}
#motion-vote-results li {
margin-top: 5px;
margin-bottom: 15px;
}
#motion-vote-results img {
margin-top: -4px; margin-top: -4px;
} }
#motion-vote-results .resultline { .well .result {
margin-bottom: 10px;
}
.well .result hr {
border-top: 1px solid; border-top: 1px solid;
padding-top: 5px;
margin: 5px 0; margin: 5px 0;
width: 10em; width: 10em;
} }

View File

@ -204,54 +204,60 @@
<span class="label label-info">{% trans motion.state.name %}</span> <span class="label label-info">{% trans motion.state.name %}</span>
<br> <br>
<!-- Vote results --> <!-- Vote result -->
<h5>{% trans "Vote results" %}:</h5> <h5>{% trans "Vote result" %}:</h5>
{% with motion.polls.all as polls %} {% with motion.polls.all as polls %}
<ul id="motion-vote-results">
{% for poll in polls %} {% for poll in polls %}
{% if perms.motion.can_manage_motion or poll.has_votes %} {% if perms.motion.can_manage_motion or poll.has_votes %}
<li><b>{{ poll.poll_number|ordinal|safe }} {% trans "vote" %}</b> <p>{{ poll.poll_number|ordinal|safe }} {% trans "vote" %}:<br>
{% if perms.motion.can_manage_motion %} {% if perms.motion.can_manage_motion %}
<a class="btn btn-mini" href="{{ poll|absolute_url:'update' }}" title="{% trans 'Edit Vote' %}"><i class="icon-pencil"></i></a> <a class="btn btn-mini activate_link {% if poll.is_active_slide %}btn-primary{% endif %}" href="{{ poll|absolute_url:'projector' }}"
<a class="btn btn-mini" href="{{ poll|absolute_url:'delete' }}" title="{% trans 'Delete Vote' %}"><i class="icon-remove"></i></a> rel="tooltip" data-original-title="{% trans 'Show vote result' %}">
<i class="icon-facetime-video {% if poll.is_active_slide %}icon-white{% endif %}"></i></a>
<a href="{% url 'motionpoll_pdf' motion.id poll.poll_number %}" class="btn btn-mini" target="_blank"
rel="tooltip" data-original-title="{% trans 'Ballot paper as PDF' %}"><i class="icon-print"></i></a>
<a class="btn btn-mini" href="{{ poll|absolute_url:'update' }}"
rel="tooltip" data-original-title="{% trans 'Edit Vote' %}"><i class="icon-pencil"></i></a>
<a class="btn btn-mini" href="{{ poll|absolute_url:'delete' }}"
rel="tooltip" data-original-title="{% trans 'Delete Vote' %}"><i class="icon-remove"></i></a>
</p>
{% endif %} {% endif %}
<br>
{% if poll.has_votes %} {% if poll.has_votes %}
<div class="result">
{% with poll.get_options.0 as option %} {% with poll.get_options.0 as option %}
<img src="{% static 'img/voting-yes.png' %}" title="{% trans 'Yes' %}"> {{ option.Yes }}<br> <img src="{% static 'img/voting-yes.png' %}" title="{% trans 'Yes' %}"> {{ option.Yes }}<br>
<img src="{% static 'img/voting-no.png' %}" title="{% trans 'No' %}"> {{ option.No }}<br> <img src="{% static 'img/voting-no.png' %}" title="{% trans 'No' %}"> {{ option.No }}<br>
<img src="{% static 'img/voting-abstention.png' %}" title="{% trans 'Abstention' %}"> {{ option.Abstain }}<br> <img src="{% static 'img/voting-abstention.png' %}" title="{% trans 'Abstention' %}"> {{ option.Abstain }}<br>
{% if poll.votesvalid != None or poll.votesinvalid != None %} {% if poll.votesvalid != None or poll.votesinvalid != None %}
<div class="resultline"> <hr>
{% if poll.votesvalid != None %} {% if poll.votesvalid != None %}
<img src="{% static 'img/voting-yes-grey.png' %}" title="{% trans 'Valid votes' %}"> {{ poll.print_votesvalid }}<br> <img src="{% static 'img/voting-yes-grey.png' %}" title="{% trans 'Valid votes' %}"> {{ poll.print_votesvalid }}<br>
{% endif %} {% endif %}
{% if poll.votesinvalid != None %} {% if poll.votesinvalid != None %}
<img src="{% static 'img/voting-invalid.png' %}" title="{% trans 'Invalid votes' %}"> {{ poll.print_votesinvalid }}<br> <img src="{% static 'img/voting-invalid.png' %}" title="{% trans 'Invalid votes' %}"> {{ poll.print_votesinvalid }}<br>
{% endif %} {% endif %}
</div>
{% endif %} {% endif %}
{% if poll.votescast != None %} {% if poll.votescast != None %}
<div class="resultline"> <hr>
<img src="{% static 'img/voting-total.png' %}" title="{% trans 'Votes cast' %}"> {{ poll.print_votescast }} <img src="{% static 'img/voting-total.png' %}" title="{% trans 'Votes cast' %}"> {{ poll.print_votescast }}
</div>
{% endif %} {% endif %}
{% endwith %} {% endwith %}
</div>
{% else %} {% else %}
{% if perms.motion.can_manage_motion %} {% if perms.motion.can_manage_motion %}
<span class="label label-info">{% trans 'No results' %}</span> <p><span class="label label-info">{% trans 'No result' %}</span></p>
{% endif %} {% endif %}
{% endif %} {% endif %}
</li>
{% endif %} {% endif %}
{% empty %} {% empty %}
<span style="margin-left:-25px;"></span>
{% endfor %} {% endfor %}
</ul>
{% if allowed_actions.create_poll %} {% if allowed_actions.create_poll %}
<a href="{% url 'motion_poll_create' motion.id %}" class="btn btn-mini"> <p>
<a href="{% url 'motionpoll_create' motion.id %}" class="btn btn-mini">
<i class="icon-assignment"></i> {% trans 'New vote' %} <i class="icon-assignment"></i> {% trans 'New vote' %}
</a> </a>
</p>
{% endif %} {% endif %}
{% endwith %} {% endwith %}

View File

@ -86,7 +86,7 @@
{% endif %}</td> {% endif %}</td>
<td> <td>
<span style="width: 1px; white-space: nowrap;"> <span style="width: 1px; white-space: nowrap;">
{% if perms.projector.can_manage_projector %} {% if perms.core.can_manage_projector %}
<a href="{{ motion|absolute_url:'projector' }}" class="activate_link btn {% if motion.is_active_slide %}btn-primary{% endif %} btn-mini" <a href="{{ motion|absolute_url:'projector' }}" class="activate_link btn {% if motion.is_active_slide %}btn-primary{% endif %} btn-mini"
rel="tooltip" data-original-title="{% trans 'Show motion' %}"> rel="tooltip" data-original-title="{% trans 'Show motion' %}">
<i class="icon-facetime-video {% if motion.is_active_slide %}icon-white{% endif %}"></i> <i class="icon-facetime-video {% if motion.is_active_slide %}icon-white{% endif %}"></i>

View File

@ -15,15 +15,21 @@
{% trans "Motion" %} {{ motion.identifier }}, {{ poll }} {% trans "Motion" %} {{ motion.identifier }}, {{ poll }}
</small> </small>
<small class="pull-right"> <small class="pull-right">
<div class="btn-toolbar">
<a href="{% url 'motion_detail' motion.id %}" class="btn btn-mini"><i class="icon-chevron-left"></i> {% trans "Back to motion" %}</a> <a href="{% url 'motion_detail' motion.id %}" class="btn btn-mini"><i class="icon-chevron-left"></i> {% trans "Back to motion" %}</a>
<!-- activate projector --> <!-- activate projector -->
{% if perms.core.can_manage_projector %} {% if perms.core.can_manage_projector %}
<a href="{{ motion|absolute_url:'projector' }}" class="activate_link btn {% if motion.is_active_slide %}btn-primary{% endif %} btn-mini" rel="tooltip" data-original-title="{% trans 'Show motion' %}"> <a href="{{ motion|absolute_url:'projector' }}" class="activate_link btn {% if motion.is_active_slide %}btn-primary{% endif %} btn-mini"
rel="tooltip" data-original-title="{% trans 'Show motion' %}">
<i class="icon-facetime-video {% if motion.is_active_slide %}icon-white{% endif %}"></i> <i class="icon-facetime-video {% if motion.is_active_slide %}icon-white{% endif %}"></i>
</a> </a>
<a class="btn btn-mini activate_link {% if poll.is_active_slide %}btn-primary{% endif %}" href="{{ poll|absolute_url:'projector' }}"
rel="tooltip" data-original-title="{% trans 'Show vote result' %}">
<i class="icon-facetime-video {% if poll.is_active_slide %}icon-white{% endif %}"></i> {% trans "Vote result" %}</a>
{% endif %}
{% if perms.motion.can_manage_motion %}
<a class="btn btn-mini" href="{{ poll|absolute_url:'delete' }}"
rel="tooltip" data-original-title="{% trans 'Delete Vote' %}"><i class="icon-remove"></i></a>
{% endif %} {% endif %}
</div>
</small> </small>
</h1> </h1>
@ -44,15 +50,15 @@
<td>{{ value.errors }}{{ value }}</td> <td>{{ value.errors }}{{ value }}</td>
</tr> </tr>
{% endfor %} {% endfor %}
<tr class="total"> <tr class="total warning">
<td>{% trans "Valid votes" %}</td> <td>{% trans "Valid votes" %}</td>
<td>{{ pollform.votesvalid.errors }}{{ pollform.votesvalid }}</td> <td>{{ pollform.votesvalid.errors }}{{ pollform.votesvalid }}</td>
</tr> </tr>
<tr class=""> <tr>
<td>{% trans "Invalid votes" %}</td> <td>{% trans "Invalid votes" %}</td>
<td>{{ pollform.votesinvalid.errors }}{{ pollform.votesinvalid }}</td> <td>{{ pollform.votesinvalid.errors }}{{ pollform.votesinvalid }}</td>
</tr> </tr>
<tr class="total"> <tr class="total warning">
<td>{% trans "Votes cast" %}</td> <td>{% trans "Votes cast" %}</td>
<td>{{ pollform.votescast.errors }}{{ pollform.votescast }}</td> <td>{{ pollform.votescast.errors }}{{ pollform.votescast }}</td>
</tr> </tr>
@ -62,7 +68,7 @@
<!-- ballot paper button --> <!-- ballot paper button -->
<p> <p>
<a href="{% url 'motion_poll_pdf' motion.id poll.poll_number %}" class="btn" target="_blank"> <a href="{% url 'motionpoll_pdf' motion.id poll.poll_number %}" class="btn" target="_blank">
<i class="icon-print"></i> {% trans 'Ballot paper as PDF' %} <i class="icon-print"></i> {% trans 'Ballot paper as PDF' %}
</a> </a>
</p> </p>

View File

@ -0,0 +1,54 @@
{% load i18n %}
{% load humanize %}
{% load staticfiles %}
<h1>
{{ poll.motion.active_version.title }}
<br>
<small>
{% 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 %}
</small>
</h1>
<p>
{% if poll.has_votes %}
{% with poll.get_options.0 as option %}
<table class="well result big">
<tr>
<td><img src="{% static 'img/voting-yes.png' %}"> {% trans 'Yes' %}:</td>
<td class="bold">{{ option.Yes }}</td>
</tr>
<tr>
<td><img src="{% static 'img/voting-no.png' %}"> {% trans 'No' %}:</td>
<td class="bold">{{ option.No }}</td>
</tr>
<tr>
<td><img src="{% static 'img/voting-abstention.png' %}"> {% trans 'Abstention' %}:</td>
<td class="bold">{{ option.No }}</td>
</tr>
{% if poll.votesvalid != None %}
<tr class="total">
<td><img src="{% static 'img/voting-yes-grey.png' %}"> {% trans 'Valid votes' %}:</td>
<td class="bold">{{ poll.print_votesvalid }}</td>
</tr>
{% endif %}
{% if poll.votesinvalid != None %}
<tr class="total">
<td><img src="{% static 'img/voting-invalid.png' %}"> {% trans 'Invalid votes' %}:</td>
<td class="bold">{{ poll.print_votesinvalid }}</td>
</tr>
{% endif %}
{% if poll.votescast != None %}
<tr class="total">
<td><img src="{% static 'img/voting-total.png' %}"> {% trans 'Votes cast' %}:</td>
<td class="bold">{{ poll.print_votescast }}</td>
</tr>
{% endif %}
</table>
{% endwith %}
{% else %}
<i>{% trans "No result available." %}</i>
{% endif %}
</p>

View File

@ -8,7 +8,7 @@
<h4 class="first">{% trans "Status" %}:</h4> <h4 class="first">{% trans "Status" %}:</h4>
{% trans motion.state.name %} {% trans motion.state.name %}
<!-- poll results --> <!-- vote result -->
{% with motion.polls.all as polls %} {% with motion.polls.all as polls %}
{% if polls.exists and polls.0.has_votes %} {% if polls.exists and polls.0.has_votes %}
{% for poll in polls reversed %} {% for poll in polls reversed %}
@ -19,12 +19,12 @@
<h4>{% trans "Poll result" %}:</h4> <h4>{% trans "Poll result" %}:</h4>
{% endif %} {% endif %}
{% with poll.get_options.0 as option %} {% with poll.get_options.0 as option %}
<div class="results"> <div class="result">
<img src="{% static 'img/voting-yes.png' %}" title="{% trans 'Yes' %}"> {{ option.Yes }} <br> <img src="{% static 'img/voting-yes.png' %}" title="{% trans 'Yes' %}"> {{ option.Yes }} <br>
<img src="{% static 'img/voting-no.png' %}" title="{% trans 'No' %}"> {{ option.No }} <br> <img src="{% static 'img/voting-no.png' %}" title="{% trans 'No' %}"> {{ option.No }} <br>
<img src="{% static 'img/voting-abstention.png' %}" title="{% trans 'Abstention' %}"> {{ option.Abstain }}<br> <img src="{% static 'img/voting-abstention.png' %}" title="{% trans 'Abstention' %}"> {{ option.Abstain }}<br>
{% if poll.votesvalid != None or poll.votesinvalid != None %} {% if poll.votesvalid != None or poll.votesinvalid != None %}
<div class="resultline"></div> <hr>
{% if poll.votesvalid != None %} {% if poll.votesvalid != None %}
<img src="{% static 'img/voting-yes-grey.png' %}" title="{% trans 'Valid votes' %}"> {{ poll.print_votesvalid }}<br> <img src="{% static 'img/voting-yes-grey.png' %}" title="{% trans 'Valid votes' %}"> {{ poll.print_votesvalid }}<br>
{% endif %} {% endif %}
@ -33,14 +33,14 @@
{% endif %} {% endif %}
{% endif %} {% endif %}
{% if poll.votescast != None %} {% if poll.votescast != None %}
<div class="resultline"></div> <hr>
<img src="{% static 'img/voting-total.png' %}" title="{% trans 'Votes cast' %}"> {{ poll.print_votescast }} <img src="{% static 'img/voting-total.png' %}" title="{% trans 'Votes cast' %}"> {{ poll.print_votescast }}
{% endif %} {% endif %}
</div> </div>
{% endwith %} {% endwith %}
{% else %} {% else %}
{% if poll|length == 1 %} {% if poll|length == 1 %}
<i>{% trans "No poll results available." %}</i> <i>{% trans "No result available." %}</i>
{% endif %} {% endif %}
{% endif %} {% endif %}
{% endfor %} {% endfor %}

View File

@ -52,19 +52,19 @@ urlpatterns = patterns(
url(r'^(?P<pk>\d+)/create_poll/$', url(r'^(?P<pk>\d+)/create_poll/$',
'poll_create', 'poll_create',
name='motion_poll_create'), name='motionpoll_create'),
url(r'^(?P<pk>\d+)/poll/(?P<poll_number>\d+)/edit/$', url(r'^(?P<pk>\d+)/poll/(?P<poll_number>\d+)/edit/$',
'poll_update', 'poll_update',
name='motion_poll_update'), name='motionpoll_update'),
url(r'^(?P<pk>\d+)/poll/(?P<poll_number>\d+)/del/$', url(r'^(?P<pk>\d+)/poll/(?P<poll_number>\d+)/del/$',
'poll_delete', 'poll_delete',
name='motion_poll_delete'), name='motionpoll_delete'),
url(r'^(?P<pk>\d+)/poll/(?P<poll_number>\d+)/pdf/$', url(r'^(?P<pk>\d+)/poll/(?P<poll_number>\d+)/pdf/$',
'poll_pdf', 'poll_pdf',
name='motion_poll_pdf'), name='motionpoll_pdf'),
url(r'^(?P<pk>\d+)/set_state/(?P<state>\d+)/$', url(r'^(?P<pk>\d+)/set_state/(?P<state>\d+)/$',
'set_state', 'set_state',

View File

@ -483,7 +483,7 @@ class PollCreateView(SingleObjectMixin, RedirectView):
""" """
permission_required = 'motion.can_manage_motion' permission_required = 'motion.can_manage_motion'
model = Motion model = Motion
url_name = 'motion_poll_detail' url_name = 'motionpoll_detail'
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
""" """
@ -504,7 +504,7 @@ class PollCreateView(SingleObjectMixin, RedirectView):
""" """
Return the URL to the UpdateView of the poll. 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() poll_create = PollCreateView.as_view()
@ -545,7 +545,7 @@ class PollUpdateView(PollMixin, PollFormView):
Poll Class to use for this view. 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): def get_context_data(self, **kwargs):
""" """

View File

@ -36,7 +36,7 @@ class BaseOption(models.Model):
try: try:
return self.get_votes().get(value=name) return self.get_votes().get(value=name)
except self.get_vote_class().DoesNotExist: except self.get_vote_class().DoesNotExist:
return None raise KeyError
class BaseVote(models.Model): class BaseVote(models.Model):

View File

@ -53,7 +53,7 @@ body{
background: url(../img/glyphicons_054_clock_big.png) no-repeat scroll 0px 0px; background: url(../img/glyphicons_054_clock_big.png) no-repeat scroll 0px 0px;
} }
/*** CONTENT with base style elements***/ /*** CONTENT with base style elements ***/
#content { #content {
position: absolute; position: absolute;
left: 75px; left: 75px;
@ -104,11 +104,21 @@ li {
.well h4.first { .well h4.first {
margin-top: 0; margin-top: 0;
} }
.resultline { .well .result {
line-height: 30px;
}
.well .result hr {
border-top: 1px solid; border-top: 1px solid;
margin: 5px 0; margin: 5px 0;
width: 10em; width: 10em;
} }
.result.big {
font-size: 120%;
line-height: 40px;
}
.result .bold {
font-weight: bold;
}
hr { hr {
margin: 10px 0; margin: 10px 0;
} }
@ -187,6 +197,6 @@ tr.total td {
border-top: 1px solid #333333; border-top: 1px solid #333333;
background-color: #e3e3e3; background-color: #e3e3e3;
} }
td.elected { tr.elected td {
background-color: #BED4DE !important; background-color: #BED4DE !important;
} }