Merge pull request #2331 from emanuelschuetze/issue2182b

Motion config: 'calculate % without abstains' (Fixes #2182)
This commit is contained in:
Norman Jäckel 2016-08-29 16:59:12 +02:00 committed by GitHub
commit 70dfe8f525
9 changed files with 85 additions and 52 deletions

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 >=0 ) { if (percentNumber >= 0 ) {
percentStr = "(" + percentNumber + "%)"; percentStr = "(" + percentNumber + "%)";
} }
votes.push({ votes.push({

View File

@ -764,6 +764,9 @@ angular.module('OpenSlidesApp.assignments.site', ['OpenSlidesApp.assignments'])
gettext('Elections'); gettext('Elections');
gettext('Ballot and ballot papers'); gettext('Ballot and ballot papers');
gettext('The 100 % base of an election result consists of'); gettext('The 100 % base of an election result consists of');
gettext('All valid votes (Yes/No/Abstain)');
gettext('All votes cast (including invalid votes)');
gettext('Disabled (no percents)');
gettext('Number of ballot papers (selection)'); gettext('Number of ballot papers (selection)');
gettext('Number of all delegates'); gettext('Number of all delegates');
gettext('Number of all participants'); gettext('Number of all participants');

View File

@ -23,6 +23,10 @@ def get_config_variables():
papers' and 'PDF'. The generator has to be evaluated during app loading papers' and 'PDF'. The generator has to be evaluated during app loading
(see apps.py). (see apps.py).
""" """
PERCENT_BASE_CHOICES_MOTION = ({
'value': "WITHOUT_ABSTAIN",
'display_name': 'Yes and No votes'},)
PERCENT_BASE_CHOICES_MOTION += PERCENT_BASE_CHOICES
# General # General
yield ConfigVariable( yield ConfigVariable(
name='motions_workflow', name='motions_workflow',
@ -148,7 +152,7 @@ def get_config_variables():
default_value='WITHOUT_INVALID', default_value='WITHOUT_INVALID',
input_type='choice', input_type='choice',
label='The 100 % base of a voting result consists of', label='The 100 % base of a voting result consists of',
choices=PERCENT_BASE_CHOICES, choices=PERCENT_BASE_CHOICES_MOTION,
weight=355, weight=355,
group='Motions', group='Motions',
subgroup='Voting and ballot papers') subgroup='Voting and ballot papers')

View File

@ -71,7 +71,7 @@ angular.module('OpenSlidesApp.motions', [
}, },
methods: { methods: {
// returns object with value and percent // returns object with value and percent
getVote: function (vote) { getVote: function (vote, type) {
if (!this.has_votes) { if (!this.has_votes) {
return; return;
} }
@ -89,13 +89,18 @@ angular.module('OpenSlidesApp.motions', [
} }
// calculate percent value // calculate percent value
var config = Config.get('motions_poll_100_percent_base').value; var config = Config.get('motions_poll_100_percent_base').value;
var percentStr, percentNumber; var percentStr;
var percentNumber = null;
if (config == "WITHOUT_INVALID" && this.votesvalid > 0 && vote >= 0) { if (config == "WITHOUT_INVALID" && this.votesvalid > 0 && vote >= 0) {
percentNumber = Math.round(vote * 100 / this.votesvalid * 10) / 10; percentNumber = Math.round(vote * 100 / this.votesvalid * 10) / 10;
} else if (config == "WITH_INVALID" && this.votescast > 0 && vote >= 0) { } else if (config == "WITH_INVALID" && this.votescast > 0 && vote >= 0) {
percentNumber = Math.round(vote * 100 / (this.votescast) * 10) / 10; percentNumber = Math.round(vote * 100 / (this.votescast) * 10) / 10;
} else if (config == "WITHOUT_ABSTAIN" && vote >= 0) {
if (type == 'yes' || type == 'no') {
percentNumber = Math.round(vote * 100 / (this.yes + this.no) * 10) / 10;
} }
if (percentNumber) { }
if (percentNumber !== null) {
percentStr = "(" + percentNumber + "%)"; percentStr = "(" + percentNumber + "%)";
} }
return { return {
@ -103,7 +108,7 @@ angular.module('OpenSlidesApp.motions', [
'percentStr': percentStr, 'percentStr': percentStr,
'percentNumber': percentNumber 'percentNumber': percentNumber
}; };
}, }
} }
}); });
} }

View File

@ -70,14 +70,15 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions', 'OpenSlid
results = function() { results = function() {
return motion.polls.map(function(poll, index) { return motion.polls.map(function(poll, index) {
var id = index + 1, var id = index + 1,
yes = poll.yes, yes = poll.yes ? poll.yes : '-', // if no poll.yes is given set it to '-'
yesRelative = (poll.yes) * 100 / (poll.votescast), yesRelative = poll.getVote(poll.yes, 'yes').percentStr,
no = poll.no, no = poll.no ? poll.no : '-',
noRelative = (poll.no) * 100 / (poll.votescast), noRelative = poll.getVote(poll.no, 'no').percentStr,
abstain = poll.abstain, abstain = poll.abstain ? poll.abstain : '-',
abstainRelative = (poll.abstain) * 100 / (poll.votescast), abstainrelativeGet = poll.getVote(poll.abstain, 'abstain').percentStr,
valid = poll.votesvalid, abstainRelative = abstainrelativeGet ? abstainrelativeGet : '',
validRelative = (poll.votesvalid) * 100 / (poll.votescast), valid = poll.votesvalid ? poll.votesvalid : '-',
validRelative = poll.getVote(poll.votesvalid, 'votesvalid').percentStr,
number = { number = {
text: id + ".", text: id + ".",
width: "5%" width: "5%"
@ -100,7 +101,7 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions', 'OpenSlid
var indexColumn = converter.createElement("text"); var indexColumn = converter.createElement("text");
var nameColumn = converter.createElement("text", "" + name); var nameColumn = converter.createElement("text", "" + name);
var valueColumn = converter.createElement("text", "" + value); var valueColumn = converter.createElement("text", "" + value);
var relColumn = converter.createElement("text", "(" + "" + relValue + "%)"); var relColumn = converter.createElement("text", relValue);
valueColumn.width = "40%"; valueColumn.width = "40%";
indexColumn.width = "5%"; indexColumn.width = "5%";
valueColumn.width = "5%"; valueColumn.width = "5%";
@ -1602,29 +1603,17 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions', 'OpenSlid
.config([ .config([
'gettext', 'gettext',
function (gettext) { function (gettext) {
gettext('The assembly may decide,');
gettext('Workflow of new motions');
gettext('Motions'); gettext('Motions');
// subgroup General
gettext('General');
gettext('Workflow of new motions');
gettext('Identifier'); gettext('Identifier');
gettext('Numbered per category'); gettext('Numbered per category');
gettext('Serially numbered'); gettext('Serially numbered');
gettext('Set it manually'); gettext('Set it manually');
gettext('Motion preamble'); gettext('Motion preamble');
gettext('Stop submitting new motions by non-staff users'); gettext('The assembly may decide,');
gettext('Allow to disable versioning');
gettext('Activate amendments');
gettext('Amendments');
gettext('Prefix for the identifier for amendments');
gettext('Number of (minimum) required supporters for a motion');
gettext('Choose 0 to disable the supporting system.');
gettext('Supporters');
gettext('Remove all supporters of a motion if a submitter edits his ' +
'motion in early state');
gettext('Title for PDF document (all motions)');
gettext('Preamble text for PDF document (all motioqns)');
gettext('Show paragraph numbering (only in PDF)');
/// Prefix for the identifier for amendments
gettext('A');
gettext('Default line numbering'); gettext('Default line numbering');
/// Line numbering: Outside /// Line numbering: Outside
gettext('Outside'); gettext('Outside');
@ -1634,6 +1623,40 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions', 'OpenSlid
gettext('None'); gettext('None');
gettext('Line length'); gettext('Line length');
gettext('The maximum number of characters per line. Relevant when line numbering is enabled. Min: 40'); gettext('The maximum number of characters per line. Relevant when line numbering is enabled. Min: 40');
gettext('Stop submitting new motions by non-staff users');
gettext('Allow to disable versioning');
// subgroup Amendments
gettext('Amendments');
gettext('Activate amendments');
gettext('Prefix for the identifier for amendments');
/// Prefix for the identifier for amendments
gettext('A');
// subgroup Suppoerters
gettext('Supporters');
gettext('Number of (minimum) required supporters for a motion');
gettext('Choose 0 to disable the supporting system.');
gettext('Remove all supporters of a motion if a submitter edits his ' +
'motion in early state');
// subgroup Voting and ballot papers
gettext('Voting and ballot papers');
gettext('The 100 % base of a voting result consists of');
gettext('All valid votes (Yes/No/Abstain)');
gettext('All votes cast (including invalid votes)');
gettext('Disabled (no percents)');
gettext('Yes and No votes');
gettext('Number of ballot papers (selection)');
gettext('Number of all delegates');
gettext('Number of all participants');
gettext('Use the following custom number');
gettext('Custom number of ballot papers');
// subgroup PDF
gettext('Title for PDF document (all motions)');
gettext('Preamble text for PDF document (all motioqns)');
gettext('Show paragraph numbering (only in PDF)');
} }
]); ]);

View File

@ -151,7 +151,7 @@
<tr> <tr>
<td class="icon"> <td class="icon">
<i class="fa fa-thumbs-up fa-2x"></i> <i class="fa fa-thumbs-up fa-2x"></i>
<td ng-init="voteYes = poll.getVote(poll.yes)"> <td ng-init="voteYes = poll.getVote(poll.yes, 'yes')">
<span class="result_label"><translate>Yes</translate>:</span> <span class="result_label"><translate>Yes</translate>:</span>
<span class="result_value"> <span class="result_value">
{{ voteYes.value }} {{ voteYes.percentStr }} {{ voteYes.value }} {{ voteYes.percentStr }}
@ -163,7 +163,7 @@
<tr> <tr>
<td class="icon"> <td class="icon">
<i class="fa fa-thumbs-down fa-2x"></i> <i class="fa fa-thumbs-down fa-2x"></i>
<td ng-init="voteNo = poll.getVote(poll.no)"> <td ng-init="voteNo = poll.getVote(poll.no, 'no')">
<span class="result_label"><translate>No</translate>:</span> <span class="result_label"><translate>No</translate>:</span>
<span class="result_value" > <span class="result_value" >
{{ voteNo.value }} {{ voteNo.percentStr }} {{ voteNo.value }} {{ voteNo.percentStr }}
@ -175,7 +175,7 @@
<tr> <tr>
<td class="icon"> <td class="icon">
<strong style="font-size: 26px">&empty;</strong> <strong style="font-size: 26px">&empty;</strong>
<td ng-init="voteAbstain = poll.getVote(poll.abstain)"> <td ng-init="voteAbstain = poll.getVote(poll.abstain, 'abstain')">
<span class="result_label"><translate>Abstain</translate>:</span> <span class="result_label"><translate>Abstain</translate>:</span>
<span class="result_value"> <span class="result_value">
{{ voteAbstain.value }} {{ voteAbstain.percentStr }} {{ voteAbstain.value }} {{ voteAbstain.percentStr }}
@ -184,19 +184,19 @@
<uib-progressbar value="voteAbstain.percentNumber" type="warning"></uib-progressbar> <uib-progressbar value="voteAbstain.percentNumber" type="warning"></uib-progressbar>
</div> </div>
<!-- valid votes --> <!-- valid votes -->
<tr> <tr ng-if="poll.votesvalid !== null">
<td class="icon"> <td class="icon">
<i class="fa fa-check fa-lg"></i> <i class="fa fa-check fa-lg"></i>
<td ng-init="votesValid = poll.getVote(poll.votesvalid)"> <td ng-init="votesValid = poll.getVote(poll.votesvalid, 'votesvalid')">
<span class="result_label"><translate>Valid votes</translate>:</span> <span class="result_label"><translate>Valid votes</translate>:</span>
<span class="result_value"> <span class="result_value">
{{ votesValid.value }} {{ votesValid.percentStr }} {{ votesValid.value }} {{ votesValid.percentStr }}
</span> </span>
<!-- invalid votes --> <!-- invalid votes -->
<tr> <tr ng-if="poll.votesinvalid !== null">
<td class="icon"> <td class="icon">
<i class="fa fa-ban fa-lg"></i> <i class="fa fa-ban fa-lg"></i>
<td ng-init="votesInvalid = poll.getVote(poll.votesinvalid)"> <td ng-init="votesInvalid = poll.getVote(poll.votesinvalid, 'votesinvalid')">
<span class="result_label"><translate>Invalid votes</translate>:</span> <span class="result_label"><translate>Invalid votes</translate>:</span>
<span class="result_value"> <span class="result_value">
{{ votesInvalid.value }} {{ votesInvalid.value }}
@ -205,10 +205,10 @@
</span> </span>
</span> </span>
<!-- votes cast --> <!-- votes cast -->
<tr class="total"> <tr class="total" ng-if="poll.votescast !== null">
<td class="icon"> <td class="icon">
<strong style="font-size: 16px">&sum;</strong> <strong style="font-size: 16px">&sum;</strong>
<td ng-init="votesCast = poll.getVote(poll.votescast)"> <td ng-init="votesCast = poll.getVote(poll.votescast, 'votescast')">
<span class="result_label"><translate>Votes cast</translate>:</span> <span class="result_label"><translate>Votes cast</translate>:</span>
<span class="result_value"> <span class="result_value">
{{ votesCast.value }} {{ votesCast.value }}

View File

@ -23,31 +23,31 @@
<tr> <tr>
<td class="icon"> <td class="icon">
<i class="fa fa-thumbs-up fa-2x"></i> <i class="fa fa-thumbs-up fa-2x"></i>
<td ng-init="voteYes = poll.getVote(poll.yes)"> <td ng-init="voteYes = poll.getVote(poll.yes, 'yes')">
<span class="result_label"><translate>Yes</translate>:</span> <span class="result_label"><translate>Yes</translate>:</span>
<span class="result_value"> <span class="result_value">
{{ voteYes.value }} {{ voteYes.percentStr }} {{ voteYes.value }} {{ voteYes.percentStr }}
</span> </span>
<div ng-if="voteYes.percentNumber"> <div ng-if="voteYes.percentNumber >= 0">
<uib-progressbar value="voteYes.percentNumber" type="success"></uib-progressbar> <uib-progressbar value="voteYes.percentNumber" type="success"></uib-progressbar>
</div> </div>
<!-- no --> <!-- no -->
<tr> <tr>
<td class="icon"> <td class="icon">
<i class="fa fa-thumbs-down fa-2x"></i> <i class="fa fa-thumbs-down fa-2x"></i>
<td ng-init="voteNo = poll.getVote(poll.no)"> <td ng-init="voteNo = poll.getVote(poll.no, 'no')">
<span class="result_label"><translate>No</translate>:</span> <span class="result_label"><translate>No</translate>:</span>
<span class="result_value" > <span class="result_value" >
{{ voteNo.value }} {{ voteNo.percentStr }} {{ voteNo.value }} {{ voteNo.percentStr }}
</span> </span>
<div ng-if="voteNo.percentNumber"> <div ng-if="voteNo.percentNumber >= 0">
<uib-progressbar value="voteNo.percentNumber" type="danger"></uib-progressbar> <uib-progressbar value="voteNo.percentNumber" type="danger"></uib-progressbar>
</div> </div>
<!-- abstain --> <!-- abstain -->
<tr> <tr>
<td class="icon"> <td class="icon">
<strong style="font-size: 26px">&empty;</strong> <strong style="font-size: 26px">&empty;</strong>
<td ng-init="voteAbstain = poll.getVote(poll.abstain)"> <td ng-init="voteAbstain = poll.getVote(poll.abstain, 'abstain')">
<span class="result_label"><translate>Abstain</translate>:</span> <span class="result_label"><translate>Abstain</translate>:</span>
<span class="result_value"> <span class="result_value">
{{ voteAbstain.value }} {{ voteAbstain.percentStr }} {{ voteAbstain.value }} {{ voteAbstain.percentStr }}

View File

@ -3,7 +3,6 @@ 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 _
from django.utils.translation import ugettext_lazy
from openslides.utils.models import MinMaxIntegerField from openslides.utils.models import MinMaxIntegerField
@ -66,11 +65,10 @@ class BaseVote(models.Model):
percent_base = 0 percent_base = 0
return print_value(self.weight, percent_base) return print_value(self.weight, percent_base)
PERCENT_BASE_CHOICES = ( PERCENT_BASE_CHOICES = (
{'value': 'WITHOUT_INVALID', 'display_name': ugettext_lazy('Only all valid votes')}, {'value': 'WITHOUT_INVALID', 'display_name': 'All valid votes (Yes/No/Abstain)'},
{'value': 'WITH_INVALID', 'display_name': ugettext_lazy('All votes cast (including invalid votes)')}, {'value': 'WITH_INVALID', 'display_name': 'All votes cast (including invalid votes)'},
{'value': 'DISABLED', 'display_name': ugettext_lazy('Disabled (no percents)')}) {'value': 'DISABLED', 'display_name': 'Disabled (no percents)'})
class CollectDefaultVotesMixin(models.Model): class CollectDefaultVotesMixin(models.Model):