Merge pull request #2319 from matakuka/assignment_withoutabstains
Calculate assignment without abstains (if one candidate) (issue #2182)
This commit is contained in:
commit
e340d83777
@ -14,6 +14,7 @@ Agenda:
|
|||||||
|
|
||||||
Assignments:
|
Assignments:
|
||||||
- Remove unused assignment config to publish winner election results only.
|
- Remove unused assignment config to publish winner election results only.
|
||||||
|
- Added options to calculate percentages on different bases.
|
||||||
|
|
||||||
Core:
|
Core:
|
||||||
- Added support for big assemblies with lots of users.
|
- Added support for big assemblies with lots of users.
|
||||||
@ -27,6 +28,7 @@ Motions:
|
|||||||
- Added configurable fields for comments.
|
- Added configurable fields for comments.
|
||||||
- Added recommendations for motions.
|
- Added recommendations for motions.
|
||||||
- Changed label of former state "commited a bill" to "refered to committee".
|
- Changed label of former state "commited a bill" to "refered to committee".
|
||||||
|
- Added options to calculate percentages on different bases.
|
||||||
|
|
||||||
Users:
|
Users:
|
||||||
- Added field is_committee and new default group Committees.
|
- Added field is_committee and new default group Committees.
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
from django.core.validators import MinValueValidator
|
from django.core.validators import MinValueValidator
|
||||||
|
|
||||||
from openslides.core.config import ConfigVariable
|
from openslides.core.config import ConfigVariable
|
||||||
from openslides.poll.models import PERCENT_BASE_CHOICES
|
|
||||||
|
|
||||||
|
|
||||||
def get_config_variables():
|
def get_config_variables():
|
||||||
@ -28,10 +27,18 @@ def get_config_variables():
|
|||||||
|
|
||||||
yield ConfigVariable(
|
yield ConfigVariable(
|
||||||
name='assignments_poll_100_percent_base',
|
name='assignments_poll_100_percent_base',
|
||||||
default_value='WITHOUT_INVALID',
|
default_value='YES_NO_ABSTAIN',
|
||||||
input_type='choice',
|
input_type='choice',
|
||||||
label='The 100 % base of an election result consists of',
|
label='The 100 % base of an election result consists of',
|
||||||
choices=PERCENT_BASE_CHOICES,
|
choices=(
|
||||||
|
{'value': 'YES_NO_ABSTAIN', 'display_name': 'Yes/No/Abstain per candidate'},
|
||||||
|
{'value': 'YES_NO', 'display_name': 'Yes/No per candidate'},
|
||||||
|
{'value': 'VALID', 'display_name': 'All valid ballots'},
|
||||||
|
{'value': 'CAST', 'display_name': 'All casted ballots'},
|
||||||
|
{'value': 'DISABLED', 'display_name': 'Disabled (no percents)'}),
|
||||||
|
help_text="For Yes/No/Abstain and Yes/No the 100 % base depends on the election method: If there are " +
|
||||||
|
"more candidates than open posts, the sum of all votes of all candidates is 100 %. Otherwise " +
|
||||||
|
"the sum of all votes per candidate is 100 %.",
|
||||||
weight=420,
|
weight=420,
|
||||||
group='Elections',
|
group='Elections',
|
||||||
subgroup='Ballot and ballot papers')
|
subgroup='Ballot and ballot papers')
|
||||||
|
28
openslides/assignments/migrations/0003_auto_20160907_0946.py
Normal file
28
openslides/assignments/migrations/0003_auto_20160907_0946.py
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.9.9 on 2016-09-07 09:46
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('assignments', '0002_assignmentpoll_yesno'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='assignmentpoll',
|
||||||
|
name='yesno',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='assignmentpoll',
|
||||||
|
name='yesnoabstain',
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='assignmentpoll',
|
||||||
|
name='pollmethod',
|
||||||
|
field=models.CharField(default='yna', max_length=5),
|
||||||
|
),
|
||||||
|
]
|
@ -208,29 +208,23 @@ 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
|
pollmethod = 'votes'
|
||||||
yesno = False
|
|
||||||
elif config['assignments_poll_vote_values'] == 'yesnoabstain':
|
elif config['assignments_poll_vote_values'] == 'yesnoabstain':
|
||||||
yesnoabstain = True
|
pollmethod = 'yna'
|
||||||
yesno = False
|
|
||||||
elif config['assignments_poll_vote_values'] == 'yesno':
|
elif config['assignments_poll_vote_values'] == 'yesno':
|
||||||
yesnoabstain = False
|
pollmethod = 'yn'
|
||||||
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
|
pollmethod = 'yna'
|
||||||
yesnoabstain = True
|
|
||||||
else:
|
else:
|
||||||
yesno = False
|
pollmethod = 'votes'
|
||||||
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,
|
pollmethod=pollmethod)
|
||||||
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
|
||||||
@ -364,8 +358,9 @@ class AssignmentPoll(RESTModelMixin, CollectDefaultVotesMixin,
|
|||||||
Assignment,
|
Assignment,
|
||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
related_name='polls')
|
related_name='polls')
|
||||||
yesnoabstain = models.BooleanField(default=False)
|
pollmethod = models.CharField(
|
||||||
yesno = models.BooleanField(default=False)
|
max_length=5,
|
||||||
|
default='yna')
|
||||||
description = models.CharField(
|
description = models.CharField(
|
||||||
max_length=79,
|
max_length=79,
|
||||||
blank=True)
|
blank=True)
|
||||||
@ -377,9 +372,9 @@ class AssignmentPoll(RESTModelMixin, CollectDefaultVotesMixin,
|
|||||||
return self.assignment
|
return self.assignment
|
||||||
|
|
||||||
def get_vote_values(self):
|
def get_vote_values(self):
|
||||||
if self.yesnoabstain:
|
if self.pollmethod == 'yna':
|
||||||
return ['Yes', 'No', 'Abstain']
|
return ['Yes', 'No', 'Abstain']
|
||||||
elif self.yesno:
|
elif self.pollmethod == 'yn':
|
||||||
return ['Yes', 'No']
|
return ['Yes', 'No']
|
||||||
else:
|
else:
|
||||||
return ['Votes']
|
return ['Votes']
|
||||||
|
@ -96,8 +96,7 @@ class AssignmentAllPollSerializer(ModelSerializer):
|
|||||||
model = AssignmentPoll
|
model = AssignmentPoll
|
||||||
fields = (
|
fields = (
|
||||||
'id',
|
'id',
|
||||||
'yesnoabstain',
|
'pollmethod',
|
||||||
'yesno',
|
|
||||||
'description',
|
'description',
|
||||||
'published',
|
'published',
|
||||||
'options',
|
'options',
|
||||||
@ -107,7 +106,7 @@ class AssignmentAllPollSerializer(ModelSerializer):
|
|||||||
'votes',
|
'votes',
|
||||||
'has_votes',
|
'has_votes',
|
||||||
'assignment') # js-data needs the assignment-id in the nested object to define relations.
|
'assignment') # js-data needs the assignment-id in the nested object to define relations.
|
||||||
read_only_fields = ('yesnoabstain',)
|
read_only_fields = ('pollmethod',)
|
||||||
validators = (default_votes_validator,)
|
validators = (default_votes_validator,)
|
||||||
|
|
||||||
def get_has_votes(self, obj):
|
def get_has_votes(self, obj):
|
||||||
@ -122,12 +121,12 @@ class AssignmentAllPollSerializer(ModelSerializer):
|
|||||||
Customized update method for polls. To update votes use the write
|
Customized update method for polls. To update votes use the write
|
||||||
only field 'votes'.
|
only field 'votes'.
|
||||||
|
|
||||||
Example data for a 'yesnoabstain'=true poll with two candidates:
|
Example data for a 'pollmethod'='yna' poll with two candidates:
|
||||||
|
|
||||||
"votes": [{"Yes": 10, "No": 4, "Abstain": -2},
|
"votes": [{"Yes": 10, "No": 4, "Abstain": -2},
|
||||||
{"Yes": -1, "No": 0, "Abstain": -2}]
|
{"Yes": -1, "No": 0, "Abstain": -2}]
|
||||||
|
|
||||||
Example data for a 'yesnoabstain'=false poll with two candidates:
|
Example data for a 'pollmethod' ='yn' poll with two candidates:
|
||||||
"votes": [{"Votes": 10}, {"Votes": 0}]
|
"votes": [{"Votes": 10}, {"Votes": 0}]
|
||||||
"""
|
"""
|
||||||
# Update votes.
|
# Update votes.
|
||||||
@ -168,8 +167,7 @@ class AssignmentShortPollSerializer(AssignmentAllPollSerializer):
|
|||||||
model = AssignmentPoll
|
model = AssignmentPoll
|
||||||
fields = (
|
fields = (
|
||||||
'id',
|
'id',
|
||||||
'yesnoabstain',
|
'pollmethod',
|
||||||
'yesno',
|
|
||||||
'description',
|
'description',
|
||||||
'published',
|
'published',
|
||||||
'options',
|
'options',
|
||||||
|
@ -21,28 +21,85 @@ angular.module('OpenSlidesApp.assignments', [])
|
|||||||
var poll = this.poll;
|
var poll = this.poll;
|
||||||
var votes = [];
|
var votes = [];
|
||||||
var config = Config.get('assignments_poll_100_percent_base').value;
|
var config = Config.get('assignments_poll_100_percent_base').value;
|
||||||
|
var impossible = false;
|
||||||
|
var yes = null, no = null, abstain = null;
|
||||||
|
angular.forEach(this.votes, function(vote) {
|
||||||
|
if (vote.value == "Yes" || vote.value == "Votes") {
|
||||||
|
yes = vote.weight;
|
||||||
|
} else if (vote.value == "No") {
|
||||||
|
no = vote.weight;
|
||||||
|
} else if (vote.value == "Abstain") {
|
||||||
|
abstain = vote.weight;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
//calculation for several candidates without yes/no options
|
||||||
|
var do_sum_of_all = false;
|
||||||
|
var sum_of_votes = 0;
|
||||||
|
if (poll.options.length > 1 && poll.pollmethod == 'votes') {
|
||||||
|
do_sum_of_all = true;
|
||||||
|
}
|
||||||
|
if (do_sum_of_all === true) {
|
||||||
|
angular.forEach(poll.options, function(option) {
|
||||||
|
angular.forEach(option.votes, function(vote) {
|
||||||
|
if (vote.value == "Votes") {
|
||||||
|
if (vote.weight >= 0 ) {
|
||||||
|
sum_of_votes = sum_of_votes + vote.weight;
|
||||||
|
} else {
|
||||||
|
impossible = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
angular.forEach(this.votes, function(vote) {
|
angular.forEach(this.votes, function(vote) {
|
||||||
// check for special value
|
// check for special value
|
||||||
var value;
|
var value;
|
||||||
switch (vote.weight) {
|
switch (vote.weight) {
|
||||||
case -1:
|
case -1:
|
||||||
value = gettextCatalog.getString('majority');
|
value = gettextCatalog.getString('majority');
|
||||||
|
impossible = true;
|
||||||
break;
|
break;
|
||||||
case -2:
|
case -2:
|
||||||
value = gettextCatalog.getString('undocumented');
|
value = gettextCatalog.getString('undocumented');
|
||||||
|
impossible = true;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
if (vote.weight >= 0) {
|
||||||
value = vote.weight;
|
value = vote.weight;
|
||||||
|
} else {
|
||||||
|
value = 0;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// calculate percent value
|
// calculate percent value
|
||||||
var percentStr, percentNumber;
|
var percentStr, percentNumber, base;
|
||||||
if (config == "WITHOUT_INVALID" && poll.votesvalid > 0 && vote.weight >= 0) {
|
if (config == "VALID") {
|
||||||
percentNumber = Math.round(vote.weight * 100 / poll.votesvalid * 10) / 10;
|
if (poll.votesvalid && poll.votesvalid > 0) {
|
||||||
} else if (config == "WITH_INVALID" && poll.votescast > 0 && vote.weight >= 0) {
|
base = poll.votesvalid;
|
||||||
percentNumber = Math.round(vote.weight * 100 / (poll.votescast) * 10) / 10;
|
|
||||||
}
|
}
|
||||||
if (percentNumber >= 0 ) {
|
} else if ( config == "CAST") {
|
||||||
|
if (poll.votescast && poll.votescast > 0) {
|
||||||
|
base = poll.votescast;
|
||||||
|
}
|
||||||
|
} else if (config == "YES_NO" && !impossible) {
|
||||||
|
if (vote.value == "Yes" || vote.value == "No" || vote.value == "Votes"){
|
||||||
|
if (do_sum_of_all) {
|
||||||
|
base = sum_of_votes;
|
||||||
|
} else {
|
||||||
|
base = yes + no;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (config == "YES_NO_ABSTAIN" && !impossible) {
|
||||||
|
if (do_sum_of_all) {
|
||||||
|
base = sum_of_votes;
|
||||||
|
} else {
|
||||||
|
base = yes + no + abstain;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (base !== 'undefined' && vote.weight >= 0) {
|
||||||
|
percentNumber = Math.round(vote.weight * 100 / base * 10) / 10;
|
||||||
|
}
|
||||||
|
if (percentNumber >= 0 && percentNumber !== 'undefined') {
|
||||||
percentStr = "(" + percentNumber + "%)";
|
percentStr = "(" + percentNumber + "%)";
|
||||||
}
|
}
|
||||||
votes.push({
|
votes.push({
|
||||||
@ -88,11 +145,20 @@ angular.module('OpenSlidesApp.assignments', [])
|
|||||||
return name;
|
return name;
|
||||||
},
|
},
|
||||||
// returns object with value and percent (for votes valid/invalid/cast only)
|
// returns object with value and percent (for votes valid/invalid/cast only)
|
||||||
getVote: function (vote) {
|
getVote: function (type) {
|
||||||
if (!this.has_votes || !vote) {
|
var value, percentStr, vote;
|
||||||
return;
|
switch(type) {
|
||||||
|
case 'votesinvalid':
|
||||||
|
vote = this.votesinvalid;
|
||||||
|
break;
|
||||||
|
case 'votesvalid':
|
||||||
|
vote = this.votesvalid;
|
||||||
|
break;
|
||||||
|
case 'votescast':
|
||||||
|
vote = this.votescast;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
var value = '';
|
if (this.has_votes && vote) {
|
||||||
switch (vote) {
|
switch (vote) {
|
||||||
case -1:
|
case -1:
|
||||||
value = gettextCatalog.getString('majority');
|
value = gettextCatalog.getString('majority');
|
||||||
@ -102,20 +168,27 @@ angular.module('OpenSlidesApp.assignments', [])
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
value = vote;
|
value = vote;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
// calculate percent value
|
if (vote >= 0) {
|
||||||
var config = Config.get('assignments_poll_100_percent_base').value;
|
var config = Config.get('assignments_poll_100_percent_base').value;
|
||||||
var percent;
|
var percentNumber;
|
||||||
if ((config == "WITHOUT_INVALID" && vote == this.votesvalid && vote >= 0) ||
|
if (config == "CAST" && this.votescast && this.votescast > 0) {
|
||||||
(config == "WITH_INVALID" && vote == this.votescast && vote >= 0)) {
|
percentNumber = Math.round(vote * 100 / this.votescast * 10) / 10;
|
||||||
percent = '(100%)';
|
} else if (config == "VALID" && this.votesvalid && this.votesvalid >= 0) {
|
||||||
|
if (type === 'votesvalid'){
|
||||||
|
percentNumber = Math.round(vote * 100 / this.votesvalid * 10) / 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (percentNumber !== 'undefined' && percentNumber >= 0 && percentNumber <=100) {
|
||||||
|
percentStr = "(" + percentNumber + "%)";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
'value': value,
|
'value': value,
|
||||||
'percent': percent
|
'percentStr': percentStr
|
||||||
};
|
};
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
relations: {
|
relations: {
|
||||||
belongsTo: {
|
belongsTo: {
|
||||||
|
@ -460,6 +460,7 @@ angular.module('OpenSlidesApp.assignments.site', ['OpenSlidesApp.assignments'])
|
|||||||
$scope.alert = { type: 'danger', msg: message, show: true };
|
$scope.alert = { type: 'danger', msg: message, show: true };
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// mark candidate as (not) elected
|
// mark candidate as (not) elected
|
||||||
$scope.markElected = function (user, reverse) {
|
$scope.markElected = function (user, reverse) {
|
||||||
if (reverse) {
|
if (reverse) {
|
||||||
@ -590,8 +591,8 @@ 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 || assignmentpoll.yesno) {
|
if (assignmentpoll.pollmethod == 'yna' || assignmentpoll.pollmethod == 'yn') {
|
||||||
if (assignmentpoll.yesnoabstain) {
|
if (assignmentpoll.pollmethod == 'yna') {
|
||||||
defaultValue = {
|
defaultValue = {
|
||||||
'yes': '',
|
'yes': '',
|
||||||
'no': '',
|
'no': '',
|
||||||
@ -608,7 +609,7 @@ angular.module('OpenSlidesApp.assignments.site', ['OpenSlidesApp.assignments'])
|
|||||||
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;
|
||||||
if (assignmentpoll.yesnoabstain){
|
if (assignmentpoll.pollmethod == 'yna'){
|
||||||
defaultValue.abstain = option.votes[2].weight;
|
defaultValue.abstain = option.votes[2].weight;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -637,7 +638,7 @@ angular.module('OpenSlidesApp.assignments.site', ['OpenSlidesApp.assignments'])
|
|||||||
},
|
},
|
||||||
defaultValue: defaultValue.no
|
defaultValue: defaultValue.no
|
||||||
});
|
});
|
||||||
if (assignmentpoll.yesnoabstain){
|
if (assignmentpoll.pollmethod == 'yna'){
|
||||||
$scope.formFields.push(
|
$scope.formFields.push(
|
||||||
{
|
{
|
||||||
key:'abstain_' + option.candidate_id,
|
key:'abstain_' + option.candidate_id,
|
||||||
@ -673,7 +674,7 @@ angular.module('OpenSlidesApp.assignments.site', ['OpenSlidesApp.assignments'])
|
|||||||
key: 'votesvalid',
|
key: 'votesvalid',
|
||||||
type: 'input',
|
type: 'input',
|
||||||
templateOptions: {
|
templateOptions: {
|
||||||
label: gettextCatalog.getString('Votes valid'),
|
label: gettextCatalog.getString('Valid ballots'),
|
||||||
type: 'number'
|
type: 'number'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -681,7 +682,7 @@ angular.module('OpenSlidesApp.assignments.site', ['OpenSlidesApp.assignments'])
|
|||||||
key: 'votesinvalid',
|
key: 'votesinvalid',
|
||||||
type: 'input',
|
type: 'input',
|
||||||
templateOptions: {
|
templateOptions: {
|
||||||
label: gettextCatalog.getString('Votes invalid'),
|
label: gettextCatalog.getString('Invalid ballots'),
|
||||||
type: 'number'
|
type: 'number'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -689,7 +690,7 @@ angular.module('OpenSlidesApp.assignments.site', ['OpenSlidesApp.assignments'])
|
|||||||
key: 'votescast',
|
key: 'votescast',
|
||||||
type: 'input',
|
type: 'input',
|
||||||
templateOptions: {
|
templateOptions: {
|
||||||
label: gettextCatalog.getString('Votes cast'),
|
label: gettextCatalog.getString('Casted ballots'),
|
||||||
type: 'number'
|
type: 'number'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -707,7 +708,7 @@ angular.module('OpenSlidesApp.assignments.site', ['OpenSlidesApp.assignments'])
|
|||||||
// save assignmentpoll
|
// save assignmentpoll
|
||||||
$scope.save = function (poll) {
|
$scope.save = function (poll) {
|
||||||
var votes = [];
|
var votes = [];
|
||||||
if (assignmentpoll.yesnoabstain) {
|
if (assignmentpoll.pollmethod == 'yna') {
|
||||||
assignmentpoll.options.forEach(function(option) {
|
assignmentpoll.options.forEach(function(option) {
|
||||||
votes.push({
|
votes.push({
|
||||||
"Yes": poll['yes_' + option.candidate_id],
|
"Yes": poll['yes_' + option.candidate_id],
|
||||||
@ -715,7 +716,7 @@ angular.module('OpenSlidesApp.assignments.site', ['OpenSlidesApp.assignments'])
|
|||||||
"Abstain": poll['abstain_' + option.candidate_id]
|
"Abstain": poll['abstain_' + option.candidate_id]
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else if (assignmentpoll.yesno) {
|
} else if (assignmentpoll.pollmethod == 'yn') {
|
||||||
assignmentpoll.options.forEach(function(option) {
|
assignmentpoll.options.forEach(function(option) {
|
||||||
votes.push({
|
votes.push({
|
||||||
"Yes": poll['yes_' + option.candidate_id],
|
"Yes": poll['yes_' + option.candidate_id],
|
||||||
@ -764,8 +765,13 @@ 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('For Yes/No/Abstain and Yes/No the 100 % base depends on the election method: If there are ' +
|
||||||
gettext('All votes cast (including invalid votes)');
|
'more candidates than open posts, the sum of all votes of all candidates is 100 %. Otherwise ' +
|
||||||
|
'the sum of all votes per candidate is 100 %.');
|
||||||
|
gettext('Yes/No/Abstain per candidate');
|
||||||
|
gettext('Yes/No per candidate');
|
||||||
|
gettext('All valid ballots');
|
||||||
|
gettext('All casted ballots');
|
||||||
gettext('Disabled (no percents)');
|
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');
|
||||||
|
@ -160,7 +160,7 @@
|
|||||||
<i class="fa fa-video-camera"></i>
|
<i class="fa fa-video-camera"></i>
|
||||||
4. <translate>Project</translate>
|
4. <translate>Project</translate>
|
||||||
</button>
|
</button>
|
||||||
| <a class="btn btn-danger btn-sm"
|
<a class="btn btn-danger btn-sm"
|
||||||
ng-bootbox-confirm="{{ 'Are you sure you want to delete this ballot?' | translate }}"
|
ng-bootbox-confirm="{{ 'Are you sure you want to delete this ballot?' | translate }}"
|
||||||
ng-bootbox-confirm-action="deleteBallot(poll)">
|
ng-bootbox-confirm-action="deleteBallot(poll)">
|
||||||
<i class="fa fa-times"></i>
|
<i class="fa fa-times"></i>
|
||||||
@ -180,7 +180,6 @@
|
|||||||
|
|
||||||
<!-- candidates (poll options) -->
|
<!-- candidates (poll options) -->
|
||||||
<tr ng-repeat="option in poll.options">
|
<tr ng-repeat="option in poll.options">
|
||||||
|
|
||||||
<!-- elected -->
|
<!-- elected -->
|
||||||
<td class="minimum">
|
<td class="minimum">
|
||||||
<button os-perms="assignments.can_manage"
|
<button os-perms="assignments.can_manage"
|
||||||
@ -188,7 +187,6 @@
|
|||||||
<i ng-if="option.is_elected" class="fa fa-star" title="{{ 'is elected' | translate }}"></i>
|
<i ng-if="option.is_elected" class="fa fa-star" title="{{ 'is elected' | translate }}"></i>
|
||||||
<i ng-if="!option.is_elected" class="fa fa-star-o" title="{{ 'is not elected' | translate }}"></i>
|
<i ng-if="!option.is_elected" class="fa fa-star-o" title="{{ 'is not elected' | translate }}"></i>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<!-- candidate name -->
|
<!-- candidate name -->
|
||||||
<td>
|
<td>
|
||||||
<a ui-sref="users.user.detail({id: option.candidate.id})">{{ option.candidate.get_full_name() }}</a>
|
<a ui-sref="users.user.detail({id: option.candidate.id})">{{ option.candidate.get_full_name() }}</a>
|
||||||
@ -197,7 +195,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 || poll.yesno">{{ vote.label }}:</span>
|
<span ng-if="poll.pollmethod == 'yna' || poll.pollmethod == 'yn'">{{ 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>
|
||||||
@ -209,21 +207,21 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<td>
|
<td>
|
||||||
<translate>Valid votes</translate>
|
<translate>Valid ballots</translate>
|
||||||
<td ng-if="poll.has_votes" ng-init="vote = poll.getVote(poll.votesvalid)">
|
<td ng-if="poll.has_votes" ng-init="vote = poll.getVote('votesvalid')">
|
||||||
{{ vote.value }} {{ vote.percent }}
|
{{ vote.value }} {{ vote.percentStr }}
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<td>
|
<td>
|
||||||
<translate>Invalid votes</translate>
|
<translate>Invalid ballots</translate>
|
||||||
<td ng-if="poll.has_votes" ng-init="vote = poll.getVote(poll.votesinvalid)">
|
<td ng-if="poll.has_votes" ng-init="vote = poll.getVote('votesinvalid')">
|
||||||
{{ vote.value }}
|
{{ vote.value }} {{ vote.percentStr }}
|
||||||
<tr class="total bg-info">
|
<tr class="total bg-info">
|
||||||
<td>
|
<td>
|
||||||
<td>
|
<td>
|
||||||
<translate>Votes cast</translate>
|
<translate>Casted ballots</translate>
|
||||||
<td ng-if="poll.has_votes" ng-init="vote = poll.getVote(poll.votescast)">
|
<td ng-if="poll.has_votes" ng-init="vote = poll.getVote('votescast')">
|
||||||
{{ vote.value }} {{ vote.percent }}
|
{{ vote.value }} {{ vote.percentStr }}
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</uib-tab>
|
</uib-tab>
|
||||||
|
@ -51,12 +51,12 @@
|
|||||||
<!-- 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 || poll.yesno">
|
<div ng-show="poll.pollmethod == 'yna' || poll.pollmethod == 'yn'">
|
||||||
<span ng-show="poll.yesnoabstain">
|
<span ng-show="poll.pollmethod == 'yna'">
|
||||||
{{ 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>
|
||||||
<span ng-show="poll.yesno">
|
<span ng-show="poll.pollmethod == 'yn'">
|
||||||
{{ votes[0].label }}: <strong>{{ votes[0].value }}</strong> ·
|
{{ votes[0].label }}: <strong>{{ votes[0].value }}</strong> ·
|
||||||
{{ votes[1].label }}: {{ votes[1].value }}</span>
|
{{ votes[1].label }}: {{ votes[1].value }}</span>
|
||||||
<uib-progress ng-if="votes[0].percentNumber>=0">
|
<uib-progress ng-if="votes[0].percentNumber>=0">
|
||||||
@ -71,7 +71,7 @@
|
|||||||
</uib-bar>
|
</uib-bar>
|
||||||
</uib-progress>
|
</uib-progress>
|
||||||
</div>
|
</div>
|
||||||
<div ng-hide="poll.yesnoabstain || poll.yesno">
|
<div ng-show="poll.pollmethod == 'votes'">
|
||||||
<div ng-repeat="vote in votes">
|
<div ng-repeat="vote in votes">
|
||||||
{{ vote.value }} {{ vote.percentStr }}
|
{{ vote.value }} {{ vote.percentStr }}
|
||||||
<div ng-if="vote.percentNumber >= 0">
|
<div ng-if="vote.percentNumber >= 0">
|
||||||
@ -84,19 +84,19 @@
|
|||||||
<!-- total votes (valid/invalid/casts) -->
|
<!-- total votes (valid/invalid/casts) -->
|
||||||
<tr class="total">
|
<tr class="total">
|
||||||
<td>
|
<td>
|
||||||
<translate>Valid votes</translate>
|
<translate>Valid ballots</translate>
|
||||||
<td ng-if="poll.has_votes" ng-init="vote = poll.getVote(poll.votesvalid)">
|
<td ng-if="poll.has_votes" ng-init="vote = poll.getVote(poll.votesvalid)">
|
||||||
{{ vote.value }} {{ vote.percent }}
|
{{ vote.value }} {{ vote.percentStr }}
|
||||||
<tr class="total">
|
<tr class="total">
|
||||||
<td>
|
<td>
|
||||||
<translate>Invalid votes</translate>
|
<translate>Invalid ballots</translate>
|
||||||
<td ng-if="poll.has_votes" ng-init="vote = poll.getVote(poll.votesinvalid)">
|
<td ng-if="poll.has_votes" ng-init="vote = poll.getVote(poll.votesinvalid)">
|
||||||
{{ vote.value }}
|
{{ vote.value }} {{ vote.percentStr }}
|
||||||
<tr class="total bg-info">
|
<tr class="total bg-info">
|
||||||
<td>
|
<td>
|
||||||
<translate>Votes cast</translate>
|
<translate>Casted ballots</translate>
|
||||||
<td ng-if="poll.has_votes" ng-init="vote = poll.getVote(poll.votescast)">
|
<td ng-if="poll.has_votes" ng-init="vote = poll.getVote(poll.votescast)">
|
||||||
{{ vote.value }} {{ vote.percent }}
|
{{ vote.value }} {{ vote.percentStr }}
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -358,7 +358,7 @@ class AssignmentPDF(PDFView):
|
|||||||
footrow_one.append(_("Valid votes"))
|
footrow_one.append(_("Valid votes"))
|
||||||
votesvalid_is_used = False
|
votesvalid_is_used = False
|
||||||
for poll in polls:
|
for poll in polls:
|
||||||
footrow_one.append(poll.print_votesvalid())
|
footrow_one.append(poll.votesvalid)
|
||||||
if poll.votesvalid is not None:
|
if poll.votesvalid is not None:
|
||||||
votesvalid_is_used = True
|
votesvalid_is_used = True
|
||||||
if votesvalid_is_used:
|
if votesvalid_is_used:
|
||||||
@ -369,7 +369,7 @@ class AssignmentPDF(PDFView):
|
|||||||
footrow_two.append(_("Invalid votes"))
|
footrow_two.append(_("Invalid votes"))
|
||||||
votesinvalid_is_used = False
|
votesinvalid_is_used = False
|
||||||
for poll in polls:
|
for poll in polls:
|
||||||
footrow_two.append(poll.print_votesinvalid())
|
footrow_two.append(poll.votesinvalid)
|
||||||
if poll.votesinvalid is not None:
|
if poll.votesinvalid is not None:
|
||||||
votesinvalid_is_used = True
|
votesinvalid_is_used = True
|
||||||
if votesinvalid_is_used:
|
if votesinvalid_is_used:
|
||||||
@ -380,7 +380,7 @@ class AssignmentPDF(PDFView):
|
|||||||
footrow_three.append(_("Votes cast"))
|
footrow_three.append(_("Votes cast"))
|
||||||
votescast_is_used = False
|
votescast_is_used = False
|
||||||
for poll in polls:
|
for poll in polls:
|
||||||
footrow_three.append(poll.print_votescast())
|
footrow_three.append(poll.votescast)
|
||||||
if poll.votescast is not None:
|
if poll.votescast is not None:
|
||||||
votescast_is_used = True
|
votescast_is_used = True
|
||||||
if votescast_is_used:
|
if votescast_is_used:
|
||||||
@ -488,7 +488,7 @@ class AssignmentPollPDF(PDFView):
|
|||||||
counter = 0
|
counter = 0
|
||||||
cellcolumnA = []
|
cellcolumnA = []
|
||||||
# Choose kind of ballot paper (YesNoAbstain, YesNo or Yes)
|
# Choose kind of ballot paper (YesNoAbstain, YesNo or Yes)
|
||||||
if self.poll.yesnoabstain or self.poll.yesno: # YesNoAbstain/YesNo ballot: max 27 candidates
|
if self.poll.pollmethod in ['yna', 'yn']: # YesNoAbstain/YesNo ballot: max 27 candidates
|
||||||
for option in options:
|
for option in options:
|
||||||
counter += 1
|
counter += 1
|
||||||
candidate = option.candidate
|
candidate = option.candidate
|
||||||
@ -498,7 +498,7 @@ class AssignmentPollPDF(PDFView):
|
|||||||
cell.append(Paragraph(
|
cell.append(Paragraph(
|
||||||
"(%s)" % candidate.structure_level,
|
"(%s)" % candidate.structure_level,
|
||||||
stylesheet['Ballot_option_suffix_YNA']))
|
stylesheet['Ballot_option_suffix_YNA']))
|
||||||
if self.poll.yesnoabstain:
|
if self.poll.pollmethod == 'yna':
|
||||||
cell.append(Paragraph(
|
cell.append(Paragraph(
|
||||||
" ", stylesheet['Ballot_option_suffix_YNA']))
|
" ", stylesheet['Ballot_option_suffix_YNA']))
|
||||||
cell.append(Paragraph("<font name='circlefont' size='15'>%(circle)s</font> \
|
cell.append(Paragraph("<font name='circlefont' size='15'>%(circle)s</font> \
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
from django.core.validators import MinValueValidator
|
from django.core.validators import MinValueValidator
|
||||||
|
|
||||||
from openslides.core.config import ConfigVariable
|
from openslides.core.config import ConfigVariable
|
||||||
from openslides.poll.models import PERCENT_BASE_CHOICES
|
|
||||||
|
|
||||||
from .models import Workflow
|
from .models import Workflow
|
||||||
|
|
||||||
@ -23,10 +22,6 @@ 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(
|
||||||
@ -180,10 +175,16 @@ def get_config_variables():
|
|||||||
|
|
||||||
yield ConfigVariable(
|
yield ConfigVariable(
|
||||||
name='motions_poll_100_percent_base',
|
name='motions_poll_100_percent_base',
|
||||||
default_value='WITHOUT_INVALID',
|
default_value='YES_NO_ABSTAIN',
|
||||||
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_MOTION,
|
choices=(
|
||||||
|
{'value': 'YES_NO_ABSTAIN', 'display_name': 'Yes/No/Abstain'},
|
||||||
|
{'value': 'YES_NO', 'display_name': 'Yes/No'},
|
||||||
|
{'value': 'VALID', 'display_name': 'All valid ballots'},
|
||||||
|
{'value': 'CAST', 'display_name': 'All casted ballots'},
|
||||||
|
{'value': 'DISABLED', 'display_name': 'Disabled (no percents)'}
|
||||||
|
),
|
||||||
weight=355,
|
weight=355,
|
||||||
group='Motions',
|
group='Motions',
|
||||||
subgroup='Voting and ballot papers')
|
subgroup='Voting and ballot papers')
|
||||||
|
@ -112,11 +112,11 @@ def motion_to_pdf(pdf, motion):
|
|||||||
yes, no, abstain = (option['Yes'], option['No'], option['Abstain'])
|
yes, no, abstain = (option['Yes'], option['No'], option['Abstain'])
|
||||||
valid, invalid, votescast = ('', '', '')
|
valid, invalid, votescast = ('', '', '')
|
||||||
if poll.votesvalid is not None:
|
if poll.votesvalid is not None:
|
||||||
valid = "<br/>%s: %s" % (_("Valid votes"), poll.print_votesvalid())
|
valid = "<br/>%s" % (_("Valid votes"))
|
||||||
if poll.votesinvalid is not None:
|
if poll.votesinvalid is not None:
|
||||||
invalid = "<br/>%s: %s" % (_("Invalid votes"), poll.print_votesinvalid())
|
invalid = "<br/>%s" % (_("Invalid votes"))
|
||||||
if poll.votescast is not None:
|
if poll.votescast is not None:
|
||||||
votescast = "<br/>%s: %s" % (_("Votes cast"), poll.print_votescast())
|
votescast = "<br/>%s" % (_("Votes cast"))
|
||||||
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']))
|
||||||
|
@ -82,30 +82,48 @@ angular.module('OpenSlidesApp.motions', [
|
|||||||
if (!this.has_votes) {
|
if (!this.has_votes) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
var impossible = false;
|
||||||
var value = '';
|
var value = '';
|
||||||
switch (vote) {
|
switch (vote) {
|
||||||
case -1:
|
case -1:
|
||||||
value = gettextCatalog.getString('majority');
|
value = gettextCatalog.getString('majority');
|
||||||
|
impossible = true;
|
||||||
break;
|
break;
|
||||||
case -2:
|
case -2:
|
||||||
value = gettextCatalog.getString('undocumented');
|
value = gettextCatalog.getString('undocumented');
|
||||||
|
impossible = true;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
if (vote >= 0) {
|
||||||
value = vote;
|
value = vote;
|
||||||
|
} else {
|
||||||
|
value = 0; //value was not defined
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// 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;
|
var percentStr;
|
||||||
var percentNumber = null;
|
var percentNumber = null;
|
||||||
if (config == "WITHOUT_INVALID" && this.votesvalid > 0 && vote >= 0) {
|
var base = null;
|
||||||
percentNumber = Math.round(vote * 100 / this.votesvalid * 10) / 10;
|
if (!impossible) {
|
||||||
} else if (config == "WITH_INVALID" && this.votescast > 0 && vote >= 0) {
|
if (config == "YES_NO_ABSTAIN") {
|
||||||
percentNumber = Math.round(vote * 100 / (this.votescast) * 10) / 10;
|
if (type == 'yes' || type == 'no' || type == 'abstain') {
|
||||||
} else if (config == "WITHOUT_ABSTAIN" && vote >= 0) {
|
base = this.yes + this.no + this.abstain;
|
||||||
if (type == 'yes' || type == 'no') {
|
|
||||||
percentNumber = Math.round(vote * 100 / (this.yes + this.no) * 10) / 10;
|
|
||||||
}
|
}
|
||||||
|
} else if (config == "YES_NO") {
|
||||||
|
if (type == 'yes' || type == 'no') {
|
||||||
|
base = this.yes + this.no;
|
||||||
|
}
|
||||||
|
} else if (config == "VALID" && type !== 'votescast' && type !== 'votesinvalid' &&
|
||||||
|
this.votesvalid > 0) {
|
||||||
|
base = this.votesvalid;
|
||||||
|
} else if (config == "CAST" && this.votescast > 0) {
|
||||||
|
base = this.votescast;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (base !== null) {
|
||||||
|
percentNumber = Math.round(vote * 100 / (base) * 10) / 10;
|
||||||
}
|
}
|
||||||
if (percentNumber !== null) {
|
if (percentNumber !== null) {
|
||||||
percentStr = "(" + percentNumber + "%)";
|
percentStr = "(" + percentNumber + "%)";
|
||||||
|
@ -85,8 +85,7 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions', 'OpenSlid
|
|||||||
no = poll.no ? poll.no : '-',
|
no = poll.no ? poll.no : '-',
|
||||||
noRelative = poll.getVote(poll.no, 'no').percentStr,
|
noRelative = poll.getVote(poll.no, 'no').percentStr,
|
||||||
abstain = poll.abstain ? poll.abstain : '-',
|
abstain = poll.abstain ? poll.abstain : '-',
|
||||||
abstainrelativeGet = poll.getVote(poll.abstain, 'abstain').percentStr,
|
abstainRelative = poll.getVote(poll.abstain, 'abstain').percentStr,
|
||||||
abstainRelative = abstainrelativeGet ? abstainrelativeGet : '',
|
|
||||||
valid = poll.votesvalid ? poll.votesvalid : '-',
|
valid = poll.votesvalid ? poll.votesvalid : '-',
|
||||||
validRelative = poll.getVote(poll.votesvalid, 'votesvalid').percentStr,
|
validRelative = poll.getVote(poll.votesvalid, 'votesvalid').percentStr,
|
||||||
number = {
|
number = {
|
||||||
@ -122,7 +121,7 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions', 'OpenSlid
|
|||||||
yesPart = converter.createElement("columns", createPart(gettextCatalog.getString("Yes"), yes, yesRelative)),
|
yesPart = converter.createElement("columns", createPart(gettextCatalog.getString("Yes"), yes, yesRelative)),
|
||||||
noPart = converter.createElement("columns", createPart(gettextCatalog.getString("No"), no, noRelative)),
|
noPart = converter.createElement("columns", createPart(gettextCatalog.getString("No"), no, noRelative)),
|
||||||
abstainPart = converter.createElement("columns", createPart(gettextCatalog.getString("Abstain"), abstain, abstainRelative)),
|
abstainPart = converter.createElement("columns", createPart(gettextCatalog.getString("Abstain"), abstain, abstainRelative)),
|
||||||
totalPart = converter.createElement("columns", createPart(gettextCatalog.getString("Valid votes"), valid, validRelative)),
|
totalPart = converter.createElement("columns", createPart(gettextCatalog.getString("Valid ballots"), valid, validRelative)),
|
||||||
heading = converter.createElement("columns", [number, headerText]),
|
heading = converter.createElement("columns", [number, headerText]),
|
||||||
pollResult = converter.createElement("stack", [
|
pollResult = converter.createElement("stack", [
|
||||||
heading, yesPart, noPart, abstainPart, totalPart
|
heading, yesPart, noPart, abstainPart, totalPart
|
||||||
@ -794,7 +793,7 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions', 'OpenSlid
|
|||||||
key: 'votesvalid',
|
key: 'votesvalid',
|
||||||
type: 'input',
|
type: 'input',
|
||||||
templateOptions: {
|
templateOptions: {
|
||||||
label: gettextCatalog.getString('Valid votes'),
|
label: gettextCatalog.getString('Valid ballots'),
|
||||||
type: 'number'
|
type: 'number'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -802,7 +801,7 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions', 'OpenSlid
|
|||||||
key: 'votesinvalid',
|
key: 'votesinvalid',
|
||||||
type: 'input',
|
type: 'input',
|
||||||
templateOptions: {
|
templateOptions: {
|
||||||
label: gettextCatalog.getString('Invalid votes'),
|
label: gettextCatalog.getString('Invalid ballots'),
|
||||||
type: 'number'
|
type: 'number'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -810,7 +809,7 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions', 'OpenSlid
|
|||||||
key: 'votescast',
|
key: 'votescast',
|
||||||
type: 'input',
|
type: 'input',
|
||||||
templateOptions: {
|
templateOptions: {
|
||||||
label: gettextCatalog.getString('Votes cast'),
|
label: gettextCatalog.getString('Casted ballots'),
|
||||||
type: 'number'
|
type: 'number'
|
||||||
}
|
}
|
||||||
}];
|
}];
|
||||||
@ -1863,10 +1862,11 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions', 'OpenSlid
|
|||||||
// subgroup Voting and ballot papers
|
// subgroup Voting and ballot papers
|
||||||
gettext('Voting and ballot papers');
|
gettext('Voting and ballot papers');
|
||||||
gettext('The 100 % base of a voting result consists of');
|
gettext('The 100 % base of a voting result consists of');
|
||||||
gettext('All valid votes (Yes/No/Abstain)');
|
gettext('Yes/No/Abstain');
|
||||||
gettext('All votes cast (including invalid votes)');
|
gettext('Yes/No');
|
||||||
|
gettext('All valid ballots');
|
||||||
|
gettext('All casted ballots');
|
||||||
gettext('Disabled (no percents)');
|
gettext('Disabled (no percents)');
|
||||||
gettext('Yes and No votes');
|
|
||||||
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');
|
||||||
|
@ -233,7 +233,7 @@
|
|||||||
<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, '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 ballots</translate>:</span>
|
||||||
<span class="result_value">
|
<span class="result_value">
|
||||||
{{ votesValid.value }} {{ votesValid.percentStr }}
|
{{ votesValid.value }} {{ votesValid.percentStr }}
|
||||||
</span>
|
</span>
|
||||||
@ -242,24 +242,18 @@
|
|||||||
<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, '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 ballots</translate>:</span>
|
||||||
<span class="result_value">
|
<span class="result_value">
|
||||||
{{ votesInvalid.value }}
|
{{ votesInvalid.value }} {{ votesInvalid.percentStr }}
|
||||||
<span ng-if="config('motions_poll_100_percent_base') == 'WITH_INVALID'">
|
|
||||||
{{ votesInvalid.percentStr }}
|
|
||||||
</span>
|
|
||||||
</span>
|
</span>
|
||||||
<!-- votes cast -->
|
<!-- votes cast -->
|
||||||
<tr class="total" ng-if="poll.votescast !== null">
|
<tr class="total" ng-if="poll.votescast !== null">
|
||||||
<td class="icon">
|
<td class="icon">
|
||||||
<strong style="font-size: 16px">∑</strong>
|
<strong style="font-size: 16px">∑</strong>
|
||||||
<td ng-init="votesCast = poll.getVote(poll.votescast, 'votescast')">
|
<td ng-init="votesCast = poll.getVote(poll.votescast, 'votescast')">
|
||||||
<span class="result_label"><translate>Votes cast</translate>:</span>
|
<span class="result_label"><translate>Casted ballots</translate>:</span>
|
||||||
<span class="result_value">
|
<span class="result_value">
|
||||||
{{ votesCast.value }}
|
{{ votesCast.value }} {{ votesCast.percentStr }}
|
||||||
<span ng-if="config('motions_poll_100_percent_base') == 'WITH_INVALID'">
|
|
||||||
{{ votesCast.percentStr }}
|
|
||||||
</span>
|
|
||||||
</span>
|
</span>
|
||||||
</table>
|
</table>
|
||||||
</ol>
|
</ol>
|
||||||
|
@ -65,11 +65,6 @@ 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 = (
|
|
||||||
{'value': 'WITHOUT_INVALID', 'display_name': 'All valid votes (Yes/No/Abstain)'},
|
|
||||||
{'value': 'WITH_INVALID', 'display_name': 'All votes cast (including invalid votes)'},
|
|
||||||
{'value': 'DISABLED', 'display_name': 'Disabled (no percents)'})
|
|
||||||
|
|
||||||
|
|
||||||
class CollectDefaultVotesMixin(models.Model):
|
class CollectDefaultVotesMixin(models.Model):
|
||||||
"""
|
"""
|
||||||
@ -91,40 +86,10 @@ class CollectDefaultVotesMixin(models.Model):
|
|||||||
|
|
||||||
def get_percent_base_choice(self):
|
def get_percent_base_choice(self):
|
||||||
"""
|
"""
|
||||||
Returns one of the three strings in PERCENT_BASE_CHOICES.
|
Returns one of the strings of the percent base.
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError('You have to provide a get_percent_base_choice() method.')
|
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):
|
|
||||||
if self.get_percent_base_choice() == 'WITH_INVALID':
|
|
||||||
value = print_value(self.votesinvalid, self.get_percent_base())
|
|
||||||
else:
|
|
||||||
value = print_value(self.votesinvalid, None)
|
|
||||||
return value
|
|
||||||
|
|
||||||
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 and self.votesvalid > 0:
|
|
||||||
base = 100 / float(self.votesvalid)
|
|
||||||
elif self.get_percent_base_choice() == "WITH_INVALID" and self.votescast and self.votescast > 0:
|
|
||||||
base = 100 / float(self.votescast)
|
|
||||||
else:
|
|
||||||
base = None
|
|
||||||
return base
|
|
||||||
|
|
||||||
|
|
||||||
class PublishPollMixin(models.Model):
|
class PublishPollMixin(models.Model):
|
||||||
"""
|
"""
|
||||||
|
Loading…
Reference in New Issue
Block a user