Merge pull request #3774 from FinnStutzenstein/abstain-no-for-assignment-votes
Added general abstain/no fields for assignments. Used in votes mode.
This commit is contained in:
commit
e9ad439cdd
27
openslides/assignments/migrations/0004_auto_20180703_1523.py
Normal file
27
openslides/assignments/migrations/0004_auto_20180703_1523.py
Normal file
@ -0,0 +1,27 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.8 on 2018-07-03 13:23
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
from openslides.utils.models import MinMaxIntegerField
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('assignments', '0003_candidate_weight'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='assignmentpoll',
|
||||
name='votesabstain',
|
||||
field=MinMaxIntegerField(null=True, blank=True, min_value=-2),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='assignmentpoll',
|
||||
name='votesno',
|
||||
field=MinMaxIntegerField(null=True, blank=True, min_value=-2),
|
||||
),
|
||||
]
|
@ -19,7 +19,7 @@ from openslides.poll.models import (
|
||||
)
|
||||
from openslides.utils.autoupdate import inform_changed_data
|
||||
from openslides.utils.exceptions import OpenSlidesError
|
||||
from openslides.utils.models import RESTModelMixin
|
||||
from openslides.utils.models import MinMaxIntegerField, RESTModelMixin
|
||||
|
||||
from .access_permissions import AssignmentAccessPermissions
|
||||
|
||||
@ -423,6 +423,11 @@ class AssignmentPoll(RESTModelMixin, CollectDefaultVotesMixin, # type: ignore
|
||||
max_length=79,
|
||||
blank=True)
|
||||
|
||||
votesabstain = MinMaxIntegerField(null=True, blank=True, min_value=-2)
|
||||
""" General abstain votes, used for pollmethod 'votes' """
|
||||
votesno = MinMaxIntegerField(null=True, blank=True, min_value=-2)
|
||||
""" General no votes, used for pollmethod 'votes' """
|
||||
|
||||
class Meta:
|
||||
default_permissions = ()
|
||||
|
||||
|
@ -111,6 +111,8 @@ class AssignmentAllPollSerializer(ModelSerializer):
|
||||
'description',
|
||||
'published',
|
||||
'options',
|
||||
'votesabstain',
|
||||
'votesno',
|
||||
'votesvalid',
|
||||
'votesinvalid',
|
||||
'votescast',
|
||||
@ -160,6 +162,8 @@ class AssignmentAllPollSerializer(ModelSerializer):
|
||||
# Update remaining writeable fields.
|
||||
instance.description = validated_data.get('description', instance.description)
|
||||
instance.published = validated_data.get('published', instance.published)
|
||||
instance.votesabstain = validated_data.get('votesabstain', instance.votesabstain)
|
||||
instance.votesno = validated_data.get('votesno', instance.votesno)
|
||||
instance.votesvalid = validated_data.get('votesvalid', instance.votesvalid)
|
||||
instance.votesinvalid = validated_data.get('votesinvalid', instance.votesinvalid)
|
||||
instance.votescast = validated_data.get('votescast', instance.votescast)
|
||||
@ -182,6 +186,8 @@ class AssignmentShortPollSerializer(AssignmentAllPollSerializer):
|
||||
'description',
|
||||
'published',
|
||||
'options',
|
||||
'votesabstain',
|
||||
'votesno',
|
||||
'votesvalid',
|
||||
'votesinvalid',
|
||||
'votescast',
|
||||
|
@ -214,7 +214,9 @@ angular.module('OpenSlidesApp.assignments', [])
|
||||
if (_.findIndex(this.options, predicate) !== -1) {
|
||||
base = void 0;
|
||||
} else {
|
||||
if (typeof base === 'undefined' && type !== 'votescast' && type !== 'votesinvalid' && type !== 'votesvalid') {
|
||||
if (typeof base === 'undefined' && type !== 'votesabstain' &&
|
||||
type !== 'votesno' && type !== 'votescast' &&
|
||||
type !== 'votesinvalid' && type !== 'votesvalid') {
|
||||
base = _.reduce(this.options, function (sum, option) {
|
||||
return sum + option.votes[0].weight;
|
||||
}, 0);
|
||||
@ -240,6 +242,12 @@ angular.module('OpenSlidesApp.assignments', [])
|
||||
config = Config.get('assignments_poll_100_percent_base').value;
|
||||
|
||||
switch (type) {
|
||||
case 'votesabstain':
|
||||
vote = this.votesabstain;
|
||||
break;
|
||||
case 'votesno':
|
||||
vote = this.votesno;
|
||||
break;
|
||||
case 'votesinvalid':
|
||||
vote = this.votesinvalid;
|
||||
break;
|
||||
|
@ -67,7 +67,7 @@ angular.module('OpenSlidesApp.assignments.pdf', ['OpenSlidesApp.core.pdf'])
|
||||
var candidatesText = gettextCatalog.getString("Candidates") + ": ";
|
||||
var userList = [];
|
||||
|
||||
angular.forEach(candidates, function(assignmentsRelatedUser) {
|
||||
_.forEach(candidates, function(assignmentsRelatedUser) {
|
||||
userList.push({
|
||||
text: assignmentsRelatedUser.user.get_full_name(),
|
||||
margin: [0, 0, 0, 10],
|
||||
@ -114,30 +114,30 @@ angular.module('OpenSlidesApp.assignments.pdf', ['OpenSlidesApp.core.pdf'])
|
||||
|
||||
//creates the voting string for the result table and differentiates between special values
|
||||
var parseVoteValue = function(voteObject, printLabel) {
|
||||
var voteVal = "";
|
||||
var voteVal = '';
|
||||
if (voteObject) {
|
||||
if (printLabel) {
|
||||
voteVal += voteObject.label + ": ";
|
||||
voteVal += voteObject.label + ': ';
|
||||
}
|
||||
voteVal += voteObject.value;
|
||||
|
||||
if (voteObject.percentStr) {
|
||||
voteVal += " " + voteObject.percentStr;
|
||||
voteVal += ' ' + voteObject.percentStr;
|
||||
}
|
||||
}
|
||||
voteVal += "\n";
|
||||
voteVal += '\n';
|
||||
return voteVal;
|
||||
};
|
||||
|
||||
// creates the election result table
|
||||
var createPollResultTable = function() {
|
||||
var resultBody = [];
|
||||
angular.forEach(assignment.polls, function(poll, pollIndex) {
|
||||
_.forEach(assignment.polls, function(poll, pollIndex) {
|
||||
if (poll.published) {
|
||||
var pollTableBody = [];
|
||||
|
||||
resultBody.push({
|
||||
text: gettextCatalog.getString("Ballot") + " " + (pollIndex+1),
|
||||
text: gettextCatalog.getString('Ballot') + ' ' + (pollIndex+1),
|
||||
bold: true,
|
||||
style: 'textItem',
|
||||
margin: [0, 15, 0, 0]
|
||||
@ -145,16 +145,16 @@ angular.module('OpenSlidesApp.assignments.pdf', ['OpenSlidesApp.core.pdf'])
|
||||
|
||||
pollTableBody.push([
|
||||
{
|
||||
text: gettextCatalog.getString("Candidates"),
|
||||
text: gettextCatalog.getString('Candidates'),
|
||||
style: 'tableHeader',
|
||||
},
|
||||
{
|
||||
text: gettextCatalog.getString("Votes"),
|
||||
text: gettextCatalog.getString('Votes'),
|
||||
style: 'tableHeader',
|
||||
}
|
||||
]);
|
||||
|
||||
angular.forEach(poll.options, function(pollOption, optionIndex) {
|
||||
_.forEach(poll.options, function(pollOption, optionIndex) {
|
||||
var candidateName = pollOption.candidate.get_full_name();
|
||||
var votes = pollOption.getVotes(); // 0 = yes, 1 = no, 2 = abstain
|
||||
var tableLine = [];
|
||||
@ -169,7 +169,7 @@ angular.module('OpenSlidesApp.assignments.pdf', ['OpenSlidesApp.core.pdf'])
|
||||
);
|
||||
} else {
|
||||
var resultBlock = [];
|
||||
angular.forEach(votes, function(vote) {
|
||||
_.forEach(votes, function(vote) {
|
||||
resultBlock.push(parseVoteValue(vote, true));
|
||||
});
|
||||
tableLine.push({
|
||||
@ -181,44 +181,26 @@ angular.module('OpenSlidesApp.assignments.pdf', ['OpenSlidesApp.core.pdf'])
|
||||
pollTableBody.push(tableLine);
|
||||
});
|
||||
|
||||
if (poll.votesvalid) {
|
||||
pollTableBody.push([
|
||||
{
|
||||
text: gettextCatalog.getString("Valid ballots"),
|
||||
style: 'tableConclude'
|
||||
},
|
||||
{
|
||||
text: parseVoteValue(poll.getVote('votesvalid'), false),
|
||||
style: 'tableConclude'
|
||||
},
|
||||
]);
|
||||
}
|
||||
var pushConcludeRow = function (title, fieldName) {
|
||||
if (poll[fieldName]) {
|
||||
pollTableBody.push([
|
||||
{
|
||||
text: gettextCatalog.getString(title),
|
||||
style: 'tableConclude'
|
||||
},
|
||||
{
|
||||
text: parseVoteValue(poll.getVote(fieldName), false),
|
||||
style: 'tableConclude'
|
||||
},
|
||||
]);
|
||||
}
|
||||
};
|
||||
|
||||
if (poll.votesinvalid) {
|
||||
pollTableBody.push([
|
||||
{
|
||||
text: gettextCatalog.getString("Invalid ballots"),
|
||||
style: 'tableConclude'
|
||||
},
|
||||
{
|
||||
text: parseVoteValue(poll.getVote('votesinvalid'), false),
|
||||
style: 'tableConclude'
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
if (poll.votescast) {
|
||||
pollTableBody.push([
|
||||
{
|
||||
text: gettextCatalog.getString("Casted ballots"),
|
||||
style: 'tableConclude'
|
||||
},
|
||||
{
|
||||
text: parseVoteValue(poll.getVote('votescast'), false),
|
||||
style: 'tableConclude'
|
||||
},
|
||||
]);
|
||||
}
|
||||
pushConcludeRow('Abstain', 'votesabstain');
|
||||
pushConcludeRow('No', 'votesno');
|
||||
pushConcludeRow('Valid ballots', 'votesvalid');
|
||||
pushConcludeRow('Invalid ballots', 'votesinvalid');
|
||||
pushConcludeRow('Casted ballots', 'votescast');
|
||||
|
||||
var resultTableJsonSting = {
|
||||
table: {
|
||||
@ -236,7 +218,7 @@ angular.module('OpenSlidesApp.assignments.pdf', ['OpenSlidesApp.core.pdf'])
|
||||
// add the legend to the result body
|
||||
if (assignment.polls.length > 0 && isElectedSemaphore) {
|
||||
resultBody.push({
|
||||
text: "* = " + gettextCatalog.getString("is elected"),
|
||||
text: '* = ' + gettextCatalog.getString('is elected'),
|
||||
margin: [0, 5, 0, 0],
|
||||
});
|
||||
}
|
||||
@ -366,12 +348,17 @@ angular.module('OpenSlidesApp.assignments.pdf', ['OpenSlidesApp.core.pdf'])
|
||||
var candidateBallotList = [];
|
||||
|
||||
if (poll.pollmethod == 'votes') {
|
||||
angular.forEach(candidates, function(option) {
|
||||
_.forEach(candidates, function(option) {
|
||||
var candidate = option.candidate.get_full_name();
|
||||
candidateBallotList.push(PDFLayout.createBallotEntry(candidate));
|
||||
});
|
||||
// Add 'no' option
|
||||
var no = gettextCatalog.getString('No');
|
||||
var ballotEntry = PDFLayout.createBallotEntry(no);
|
||||
ballotEntry.margin[1] = 25; // top margin
|
||||
candidateBallotList.push(ballotEntry);
|
||||
} else {
|
||||
angular.forEach(candidates, function(option) {
|
||||
_.forEach(candidates, function(option) {
|
||||
var candidate;
|
||||
if (option.candidate) {
|
||||
candidate = option.candidate.get_full_name();
|
||||
@ -565,7 +552,7 @@ angular.module('OpenSlidesApp.assignments.pdf', ['OpenSlidesApp.core.pdf'])
|
||||
};
|
||||
|
||||
var toc = [];
|
||||
angular.forEach(assignmentTitles, function(title) {
|
||||
_.forEach(assignmentTitles, function(title) {
|
||||
toc.push({
|
||||
text: title,
|
||||
style: "tableofcontent"
|
||||
@ -656,7 +643,7 @@ angular.module('OpenSlidesApp.assignments.pdf', ['OpenSlidesApp.core.pdf'])
|
||||
createBallotPdf: function (assignment, pollId) {
|
||||
var thePoll;
|
||||
var pollNumber;
|
||||
angular.forEach(assignment.polls, function(poll, pollIndex) {
|
||||
_.forEach(assignment.polls, function(poll, pollIndex) {
|
||||
if (poll.id == pollId) {
|
||||
thePoll = poll;
|
||||
pollNumber = pollIndex+1;
|
||||
|
@ -708,7 +708,7 @@ angular.module('OpenSlidesApp.assignments.site', [
|
||||
|
||||
// add dynamic form fields
|
||||
var options = $filter('orderBy')(assignmentpoll.options, 'weight');
|
||||
options.forEach(function(option) {
|
||||
_.forEach(options, function(option) {
|
||||
var defaultValue;
|
||||
if (assignmentpoll.pollmethod == 'yna' || assignmentpoll.pollmethod == 'yn') {
|
||||
defaultValue = {};
|
||||
@ -783,35 +783,60 @@ angular.module('OpenSlidesApp.assignments.site', [
|
||||
});
|
||||
}
|
||||
});
|
||||
// add general form fields
|
||||
$scope.formFields.push(
|
||||
if (assignmentpoll.pollmethod == 'votes'){
|
||||
$scope.formFields.push(
|
||||
{
|
||||
key: 'votesvalid',
|
||||
key: 'votesabstain',
|
||||
type: 'input',
|
||||
templateOptions: {
|
||||
label: gettextCatalog.getString('Valid ballots'),
|
||||
label: gettextCatalog.getString('Abstain'),
|
||||
type: 'number',
|
||||
min: -2,
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'votesinvalid',
|
||||
key: 'votesno',
|
||||
type: 'input',
|
||||
templateOptions: {
|
||||
label: gettextCatalog.getString('Invalid ballots'),
|
||||
type: 'number',
|
||||
min: -2,
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'votescast',
|
||||
type: 'input',
|
||||
templateOptions: {
|
||||
label: gettextCatalog.getString('Casted ballots'),
|
||||
label: gettextCatalog.getString('No'),
|
||||
type: 'number',
|
||||
min: -2,
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
// add general form fields
|
||||
$scope.formFields.push(
|
||||
{
|
||||
template: '<hr class="smallhr">',
|
||||
},
|
||||
{
|
||||
key: 'votesvalid',
|
||||
type: 'input',
|
||||
templateOptions: {
|
||||
label: gettextCatalog.getString('Valid ballots'),
|
||||
type: 'number',
|
||||
min: -2,
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'votesinvalid',
|
||||
type: 'input',
|
||||
templateOptions: {
|
||||
label: gettextCatalog.getString('Invalid ballots'),
|
||||
type: 'number',
|
||||
min: -2,
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'votescast',
|
||||
type: 'input',
|
||||
templateOptions: {
|
||||
label: gettextCatalog.getString('Casted ballots'),
|
||||
type: 'number',
|
||||
min: -2,
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// save assignmentpoll
|
||||
@ -820,22 +845,22 @@ angular.module('OpenSlidesApp.assignments.site', [
|
||||
if (assignmentpoll.pollmethod == 'yna') {
|
||||
assignmentpoll.options.forEach(function(option) {
|
||||
votes.push({
|
||||
"Yes": poll['yes_' + option.candidate_id],
|
||||
"No": poll['no_' + option.candidate_id],
|
||||
"Abstain": poll['abstain_' + option.candidate_id]
|
||||
'Yes': poll['yes_' + option.candidate_id],
|
||||
'No': poll['no_' + option.candidate_id],
|
||||
'Abstain': poll['abstain_' + option.candidate_id]
|
||||
});
|
||||
});
|
||||
} else if (assignmentpoll.pollmethod == 'yn') {
|
||||
assignmentpoll.options.forEach(function(option) {
|
||||
votes.push({
|
||||
"Yes": poll['yes_' + option.candidate_id],
|
||||
"No": poll['no_' + option.candidate_id]
|
||||
'Yes': poll['yes_' + option.candidate_id],
|
||||
'No': poll['no_' + option.candidate_id]
|
||||
});
|
||||
});
|
||||
} else {
|
||||
assignmentpoll.options.forEach(function(option) {
|
||||
votes.push({
|
||||
"Votes": poll['vote_' + option.candidate_id],
|
||||
'Votes': poll['vote_' + option.candidate_id],
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -843,9 +868,11 @@ angular.module('OpenSlidesApp.assignments.site', [
|
||||
poll.DSUpdate({
|
||||
assignment_id: poll.assignment_id,
|
||||
votes: votes,
|
||||
votesabstain: poll.votesabstain,
|
||||
votesno: poll.votesno,
|
||||
votesvalid: poll.votesvalid,
|
||||
votesinvalid: poll.votesinvalid,
|
||||
votescast: poll.votescast
|
||||
votescast: poll.votescast,
|
||||
})
|
||||
.then(function(success) {
|
||||
$scope.alert.show = false;
|
||||
|
@ -195,10 +195,6 @@
|
||||
{{ option.candidate.get_full_name() }}
|
||||
</a>
|
||||
</ul>
|
||||
<strong translate>Election method</strong><br>
|
||||
<span ng-if="poll.pollmethod == 'votes'" translate>One vote per candidate</span>
|
||||
<span ng-if="poll.pollmethod == 'yna'" translate>Yes/No/Abstain per candidate</span>
|
||||
<span ng-if="poll.pollmethod == 'yn'" translate>Yes/No per candidate</span>
|
||||
</div>
|
||||
|
||||
<!-- Settings for majority calculations -->
|
||||
@ -254,6 +250,18 @@
|
||||
</span>
|
||||
|
||||
<!-- total votes (valid/invalid/casts) -->
|
||||
<tr ng-if="poll.pollmethod === 'votes'">
|
||||
<td>
|
||||
<translate>Abstain</translate>
|
||||
<td>
|
||||
{{ poll.getVote('votesabstain').value }}
|
||||
{{ poll.getVote('votesabstain').percentStr }}
|
||||
<tr ng-if="poll.pollmethod === 'votes'">
|
||||
<td>
|
||||
<translate>No</translate>
|
||||
<td>
|
||||
{{ poll.getVote('votesno').value }}
|
||||
{{ poll.getVote('votesno').percentStr }}
|
||||
<tr>
|
||||
<td>
|
||||
<translate>Valid ballots</translate>
|
||||
@ -273,6 +281,11 @@
|
||||
{{ poll.getVote('votescast').value }}
|
||||
{{ poll.getVote('votescast').percentStr }}
|
||||
</table>
|
||||
|
||||
<strong translate>Election method</strong><br>
|
||||
<span ng-if="poll.pollmethod == 'votes'" translate>One vote per candidate</span>
|
||||
<span ng-if="poll.pollmethod == 'yna'" translate>Yes/No/Abstain per candidate</span>
|
||||
<span ng-if="poll.pollmethod == 'yn'" translate>Yes/No per candidate</span>
|
||||
</div>
|
||||
</div>
|
||||
</uib-tab>
|
||||
|
@ -63,7 +63,17 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- total votes (valid/invalid/casts) -->
|
||||
<!-- total votes (abstain/no/valid/invalid/casts) -->
|
||||
<tr class="total" ng-if="poll.has_votes && poll.pollmethod === 'votes' && poll.getVote('votesabstain').value !== null">
|
||||
<td>
|
||||
<translate>Abstain</translate>
|
||||
<td ng-init="vote = poll.getVote('votesabstain')">
|
||||
{{ vote.value }} {{ vote.percentStr }}
|
||||
<tr class="total" ng-if="poll.has_votes && poll.pollmethod === 'votes' && poll.getVote('votesno').value !== null">
|
||||
<td>
|
||||
<translate>No</translate>
|
||||
<td ng-init="vote = poll.getVote('votesno')">
|
||||
{{ vote.value }} {{ vote.percentStr }}
|
||||
<tr class="total" ng-if="poll.has_votes && poll.getVote('votesvalid').value !== null">
|
||||
<td>
|
||||
<translate>Valid ballots</translate>
|
||||
|
Loading…
Reference in New Issue
Block a user