Fixed #1102 (New config option to set percent base for polls)
Use locale format for poll percentage decimal number.
This commit is contained in:
parent
579c9d49f0
commit
fa6c7d9d86
@ -21,6 +21,7 @@ Participants:
|
|||||||
Files:
|
Files:
|
||||||
- Enabled update and delete view for uploader refering to his own files.
|
- Enabled update and delete view for uploader refering to his own files.
|
||||||
Other:
|
Other:
|
||||||
|
- New config option to set the 100% base for polls (motions/elections).
|
||||||
- Changed widget api. Used new metaclass.
|
- Changed widget api. Used new metaclass.
|
||||||
- Changed api for plugins. Used entry points to detect them automaticly.
|
- Changed api for plugins. Used entry points to detect them automaticly.
|
||||||
- Renamed config api classes.
|
- Renamed config api classes.
|
||||||
|
@ -10,7 +10,7 @@ from django.utils.translation import ugettext_lazy, ugettext_noop
|
|||||||
from openslides.agenda.models import Item, Speaker
|
from openslides.agenda.models import Item, Speaker
|
||||||
from openslides.config.api import config
|
from openslides.config.api import config
|
||||||
from openslides.poll.models import (BaseOption, BasePoll, BaseVote,
|
from openslides.poll.models import (BaseOption, BasePoll, BaseVote,
|
||||||
CollectInvalid, CollectVotesCast,
|
CollectDefaultVotesMixin,
|
||||||
PublishPollMixin)
|
PublishPollMixin)
|
||||||
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
|
||||||
@ -271,7 +271,7 @@ class AssignmentOption(BaseOption):
|
|||||||
return unicode(self.candidate)
|
return unicode(self.candidate)
|
||||||
|
|
||||||
|
|
||||||
class AssignmentPoll(RelatedModelMixin, CollectInvalid, CollectVotesCast,
|
class AssignmentPoll(RelatedModelMixin, CollectDefaultVotesMixin,
|
||||||
PublishPollMixin, AbsoluteUrlMixin, BasePoll):
|
PublishPollMixin, AbsoluteUrlMixin, BasePoll):
|
||||||
option_class = AssignmentOption
|
option_class = AssignmentOption
|
||||||
assignment = models.ForeignKey(Assignment, related_name='poll_set')
|
assignment = models.ForeignKey(Assignment, related_name='poll_set')
|
||||||
@ -319,6 +319,9 @@ class AssignmentPoll(RelatedModelMixin, CollectInvalid, CollectVotesCast,
|
|||||||
def get_ballot(self):
|
def get_ballot(self):
|
||||||
return self.assignment.poll_set.filter(id__lte=self.id).count()
|
return self.assignment.poll_set.filter(id__lte=self.id).count()
|
||||||
|
|
||||||
|
def get_percent_base_choice(self):
|
||||||
|
return config['assignment_poll_100_percent_base']
|
||||||
|
|
||||||
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)
|
||||||
|
@ -7,6 +7,7 @@ from django.utils.translation import ugettext_lazy, ugettext_noop
|
|||||||
|
|
||||||
from openslides.config.api import ConfigGroup, ConfigGroupedCollection, ConfigVariable
|
from openslides.config.api import ConfigGroup, ConfigGroupedCollection, ConfigVariable
|
||||||
from openslides.config.signals import config_signal
|
from openslides.config.signals import config_signal
|
||||||
|
from openslides.poll.models import PERCENT_BASE_CHOICES
|
||||||
|
|
||||||
|
|
||||||
@receiver(config_signal, dispatch_uid='setup_assignment_config')
|
@receiver(config_signal, dispatch_uid='setup_assignment_config')
|
||||||
@ -26,6 +27,14 @@ def setup_assignment_config(sender, **kwargs):
|
|||||||
('auto', ugettext_lazy('Automatic assign of method')),
|
('auto', ugettext_lazy('Automatic assign of method')),
|
||||||
('votes', ugettext_lazy('Always one option per candidate')),
|
('votes', ugettext_lazy('Always one option per candidate')),
|
||||||
('yesnoabstain', ugettext_lazy('Always Yes-No-Abstain per candidate')))))
|
('yesnoabstain', ugettext_lazy('Always Yes-No-Abstain per candidate')))))
|
||||||
|
assignment_poll_100_percent_base = ConfigVariable(
|
||||||
|
name='assignment_poll_100_percent_base',
|
||||||
|
default_value='WITHOUT_INVALID',
|
||||||
|
form_field=forms.ChoiceField(
|
||||||
|
widget=forms.Select(),
|
||||||
|
required=False,
|
||||||
|
label=ugettext_lazy('The 100 % base of an election result consists of'),
|
||||||
|
choices=PERCENT_BASE_CHOICES))
|
||||||
assignment_pdf_ballot_papers_selection = ConfigVariable(
|
assignment_pdf_ballot_papers_selection = ConfigVariable(
|
||||||
name='assignment_pdf_ballot_papers_selection',
|
name='assignment_pdf_ballot_papers_selection',
|
||||||
default_value='CUSTOM_NUMBER',
|
default_value='CUSTOM_NUMBER',
|
||||||
@ -55,6 +64,7 @@ def setup_assignment_config(sender, **kwargs):
|
|||||||
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,
|
||||||
|
assignment_poll_100_percent_base,
|
||||||
assignment_pdf_ballot_papers_selection,
|
assignment_pdf_ballot_papers_selection,
|
||||||
assignment_pdf_ballot_papers_number,
|
assignment_pdf_ballot_papers_number,
|
||||||
assignment_publish_winner_results_only))
|
assignment_publish_winner_results_only))
|
||||||
|
@ -187,8 +187,8 @@
|
|||||||
<td>
|
<td>
|
||||||
{% if candidate in assignment.elected %}
|
{% if candidate in assignment.elected %}
|
||||||
{% if perms.assignment.can_manage_assignment %}
|
{% if perms.assignment.can_manage_assignment %}
|
||||||
<a class="election_link elected" href="{% url 'assignment_user_not_elected' assignment.id candidate.person_id %}"
|
<a class="election_link elected tooltip-bottom" href="{% url 'assignment_user_not_elected' assignment.id candidate.person_id %}"
|
||||||
rel="tooltip" data-original-title="{% trans 'Mark candidate as elected' %}"></a>
|
data-original-title="{% trans 'Mark candidate as elected' %}"></a>
|
||||||
{% else %}
|
{% else %}
|
||||||
<a class="elected">
|
<a class="elected">
|
||||||
<img src="{% static 'img/voting-yes.png' %}" class="tooltip-bottom" data-original-title="{% trans 'Candidate is elected' %}">
|
<img src="{% static 'img/voting-yes.png' %}" class="tooltip-bottom" data-original-title="{% trans 'Candidate is elected' %}">
|
||||||
@ -196,7 +196,8 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% else %}
|
{% else %}
|
||||||
{% if perms.assignment.can_manage_assignment %}
|
{% if perms.assignment.can_manage_assignment %}
|
||||||
<a class="election_link" href="{% url 'assignment_user_elected' assignment.id candidate.person_id %}"></a>
|
<a class="election_link tooltip-bottom" href="{% url 'assignment_user_elected' assignment.id candidate.person_id %}"
|
||||||
|
data-original-title="{% trans 'Mark candidate as elected' %}"></a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<a href="{{ candidate|absolute_url }}">{{ candidate }}</a>
|
<a href="{{ candidate|absolute_url }}">{{ candidate }}</a>
|
||||||
@ -221,13 +222,29 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'Valid votes' %}</td>
|
||||||
|
{% for poll in polls %}
|
||||||
|
{% if poll.published or perms.assignment.can_manage_assignment %}
|
||||||
|
<td style="white-space:nowrap;">
|
||||||
|
{% if poll.has_votes %}
|
||||||
|
<img src="{% static 'img/voting-yes-grey.png' %}" class="tooltip-left" data-original-title="{% trans 'Valid votes' %}">
|
||||||
|
{{ poll.print_votesvalid }}
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% if assignment.candidates and perms.assignment.can_manage_assignment and assignment.status == "vot" %}
|
||||||
|
<td></td>
|
||||||
|
{% endif %}
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>{% trans 'Invalid votes' %}</td>
|
<td>{% trans 'Invalid votes' %}</td>
|
||||||
{% for poll in polls %}
|
{% for poll in polls %}
|
||||||
{% if poll.published or perms.assignment.can_manage_assignment %}
|
{% if poll.published or perms.assignment.can_manage_assignment %}
|
||||||
<td style="white-space:nowrap;">
|
<td style="white-space:nowrap;">
|
||||||
{% if poll.has_votes %}
|
{% if poll.has_votes %}
|
||||||
<img src="{% static 'img/voting-invalid.png' %}" class="tooltip-left" data-original-title="{% trans 'Invalid' %}">
|
<img src="{% static 'img/voting-invalid.png' %}" class="tooltip-left" data-original-title="{% trans 'Invalid votes' %}">
|
||||||
{{ poll.print_votesinvalid }}
|
{{ poll.print_votesinvalid }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
@ -238,13 +255,13 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="info total">
|
<tr class="info total">
|
||||||
<td><strong>{% trans 'Votes cast' %}</strong></td>
|
<td>{% trans 'Votes cast' %}</td>
|
||||||
{% for poll in polls %}
|
{% for poll in polls %}
|
||||||
{% if poll.published or perms.assignment.can_manage_assignment %}
|
{% if poll.published or perms.assignment.can_manage_assignment %}
|
||||||
<td style="white-space:nowrap;">
|
<td style="white-space:nowrap;">
|
||||||
{% if poll.has_votes %}
|
{% if poll.has_votes %}
|
||||||
<img src="{% static 'img/voting-total.png' %}" class="tooltip-left" data-original-title="{% trans 'Votes cast' %}">
|
<img src="{% static 'img/voting-total.png' %}" class="tooltip-left" data-original-title="{% trans 'Votes cast' %}">
|
||||||
<strong>{{ poll.print_votescast }}</strong>
|
{{ poll.print_votescast }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -50,6 +50,16 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
<tr>
|
||||||
|
<td>{% trans "Valid votes" %}</td>
|
||||||
|
{% for value in poll.get_vote_values %}
|
||||||
|
{% if forloop.first %}
|
||||||
|
<td>{{ pollform.votesvalid.errors }}{{ pollform.votesvalid }}</td>
|
||||||
|
{% else %}
|
||||||
|
<td></td>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>{% trans "Invalid votes" %}</td>
|
<td>{% trans "Invalid votes" %}</td>
|
||||||
{% for value in poll.get_vote_values %}
|
{% for value in poll.get_vote_values %}
|
||||||
|
@ -80,12 +80,23 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% 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>
|
<tr>
|
||||||
<td>{% trans 'Invalid votes' %}</td>
|
<td>{% trans 'Invalid votes' %}</td>
|
||||||
{% for poll in polls %}
|
{% for poll in polls %}
|
||||||
<td style="white-space:nowrap;">
|
<td style="white-space:nowrap;">
|
||||||
{% if poll.has_votes %}
|
{% if poll.has_votes %}
|
||||||
<img src="{% static 'img/voting-invalid.png' %}" title="{% trans 'Invalid' %}">
|
<img src="{% static 'img/voting-invalid.png' %}" title="{% trans 'Invalid votes' %}">
|
||||||
{{ poll.print_votesinvalid }}
|
{{ poll.print_votesinvalid }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
@ -94,13 +105,13 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<tr class="total">
|
<tr class="total">
|
||||||
<td>
|
<td>
|
||||||
<strong>{% trans 'Votes cast' %}</strong>
|
{% trans 'Votes cast' %}
|
||||||
</td>
|
</td>
|
||||||
{% for poll in polls %}
|
{% for poll in polls %}
|
||||||
<td style="white-space:nowrap;">
|
<td style="white-space:nowrap;">
|
||||||
{% if poll.has_votes %}
|
{% if poll.has_votes %}
|
||||||
<img src="{% static 'img/voting-total.png' %}" title="{% trans 'Votes cast' %}">
|
<img src="{% static 'img/voting-total.png' %}" title="{% trans 'Votes cast' %}">
|
||||||
<strong>{{ poll.print_votescast }}</strong>
|
{{ poll.print_votescast }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
@ -423,19 +423,29 @@ class AssignmentPDF(PDFView):
|
|||||||
pass
|
pass
|
||||||
data_votes.append(row)
|
data_votes.append(row)
|
||||||
|
|
||||||
# Add votes invalid row
|
# Add valid votes row
|
||||||
footrow_one = []
|
if poll.votesvalid is not None:
|
||||||
footrow_one.append(_("Invalid votes"))
|
footrow_one = []
|
||||||
for poll in polls:
|
footrow_one.append(_("Valid votes"))
|
||||||
footrow_one.append(poll.print_votesinvalid())
|
for poll in polls:
|
||||||
data_votes.append(footrow_one)
|
footrow_one.append(poll.print_votesvalid())
|
||||||
|
data_votes.append(footrow_one)
|
||||||
|
|
||||||
|
# Add invalid votes row
|
||||||
|
if poll.votesinvalid is not None:
|
||||||
|
footrow_two = []
|
||||||
|
footrow_two.append(_("Invalid votes"))
|
||||||
|
for poll in polls:
|
||||||
|
footrow_two.append(poll.print_votesinvalid())
|
||||||
|
data_votes.append(footrow_two)
|
||||||
|
|
||||||
# Add votes cast row
|
# Add votes cast row
|
||||||
footrow_two = []
|
if poll.votescast is not None:
|
||||||
footrow_two.append(_("Votes cast"))
|
footrow_three = []
|
||||||
for poll in polls:
|
footrow_three.append(_("Votes cast"))
|
||||||
footrow_two.append(poll.print_votescast())
|
for poll in polls:
|
||||||
data_votes.append(footrow_two)
|
footrow_three.append(poll.print_votescast())
|
||||||
|
data_votes.append(footrow_three)
|
||||||
|
|
||||||
table_votes = Table(data_votes)
|
table_votes = Table(data_votes)
|
||||||
table_votes.setStyle(
|
table_votes.setStyle(
|
||||||
|
@ -9,8 +9,7 @@ from django.utils.translation import ugettext_lazy, ugettext_noop
|
|||||||
|
|
||||||
from openslides.config.api import config
|
from openslides.config.api import config
|
||||||
from openslides.mediafile.models import Mediafile
|
from openslides.mediafile.models import Mediafile
|
||||||
from openslides.poll.models import (BaseOption, BasePoll, BaseVote,
|
from openslides.poll.models import (BaseOption, BasePoll, BaseVote, CollectDefaultVotesMixin)
|
||||||
CollectInvalid, CollectVotesCast)
|
|
||||||
from openslides.projector.models import RelatedModelMixin, SlideMixin
|
from openslides.projector.models import RelatedModelMixin, SlideMixin
|
||||||
from jsonfield import JSONField
|
from jsonfield import JSONField
|
||||||
from openslides.utils.models import AbsoluteUrlMixin
|
from openslides.utils.models import AbsoluteUrlMixin
|
||||||
@ -694,8 +693,7 @@ class MotionOption(BaseOption):
|
|||||||
"""The VoteClass, to witch this Class links."""
|
"""The VoteClass, to witch this Class links."""
|
||||||
|
|
||||||
|
|
||||||
class MotionPoll(RelatedModelMixin, CollectInvalid, CollectVotesCast,
|
class MotionPoll(RelatedModelMixin, CollectDefaultVotesMixin, AbsoluteUrlMixin, BasePoll):
|
||||||
AbsoluteUrlMixin, BasePoll):
|
|
||||||
"""The Class to saves the poll results for a motion poll."""
|
"""The Class to saves the poll results for a motion poll."""
|
||||||
|
|
||||||
motion = models.ForeignKey(Motion, related_name='polls')
|
motion = models.ForeignKey(Motion, related_name='polls')
|
||||||
@ -746,6 +744,9 @@ class MotionPoll(RelatedModelMixin, CollectInvalid, CollectVotesCast,
|
|||||||
def get_related_model(self):
|
def get_related_model(self):
|
||||||
return self.motion
|
return self.motion
|
||||||
|
|
||||||
|
def get_percent_base_choice(self):
|
||||||
|
return config['motion_poll_100_percent_base']
|
||||||
|
|
||||||
|
|
||||||
class State(models.Model):
|
class State(models.Model):
|
||||||
"""
|
"""
|
||||||
|
@ -113,17 +113,21 @@ def motion_to_pdf(pdf, motion):
|
|||||||
for poll in polls:
|
for poll in polls:
|
||||||
ballotcounter += 1
|
ballotcounter += 1
|
||||||
option = poll.get_options()[0]
|
option = poll.get_options()[0]
|
||||||
yes, no, abstain, invalid, votecast = (
|
yes, no, abstain = (option['Yes'], option['No'], option['Abstain'])
|
||||||
option['Yes'], option['No'], option['Abstain'],
|
valid, invalid, votescast = ('', '', '')
|
||||||
poll.print_votesinvalid(), poll.print_votescast())
|
if poll.votesvalid is not None:
|
||||||
|
valid = "<br/>%s: %s" % (_("Valid votes"), poll.print_votesvalid())
|
||||||
|
if poll.votesinvalid is not None:
|
||||||
|
invalid = "<br/>%s: %s" % (_("Invalid votes"), poll.print_votesinvalid())
|
||||||
|
if poll.votescast is not None:
|
||||||
|
votescast = "<br/>%s: %s" % (_("Votes cast"), poll.print_votescast())
|
||||||
if len(polls) > 1:
|
if len(polls) > 1:
|
||||||
cell6b.append(Paragraph("%s. %s" % (ballotcounter, _("Vote")),
|
cell6b.append(Paragraph("%s. %s" % (ballotcounter, _("Vote")),
|
||||||
stylesheet['Bold']))
|
stylesheet['Bold']))
|
||||||
cell6b.append(Paragraph(
|
cell6b.append(Paragraph(
|
||||||
"%s: %s <br/> %s: %s <br/> %s: %s <br/> %s: %s <br/> %s: %s" %
|
"%s: %s <br/> %s: %s <br/> %s: %s <br/> %s %s %s" %
|
||||||
(_("Yes"), yes, _("No"), no, _("Abstention"), abstain, _("Invalid"),
|
(_("Yes"), yes, _("No"), no, _("Abstention"), abstain, valid, invalid, votescast),
|
||||||
invalid, _("Votes cast"), votecast), stylesheet['Normal']))
|
stylesheet['Normal']))
|
||||||
cell6b.append(Spacer(0, 0.2 * cm))
|
cell6b.append(Spacer(0, 0.2 * cm))
|
||||||
motion_data.append([cell6a, cell6b])
|
motion_data.append([cell6a, cell6b])
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ from django.utils.translation import ugettext_lazy, ugettext_noop
|
|||||||
from openslides.config.api import ConfigGroup, ConfigGroupedCollection, ConfigVariable
|
from openslides.config.api import ConfigGroup, ConfigGroupedCollection, ConfigVariable
|
||||||
from openslides.config.signals import config_signal
|
from openslides.config.signals import config_signal
|
||||||
from openslides.core.signals import post_database_setup
|
from openslides.core.signals import post_database_setup
|
||||||
|
from openslides.poll.models import PERCENT_BASE_CHOICES
|
||||||
|
|
||||||
from .models import State, Workflow
|
from .models import State, Workflow
|
||||||
|
|
||||||
@ -85,7 +86,15 @@ def setup_motion_config(sender, **kwargs):
|
|||||||
title=ugettext_lazy('Supporters'),
|
title=ugettext_lazy('Supporters'),
|
||||||
variables=(motion_min_supporters, motion_remove_supporters))
|
variables=(motion_min_supporters, motion_remove_supporters))
|
||||||
|
|
||||||
# Ballot papers
|
# Voting and ballot papers
|
||||||
|
motion_poll_100_percent_base = ConfigVariable(
|
||||||
|
name='motion_poll_100_percent_base',
|
||||||
|
default_value='WITHOUT_INVALID',
|
||||||
|
form_field=forms.ChoiceField(
|
||||||
|
widget=forms.Select(),
|
||||||
|
required=False,
|
||||||
|
label=ugettext_lazy('The 100 % base of a voting result consists of'),
|
||||||
|
choices=PERCENT_BASE_CHOICES))
|
||||||
motion_pdf_ballot_papers_selection = ConfigVariable(
|
motion_pdf_ballot_papers_selection = ConfigVariable(
|
||||||
name='motion_pdf_ballot_papers_selection',
|
name='motion_pdf_ballot_papers_selection',
|
||||||
default_value='CUSTOM_NUMBER',
|
default_value='CUSTOM_NUMBER',
|
||||||
@ -106,8 +115,8 @@ def setup_motion_config(sender, **kwargs):
|
|||||||
min_value=1,
|
min_value=1,
|
||||||
label=ugettext_lazy('Custom number of ballot papers')))
|
label=ugettext_lazy('Custom number of ballot papers')))
|
||||||
group_ballot_papers = ConfigGroup(
|
group_ballot_papers = ConfigGroup(
|
||||||
title=ugettext_lazy('Ballot papers'),
|
title=ugettext_lazy('Voting and ballot papers'),
|
||||||
variables=(motion_pdf_ballot_papers_selection, motion_pdf_ballot_papers_number))
|
variables=(motion_poll_100_percent_base, motion_pdf_ballot_papers_selection, motion_pdf_ballot_papers_number))
|
||||||
|
|
||||||
# PDF
|
# PDF
|
||||||
motion_pdf_title = ConfigVariable(
|
motion_pdf_title = ConfigVariable(
|
||||||
|
@ -50,3 +50,9 @@ td.diff_header {
|
|||||||
#motion-vote-results img {
|
#motion-vote-results img {
|
||||||
margin-top: -4px;
|
margin-top: -4px;
|
||||||
}
|
}
|
||||||
|
#motion-vote-results .resultline {
|
||||||
|
border-top: 1px solid;
|
||||||
|
padding-top: 5px;
|
||||||
|
margin: 5px 0;
|
||||||
|
width: 10em;
|
||||||
|
}
|
||||||
|
@ -221,10 +221,21 @@
|
|||||||
<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>
|
||||||
<img src="{% static 'img/voting-invalid.png' %}" title="{% trans 'Invalid' %}"> {{ poll.print_votesinvalid }}<br>
|
{% if poll.votesvalid != None or poll.votesinvalid != None %}
|
||||||
<div style="border-top: 1px solid; padding-top: 5px; margin: 5px 0; width: 10em;">
|
<div class="resultline">
|
||||||
<img src="{% static 'img/voting-total.png' %}" title="{% trans 'Votes cast' %}"> {{ poll.print_votescast }}
|
{% if poll.votesvalid != None %}
|
||||||
</div>
|
<img src="{% static 'img/voting-yes-grey.png' %}" title="{% trans 'Valid votes' %}"> {{ poll.print_votesvalid }}<br>
|
||||||
|
{% endif %}
|
||||||
|
{% if poll.votesinvalid != None %}
|
||||||
|
<img src="{% static 'img/voting-invalid.png' %}" title="{% trans 'Invalid votes' %}"> {{ poll.print_votesinvalid }}<br>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% if poll.votescast != None %}
|
||||||
|
<div class="resultline">
|
||||||
|
<img src="{% static 'img/voting-total.png' %}" title="{% trans 'Votes cast' %}"> {{ poll.print_votescast }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
{% else %}
|
{% else %}
|
||||||
{% if perms.motion.can_manage_motion %}
|
{% if perms.motion.can_manage_motion %}
|
||||||
|
@ -45,6 +45,10 @@
|
|||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<tr class="total">
|
<tr class="total">
|
||||||
|
<td>{% trans "Valid votes" %}</td>
|
||||||
|
<td>{{ pollform.votesvalid.errors }}{{ pollform.votesvalid }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="">
|
||||||
<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>
|
||||||
|
@ -23,9 +23,19 @@
|
|||||||
<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>
|
||||||
<img src="{% static 'img/voting-invalid.png' %}" title="{% trans 'Invalid' %}"> {{ poll.print_votesinvalid }}<br>
|
{% if poll.votesvalid != None or poll.votesinvalid != None %}
|
||||||
<hr>
|
<div class="resultline"></div>
|
||||||
<img src="{% static 'img/voting-total.png' %}" title="{% trans 'Votes cast' %}"> {{ poll.print_votescast }}
|
{% if poll.votesvalid != None %}
|
||||||
|
<img src="{% static 'img/voting-yes-grey.png' %}" title="{% trans 'Valid votes' %}"> {{ poll.print_votesvalid }}<br>
|
||||||
|
{% endif %}
|
||||||
|
{% if poll.votesinvalid != None %}
|
||||||
|
<img src="{% static 'img/voting-invalid.png' %}" title="{% trans 'Invalid votes' %}"> {{ poll.print_votesinvalid }}<br>
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
{% if poll.votescast != None %}
|
||||||
|
<div class="resultline"></div>
|
||||||
|
<img src="{% static 'img/voting-total.png' %}" title="{% trans 'Votes cast' %}"> {{ poll.print_votescast }}
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
{% else %}
|
{% else %}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import locale
|
||||||
|
|
||||||
from django.core.exceptions import ObjectDoesNotExist
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
@ -60,17 +62,28 @@ class BaseVote(models.Model):
|
|||||||
if raw:
|
if raw:
|
||||||
return self.weight
|
return self.weight
|
||||||
try:
|
try:
|
||||||
percent_base = self.option.poll.percent_base()
|
percent_base = self.option.poll.get_percent_base()
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
# The poll class is no child of CollectVotesCast
|
# The poll class is no child of CollectVotesCast
|
||||||
percent_base = 0
|
percent_base = 0
|
||||||
return print_value(self.weight, percent_base)
|
return print_value(self.weight, percent_base)
|
||||||
|
|
||||||
|
|
||||||
class CollectVotesCast(models.Model):
|
PERCENT_BASE_CHOICES = (
|
||||||
|
('WITHOUT_INVALID', ugettext_lazy('All valid votes (Yes + No + Abstention)')),
|
||||||
|
('WITH_INVALID', ugettext_lazy('All votes casts (valid + invalid votes)')),
|
||||||
|
('DISABLED', ugettext_lazy('Disabled (no percents)')))
|
||||||
|
|
||||||
|
|
||||||
|
class CollectDefaultVotesMixin(models.Model):
|
||||||
"""
|
"""
|
||||||
Mixin for a poll to collect the votes cast.
|
Mixin for a poll to collect the default vote values for valid votes,
|
||||||
|
invalid votes and votes cast.
|
||||||
"""
|
"""
|
||||||
|
votesvalid = MinMaxIntegerField(null=True, blank=True, min_value=-2,
|
||||||
|
verbose_name=ugettext_lazy('Votes valid'))
|
||||||
|
votesinvalid = MinMaxIntegerField(null=True, blank=True, min_value=-2,
|
||||||
|
verbose_name=ugettext_lazy('Votes invalid'))
|
||||||
votescast = MinMaxIntegerField(null=True, blank=True, min_value=-2,
|
votescast = MinMaxIntegerField(null=True, blank=True, min_value=-2,
|
||||||
verbose_name=ugettext_lazy('Votes cast'))
|
verbose_name=ugettext_lazy('Votes cast'))
|
||||||
|
|
||||||
@ -78,39 +91,46 @@ class CollectVotesCast(models.Model):
|
|||||||
abstract = True
|
abstract = True
|
||||||
|
|
||||||
def append_pollform_fields(self, fields):
|
def append_pollform_fields(self, fields):
|
||||||
fields.append('votescast')
|
fields.append('votesvalid')
|
||||||
super(CollectVotesCast, self).append_pollform_fields(fields)
|
|
||||||
|
|
||||||
def print_votescast(self):
|
|
||||||
return print_value(self.votescast, self.percent_base())
|
|
||||||
|
|
||||||
def percent_base(self):
|
|
||||||
if self.votescast > 0:
|
|
||||||
return 100 / float(self.votescast)
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
class CollectInvalid(models.Model):
|
|
||||||
"""
|
|
||||||
Mixin for a poll to collect invalid votes.
|
|
||||||
"""
|
|
||||||
votesinvalid = MinMaxIntegerField(null=True, blank=True, min_value=-2,
|
|
||||||
verbose_name=ugettext_lazy('Votes invalid'))
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
abstract = True
|
|
||||||
|
|
||||||
def append_pollform_fields(self, fields):
|
|
||||||
fields.append('votesinvalid')
|
fields.append('votesinvalid')
|
||||||
super(CollectInvalid, self).append_pollform_fields(fields)
|
fields.append('votescast')
|
||||||
|
super(CollectDefaultVotesMixin, self).append_pollform_fields(fields)
|
||||||
|
|
||||||
|
def get_percent_base_choice(self):
|
||||||
|
"""
|
||||||
|
Returns one of the three strings in PERCENT_BASE_CHOICES.
|
||||||
|
"""
|
||||||
|
raise NotImplementedError('You have to provide a get_percent_base_choice() method.')
|
||||||
|
|
||||||
|
def print_votesvalid(self):
|
||||||
|
if self.get_percent_base_choice() == 'DISABLED':
|
||||||
|
value = print_value(self.votesvalid, None)
|
||||||
|
else:
|
||||||
|
value = print_value(self.votesvalid, self.get_percent_base())
|
||||||
|
return value
|
||||||
|
|
||||||
def print_votesinvalid(self):
|
def print_votesinvalid(self):
|
||||||
try:
|
if self.get_percent_base_choice() == 'WITH_INVALID':
|
||||||
percent_base = self.percent_base()
|
value = print_value(self.votesinvalid, self.get_percent_base())
|
||||||
except AttributeError:
|
else:
|
||||||
# The poll class is no child of CollectVotesCast
|
value = print_value(self.votesinvalid, None)
|
||||||
percent_base = 0
|
return value
|
||||||
return print_value(self.votesinvalid, percent_base)
|
|
||||||
|
def print_votescast(self):
|
||||||
|
if self.get_percent_base_choice() == 'WITH_INVALID':
|
||||||
|
value = print_value(self.votescast, self.get_percent_base())
|
||||||
|
else:
|
||||||
|
value = print_value(self.votescast, None)
|
||||||
|
return value
|
||||||
|
|
||||||
|
def get_percent_base(self):
|
||||||
|
if self.get_percent_base_choice() == "WITHOUT_INVALID" and self.votesvalid > 0:
|
||||||
|
base = 100 / float(self.votesvalid)
|
||||||
|
elif self.get_percent_base_choice() == "WITH_INVALID" and self.votescast > 0:
|
||||||
|
base = 100 / float(self.votescast)
|
||||||
|
else:
|
||||||
|
base = None
|
||||||
|
return base
|
||||||
|
|
||||||
|
|
||||||
class PublishPollMixin(models.Model):
|
class PublishPollMixin(models.Model):
|
||||||
@ -251,7 +271,8 @@ def print_value(value, percent_base=0):
|
|||||||
verbose_value = _('undocumented')
|
verbose_value = _('undocumented')
|
||||||
else:
|
else:
|
||||||
if percent_base:
|
if percent_base:
|
||||||
verbose_value = u'%d (%.2f %%)' % (value, value * percent_base)
|
locale.setlocale(locale.LC_ALL, '')
|
||||||
|
verbose_value = u'%d (%s %%)' % (value, locale.format('%.1f', value * percent_base))
|
||||||
else:
|
else:
|
||||||
verbose_value = u'%s' % value
|
verbose_value = u'%s' % value
|
||||||
return verbose_value
|
return verbose_value
|
||||||
|
@ -104,9 +104,10 @@ li {
|
|||||||
.well h4.first {
|
.well h4.first {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
.well .results hr {
|
.resultline {
|
||||||
|
border-top: 1px solid;
|
||||||
margin: 5px 0;
|
margin: 5px 0;
|
||||||
border: 1px solid #E3E3E3;
|
width: 10em;
|
||||||
}
|
}
|
||||||
hr {
|
hr {
|
||||||
margin: 10px 0;
|
margin: 10px 0;
|
||||||
|
@ -86,10 +86,10 @@ class TestMotionDetailView(MotionViewTestCase):
|
|||||||
response = self.staff_client.post(
|
response = self.staff_client.post(
|
||||||
'/motion/1/poll/1/edit/',
|
'/motion/1/poll/1/edit/',
|
||||||
{'option-1-Yes': '10',
|
{'option-1-Yes': '10',
|
||||||
'pollform-votescast': '50'})
|
'pollform-votesvalid': '50'})
|
||||||
self.assertRedirects(response, '/motion/1/')
|
self.assertRedirects(response, '/motion/1/')
|
||||||
response = self.staff_client.get('/motion/1/')
|
response = self.staff_client.get('/motion/1/')
|
||||||
self.assertContains(response, '100.00 %')
|
self.assertContains(response, '50 (100')
|
||||||
|
|
||||||
def test_deleted_supporter(self):
|
def test_deleted_supporter(self):
|
||||||
config['motion_min_supporters'] = 1
|
config['motion_min_supporters'] = 1
|
||||||
|
Loading…
Reference in New Issue
Block a user