Merge pull request #2189 from matakuka/YesNoVotes

adding option "yes/no for each candidate"
This commit is contained in:
Emanuel Schütze 2016-06-12 11:11:10 +02:00 committed by GitHub
commit 7a94b6511b
9 changed files with 113 additions and 39 deletions

View File

@ -22,7 +22,8 @@ def get_config_variables():
choices=( choices=(
{'value': 'auto', 'display_name': ugettext_lazy('Automatic assign of method')}, {'value': 'auto', 'display_name': ugettext_lazy('Automatic assign of method')},
{'value': 'votes', 'display_name': ugettext_lazy('Always one option per candidate')}, {'value': 'votes', 'display_name': ugettext_lazy('Always one option per candidate')},
{'value': 'yesnoabstain', 'display_name': ugettext_lazy('Always Yes-No-Abstain per candidate')}), {'value': 'yesnoabstain', 'display_name': ugettext_lazy('Always Yes-No-Abstain per candidate')},
{'value': 'yesno', 'display_name': ugettext_lazy('Always Yes/No per candidate')}),
weight=410, weight=410,
group=ugettext_lazy('Elections'), group=ugettext_lazy('Elections'),
subgroup=ugettext_lazy('Ballot and ballot papers')) subgroup=ugettext_lazy('Ballot and ballot papers'))

View File

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.7 on 2016-06-09 14:20
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('assignments', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='assignmentpoll',
name='yesno',
field=models.BooleanField(default=False),
),
]

View File

@ -209,20 +209,28 @@ class Assignment(RESTModelMixin, models.Model):
# Find out the method of the election # Find out the method of the election
if config['assignments_poll_vote_values'] == 'votes': if config['assignments_poll_vote_values'] == 'votes':
yesnoabstain = False yesnoabstain = False
yesno = False
elif config['assignments_poll_vote_values'] == 'yesnoabstain': elif config['assignments_poll_vote_values'] == 'yesnoabstain':
yesnoabstain = True yesnoabstain = True
yesno = False
elif config['assignments_poll_vote_values'] == 'yesno':
yesnoabstain = False
yesno = True
else: else:
# config['assignments_poll_vote_values'] == 'auto' # config['assignments_poll_vote_values'] == 'auto'
# candidates <= available posts -> yes/no/abstain # candidates <= available posts -> yes/no/abstain
if len(candidates) <= (self.open_posts - self.elected.count()): if len(candidates) <= (self.open_posts - self.elected.count()):
yesno = False
yesnoabstain = True yesnoabstain = True
else: else:
yesno = False
yesnoabstain = False yesnoabstain = False
# Create the poll with the candidates. # Create the poll with the candidates.
poll = self.polls.create( poll = self.polls.create(
description=self.poll_description_default, description=self.poll_description_default,
yesnoabstain=yesnoabstain) yesnoabstain=yesnoabstain,
yesno=yesno)
poll.set_options({'candidate': user} for user in candidates) poll.set_options({'candidate': user} for user in candidates)
# Add all candidates to list of speakers of related agenda item # Add all candidates to list of speakers of related agenda item
@ -357,6 +365,7 @@ class AssignmentPoll(RESTModelMixin, CollectDefaultVotesMixin,
on_delete=models.CASCADE, on_delete=models.CASCADE,
related_name='polls') related_name='polls')
yesnoabstain = models.BooleanField(default=False) yesnoabstain = models.BooleanField(default=False)
yesno = models.BooleanField(default=False)
description = models.CharField( description = models.CharField(
max_length=79, max_length=79,
blank=True) blank=True)
@ -370,6 +379,8 @@ class AssignmentPoll(RESTModelMixin, CollectDefaultVotesMixin,
def get_vote_values(self): def get_vote_values(self):
if self.yesnoabstain: if self.yesnoabstain:
return ['Yes', 'No', 'Abstain'] return ['Yes', 'No', 'Abstain']
elif self.yesno:
return ['Yes', 'No']
else: else:
return ['Votes'] return ['Votes']

View File

@ -97,6 +97,7 @@ class AssignmentAllPollSerializer(ModelSerializer):
fields = ( fields = (
'id', 'id',
'yesnoabstain', 'yesnoabstain',
'yesno',
'description', 'description',
'published', 'published',
'options', 'options',
@ -168,6 +169,7 @@ class AssignmentShortPollSerializer(AssignmentAllPollSerializer):
fields = ( fields = (
'id', 'id',
'yesnoabstain', 'yesnoabstain',
'yesno',
'description', 'description',
'published', 'published',
'options', 'options',

View File

@ -42,7 +42,7 @@ angular.module('OpenSlidesApp.assignments', [])
} else if (config == "WITH_INVALID" && poll.votescast > 0 && vote.weight >= 0) { } else if (config == "WITH_INVALID" && poll.votescast > 0 && vote.weight >= 0) {
percentNumber = Math.round(vote.weight * 100 / (poll.votescast) * 10) / 10; percentNumber = Math.round(vote.weight * 100 / (poll.votescast) * 10) / 10;
} }
if (percentNumber) { if (percentNumber >=0) {
percentStr = "(" + percentNumber + "%)"; percentStr = "(" + percentNumber + "%)";
} }
votes.push({ votes.push({

View File

@ -531,16 +531,27 @@ angular.module('OpenSlidesApp.assignments.site', ['OpenSlidesApp.assignments'])
// add dynamic form fields // add dynamic form fields
assignmentpoll.options.forEach(function(option) { assignmentpoll.options.forEach(function(option) {
var defaultValue; var defaultValue;
if (assignmentpoll.yesnoabstain) { if (assignmentpoll.yesnoabstain || assignmentpoll.yesno) {
defaultValue = { if (assignmentpoll.yesnoabstain) {
'yes': '', defaultValue = {
'no': '', 'yes': '',
'abstain': '' 'no': '',
}; 'abstain': ''
};
}
else {
defaultValue = {
'yes': '',
'no': ''
};
}
if (option.votes.length) { if (option.votes.length) {
defaultValue.yes = option.votes[0].weight; defaultValue.yes = option.votes[0].weight;
defaultValue.no = option.votes[1].weight; defaultValue.no = option.votes[1].weight;
defaultValue.abstain = option.votes[2].weight; if (assignmentpoll.yesnoabstain){
defaultValue.abstain = option.votes[2].weight;
}
} }
$scope.formFields.push( $scope.formFields.push(
{ {
@ -566,7 +577,9 @@ angular.module('OpenSlidesApp.assignments.site', ['OpenSlidesApp.assignments'])
required: true required: true
}, },
defaultValue: defaultValue.no defaultValue: defaultValue.no
}, });
if (assignmentpoll.yesnoabstain){
$scope.formFields.push(
{ {
key:'abstain_' + option.candidate_id, key:'abstain_' + option.candidate_id,
type: 'input', type: 'input',
@ -577,6 +590,7 @@ angular.module('OpenSlidesApp.assignments.site', ['OpenSlidesApp.assignments'])
}, },
defaultValue: defaultValue.abstain defaultValue: defaultValue.abstain
}); });
}
} else { } else {
if (option.votes.length) { if (option.votes.length) {
defaultValue = option.votes[0].weight; defaultValue = option.votes[0].weight;
@ -642,6 +656,13 @@ angular.module('OpenSlidesApp.assignments.site', ['OpenSlidesApp.assignments'])
"Abstain": poll['abstain_' + option.candidate_id] "Abstain": poll['abstain_' + option.candidate_id]
}); });
}); });
} else if (assignmentpoll.yesno) {
assignmentpoll.options.forEach(function(option) {
votes.push({
"Yes": poll['yes_' + option.candidate_id],
"No": poll['no_' + option.candidate_id]
});
});
} else { } else {
assignmentpoll.options.forEach(function(option) { assignmentpoll.options.forEach(function(option) {
votes.push({ votes.push({
@ -651,11 +672,11 @@ angular.module('OpenSlidesApp.assignments.site', ['OpenSlidesApp.assignments'])
} }
// save change poll object on server // save change poll object on server
poll.DSUpdate({ poll.DSUpdate({
assignment_id: poll.assignment_id, assignment_id: poll.assignment_id,
votes: votes, votes: votes,
votesvalid: poll.votesvalid, votesvalid: poll.votesvalid,
votesinvalid: poll.votesinvalid, votesinvalid: poll.votesinvalid,
votescast: poll.votescast votescast: poll.votescast
}) })
.then(function(success) { .then(function(success) {
$scope.alert.show = false; $scope.alert.show = false;

View File

@ -187,7 +187,7 @@
<td ng-if="poll.has_votes"> <td ng-if="poll.has_votes">
<div ng-init="votes = option.getVotes()"> <div ng-init="votes = option.getVotes()">
<div ng-repeat="vote in votes"> <div ng-repeat="vote in votes">
<span ng-if="poll.yesnoabstain">{{ vote.label }}:</span> <span ng-if="poll.yesnoabstain || poll.yesno">{{ vote.label }}:</span>
{{ vote.value }} {{ vote.percentStr }} {{ vote.value }} {{ vote.percentStr }}
<div ng-if="vote.percentNumber"> <div ng-if="vote.percentNumber">
<uib-progressbar value="vote.percentNumber" type="success"></uib-progressbar> <uib-progressbar value="vote.percentNumber" type="success"></uib-progressbar>

View File

@ -51,27 +51,30 @@
<!-- votes --> <!-- votes -->
<td ng-if="poll.has_votes"> <td ng-if="poll.has_votes">
<div ng-init="votes = option.getVotes()"> <div ng-init="votes = option.getVotes()">
<div ng-show="poll.yesnoabstain"> <div ng-show="poll.yesnoabstain || poll.yesno">
<span> <span ng-show="poll.yesnoabstain">
{{ votes[0].label }}: <strong>{{ votes[0].value }}</strong> · {{ votes[0].label }}: <strong>{{ votes[0].value }}</strong> ·
{{ votes[1].label }}: {{ votes[1].value }} · {{ votes[1].label }}: {{ votes[1].value }} ·
{{ votes[2].label }}: {{ votes[2].value }} </span> {{ votes[2].label }}: {{ votes[2].value }} </span>
<uib-progress ng-if="votes[0].percentNumber"> <span ng-show="poll.yesno">
{{ votes[0].label }}: <strong>{{ votes[0].value }}</strong> ·
{{ votes[1].label }}: {{ votes[1].value }}</span>
<uib-progress ng-if="votes[0].percentNumber>=0">
<uib-bar value="votes[0].percentNumber" type="success"> <uib-bar value="votes[0].percentNumber" type="success">
<span ng-hide="votes[0].percentNumber < 5">{{votes[0].percentNumber}} %</span> <span ng-hide="votes[0].percentNumber < 5">{{votes[0].percentNumber}} %</span>
</uib-bar> </uib-bar>
<uib-bar value="votes[1].percentNumber" type="danger"> <uib-bar value="votes[1].percentNumber" type="danger">
<span ng-hide="votes[1].percentNumber < 5">{{votes[1].percentNumber}} %</span> <span ng-hide="votes[1].percentNumber < 5">{{votes[1].percentNumber}} %</span>
</uib-bar> </uib-bar>
<uib-bar value="votes[2].percentNumber" type="warning"></uib-bar> <uib-bar value="votes[2].percentNumber" type="warning">
<span ng-hide="votes[2].percentNumber < 5">{{votes[2].percentNumber}} %</span> <span ng-hide="votes[2].percentNumber < 5">{{votes[2].percentNumber}} %</span>
</uib-bar> </uib-bar>
</uib-progress> </uib-progress>
</div> </div>
<div ng-hide="poll.yesnoabstain"> <div ng-hide="poll.yesnoabstain || poll.yesno">
<div ng-repeat="vote in votes"> <div ng-repeat="vote in votes">
{{ vote.value }} {{ vote.percentStr }} - {{vote.percentNumber}} {{ vote.value }} {{ vote.percentStr }}
<div ng-if="vote.percentNumber"> <div ng-if="vote.percentNumber >= 0">
<uib-progressbar value="vote.percentNumber" type="success"></uib-progressbar> <uib-progressbar value="vote.percentNumber" type="success"></uib-progressbar>
</div> </div>
</div> </div>

View File

@ -342,6 +342,10 @@ class AssignmentPDF(PDFView):
_("Y: %(YES)s\nN: %(NO)s\nA: %(ABSTAIN)s") _("Y: %(YES)s\nN: %(NO)s\nA: %(ABSTAIN)s")
% {'YES': vote['Yes'], 'NO': vote['No'], % {'YES': vote['Yes'], 'NO': vote['No'],
'ABSTAIN': vote['Abstain']}) 'ABSTAIN': vote['Abstain']})
elif 'Yes' in vote and 'No' in vote:
row.append(
_("Y: %(YES)s\nN: %(NO)s")
% {'YES': vote['Yes'], 'NO': vote['No']})
elif 'Votes' in vote: elif 'Votes' in vote:
row.append(vote['Votes']) row.append(vote['Votes'])
else: else:
@ -482,8 +486,8 @@ class AssignmentPollPDF(PDFView):
counter = 0 counter = 0
cellcolumnA = [] cellcolumnA = []
# Choose kind of ballot paper (YesNoAbstain or Yes) # Choose kind of ballot paper (YesNoAbstain, YesNo or Yes)
if self.poll.yesnoabstain: # YesNoAbstain ballot: max 27 candidates if self.poll.yesnoabstain or self.poll.yesno: # YesNoAbstain/YesNo ballot: max 27 candidates
for option in options: for option in options:
counter += 1 counter += 1
candidate = option.candidate candidate = option.candidate
@ -494,19 +498,31 @@ class AssignmentPollPDF(PDFView):
"(%s)" % candidate.structure_level, "(%s)" % candidate.structure_level,
stylesheet['Ballot_option_suffix_YNA'])) stylesheet['Ballot_option_suffix_YNA']))
else: else:
cell.append(Paragraph( if self.poll.yesnoabstain:
"&nbsp;", stylesheet['Ballot_option_suffix_YNA'])) cell.append(Paragraph(
cell.append(Paragraph("<font name='circlefont' size='15'>%(circle)s</font> \ "&nbsp;", stylesheet['Ballot_option_suffix_YNA']))
<font name='Ubuntu'>%(yes)s &nbsp;&nbsp;&nbsp;</font> \ cell.append(Paragraph("<font name='circlefont' size='15'>%(circle)s</font> \
<font name='circlefont' size='15'>%(circle)s</font> \ <font name='Ubuntu'>%(yes)s &nbsp;&nbsp;&nbsp;</font> \
<font name='Ubuntu'>%(no)s &nbsp;&nbsp;&nbsp;</font> \ <font name='circlefont' size='15'>%(circle)s</font> \
<font name='circlefont' size='15'>%(circle)s</font> \ <font name='Ubuntu'>%(no)s &nbsp;&nbsp;&nbsp;</font> \
<font name='Ubuntu'>%(abstain)s</font>" % <font name='circlefont' size='15'>%(circle)s</font> \
{'circle': circle, <font name='Ubuntu'>%(abstain)s</font>" %
'yes': _("Yes"), {'circle': circle,
'no': _("No"), 'yes': _("Yes"),
'abstain': _("Abstain")}, 'no': _("No"),
stylesheet['Ballot_option_circle_YNA'])) 'abstain': _("Abstain")},
stylesheet['Ballot_option_circle_YNA']))
else:
cell.append(Paragraph(
"&nbsp;", stylesheet['Ballot_option_suffix_YNA']))
cell.append(Paragraph("<font name='circlefont' size='15'>%(circle)s</font> \
<font name='Ubuntu'>%(yes)s &nbsp;&nbsp;&nbsp;</font> \
<font name='circlefont' size='15'>%(circle)s</font> \
<font name='Ubuntu'>%(no)s &nbsp;&nbsp;&nbsp;</font>" %
{'circle': circle,
'yes': _("Yes"),
'no': _("No")},
stylesheet['Ballot_option_circle_YNA']))
if counter == 13: if counter == 13:
cellcolumnA = cell cellcolumnA = cell
cell = [] cell = []