Refactored majority calculation. Added cache for MotionPollDetailCtrl.
This commit is contained in:
parent
e5e1e3e8ba
commit
45aa4146da
@ -31,6 +31,7 @@ Motions:
|
|||||||
- 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.
|
- Added options to calculate percentages on different bases.
|
||||||
|
- Added majority calculation.
|
||||||
|
|
||||||
Users:
|
Users:
|
||||||
- Added field is_committee and new default group Committees.
|
- Added field is_committee and new default group Committees.
|
||||||
|
@ -13,7 +13,8 @@ INPUT_TYPE_MAPPING = {
|
|||||||
'comments': list,
|
'comments': list,
|
||||||
'colorpicker': str,
|
'colorpicker': str,
|
||||||
'datetimepicker': int,
|
'datetimepicker': int,
|
||||||
'float': float}
|
'majorityMethod': str,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class ConfigHandler:
|
class ConfigHandler:
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
// The core module for the OpenSlides site
|
// The core module for the OpenSlides site
|
||||||
angular.module('OpenSlidesApp.core.site', [
|
angular.module('OpenSlidesApp.core.site', [
|
||||||
'OpenSlidesApp.core',
|
'OpenSlidesApp.core',
|
||||||
|
'OpenSlidesApp.poll.majority',
|
||||||
'ui.router',
|
'ui.router',
|
||||||
'angular-loading-bar',
|
'angular-loading-bar',
|
||||||
'colorpicker.module',
|
'colorpicker.module',
|
||||||
@ -424,19 +425,19 @@ angular.module('OpenSlidesApp.core.site', [
|
|||||||
'Config',
|
'Config',
|
||||||
'gettextCatalog',
|
'gettextCatalog',
|
||||||
function($parse, Config, gettextCatalog) {
|
function($parse, Config, gettextCatalog) {
|
||||||
function getHtmlType(type) {
|
var getHtmlType = function (type) {
|
||||||
return {
|
return {
|
||||||
string: 'text',
|
string: 'text',
|
||||||
text: 'textarea',
|
text: 'textarea',
|
||||||
integer: 'number',
|
integer: 'number',
|
||||||
float: 'number',
|
|
||||||
boolean: 'checkbox',
|
boolean: 'checkbox',
|
||||||
choice: 'choice',
|
choice: 'choice',
|
||||||
comments: 'comments',
|
comments: 'comments',
|
||||||
colorpicker: 'colorpicker',
|
colorpicker: 'colorpicker',
|
||||||
datetimepicker: 'datetimepicker',
|
datetimepicker: 'datetimepicker',
|
||||||
|
majorityMethod: 'choice',
|
||||||
}[type];
|
}[type];
|
||||||
}
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
restrict: 'E',
|
restrict: 'E',
|
||||||
@ -586,11 +587,12 @@ angular.module('OpenSlidesApp.core.site', [
|
|||||||
// Config Controller
|
// Config Controller
|
||||||
.controller('ConfigCtrl', [
|
.controller('ConfigCtrl', [
|
||||||
'$scope',
|
'$scope',
|
||||||
|
'MajorityMethodChoices',
|
||||||
'Config',
|
'Config',
|
||||||
'configOptions',
|
'configOptions',
|
||||||
'gettextCatalog',
|
'gettextCatalog',
|
||||||
'DateTimePickerTranslation',
|
'DateTimePickerTranslation',
|
||||||
function($scope, Config, configOptions, gettextCatalog, DateTimePickerTranslation) {
|
function($scope, MajorityMethodChoices, Config, configOptions, gettextCatalog, DateTimePickerTranslation) {
|
||||||
Config.bindAll({}, $scope, 'configs');
|
Config.bindAll({}, $scope, 'configs');
|
||||||
$scope.configGroups = configOptions.data.config_groups;
|
$scope.configGroups = configOptions.data.config_groups;
|
||||||
$scope.dateTimePickerTranslatedButtons = DateTimePickerTranslation.getButtons();
|
$scope.dateTimePickerTranslatedButtons = DateTimePickerTranslation.getButtons();
|
||||||
@ -601,7 +603,7 @@ angular.module('OpenSlidesApp.core.site', [
|
|||||||
Config.save(key);
|
Config.save(key);
|
||||||
};
|
};
|
||||||
|
|
||||||
/* For comments input */
|
// For comments input
|
||||||
$scope.addComment = function (key, parent) {
|
$scope.addComment = function (key, parent) {
|
||||||
parent.value.push({
|
parent.value.push({
|
||||||
name: gettextCatalog.getString('New'),
|
name: gettextCatalog.getString('New'),
|
||||||
@ -613,6 +615,26 @@ angular.module('OpenSlidesApp.core.site', [
|
|||||||
parent.value.splice(index, 1);
|
parent.value.splice(index, 1);
|
||||||
$scope.save(key, parent.value);
|
$scope.save(key, parent.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// For majority method
|
||||||
|
angular.forEach(
|
||||||
|
_.filter($scope.configGroups, function (configGroup) {
|
||||||
|
return configGroup.name === 'Motions' || configGroup.name === 'Elections';
|
||||||
|
}),
|
||||||
|
function (configGroup) {
|
||||||
|
var configItem;
|
||||||
|
_.forEach(configGroup.subgroups, function (subgroup) {
|
||||||
|
configItem = _.find(subgroup.items, ['input_type', 'majorityMethod']);
|
||||||
|
if (configItem !== undefined) {
|
||||||
|
// Break the forEach loop if we found something.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (configItem !== undefined) {
|
||||||
|
configItem.choices = MajorityMethodChoices;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from django.core.validators import MaxValueValidator, MinValueValidator
|
from django.core.validators import MinValueValidator
|
||||||
|
|
||||||
from openslides.core.config import ConfigVariable
|
from openslides.core.config import ConfigVariable
|
||||||
|
|
||||||
@ -186,16 +186,16 @@ def get_config_variables():
|
|||||||
group='Motions',
|
group='Motions',
|
||||||
subgroup='Voting and ballot papers')
|
subgroup='Voting and ballot papers')
|
||||||
|
|
||||||
|
# TODO: Add server side validation of the choices.
|
||||||
yield ConfigVariable(
|
yield ConfigVariable(
|
||||||
name='motions_poll_default_quorum',
|
name='motions_poll_default_majority_method',
|
||||||
default_value=50,
|
default_value='simple_majority',
|
||||||
input_type='float',
|
input_type='majorityMethod',
|
||||||
label='Quorum for Majority tests',
|
label='Method for majority tests',
|
||||||
help_text='Default percentage that must be surpassed for a motion to be successfull',
|
help_text='Default method to determine whether a motion is successful.',
|
||||||
weight=357,
|
weight=357,
|
||||||
group='Motions',
|
group='Motions',
|
||||||
subgroup='Voting and ballot papers',
|
subgroup='Voting and ballot papers')
|
||||||
validators=(MinValueValidator(0), MaxValueValidator(100),))
|
|
||||||
|
|
||||||
yield ConfigVariable(
|
yield ConfigVariable(
|
||||||
name='motions_pdf_ballot_papers_selection',
|
name='motions_pdf_ballot_papers_selection',
|
||||||
|
@ -6,6 +6,7 @@ angular.module('OpenSlidesApp.motions.site', [
|
|||||||
'OpenSlidesApp.motions',
|
'OpenSlidesApp.motions',
|
||||||
'OpenSlidesApp.motions.diff',
|
'OpenSlidesApp.motions.diff',
|
||||||
'OpenSlidesApp.motions.motionservices',
|
'OpenSlidesApp.motions.motionservices',
|
||||||
|
'OpenSlidesApp.poll.majority',
|
||||||
'OpenSlidesApp.core.pdf',
|
'OpenSlidesApp.core.pdf',
|
||||||
'OpenSlidesApp.motions.pdf'
|
'OpenSlidesApp.motions.pdf'
|
||||||
])
|
])
|
||||||
@ -479,83 +480,51 @@ angular.module('OpenSlidesApp.motions.site', [
|
|||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
|
||||||
// child controller of MotionDetailCtrl for each single poll.
|
// Cache for MotionPollDetailCtrl so that users choices are keeped during user actions (e. g. save poll form).
|
||||||
// TODO for now it is ust needed for majority Tests, which may be moved to a more generic
|
.value('MotionPollDetailCtrlCache', {})
|
||||||
// place later
|
|
||||||
|
// Child controller of MotionDetailCtrl for each single poll.
|
||||||
.controller('MotionPollDetailCtrl', [
|
.controller('MotionPollDetailCtrl', [
|
||||||
'$scope',
|
'$scope',
|
||||||
|
'MajorityMethodChoices',
|
||||||
|
'MotionMajority',
|
||||||
'Config',
|
'Config',
|
||||||
function($scope, Config) {
|
'MotionPollDetailCtrlCache',
|
||||||
$scope.base = Config.get('motions_poll_100_percent_base').value;
|
function ($scope, MajorityMethodChoices, MotionMajority, Config, MotionPollDetailCtrlCache) {
|
||||||
$scope.basechoices = [{'value': 'YES_NO_ABSTAIN', 'display_name': 'Yes/No/Abstain'},
|
// Define choices.
|
||||||
{'value': 'YES_NO', 'display_name': 'Yes/No'},
|
$scope.methodChoices = MajorityMethodChoices;
|
||||||
{'value': 'VALID', 'display_name': 'All valid ballots'},
|
// TODO: Get $scope.baseChoices from config_variables.py without copying them.
|
||||||
{'value': 'CAST', 'display_name': 'All casted ballots'},
|
|
||||||
{'value': 'DISABLED', 'display_name': 'Disabled (no percents)'}];
|
// Setup empty cache with default values.
|
||||||
$scope.quorum = Config.get('motions_poll_default_quorum').value;
|
if (MotionPollDetailCtrlCache[$scope.poll.id] === undefined) {
|
||||||
$scope.isPossible = function() {
|
MotionPollDetailCtrlCache[$scope.poll.id] = {
|
||||||
if ($scope.base == 'CAST' && $scope.poll.votescast > 0) {
|
isMajorityCalculation: true,
|
||||||
return true;
|
isMajorityDetails: false,
|
||||||
} else if ($scope.base == 'VALID' && $scope.poll.votesvalid > 0) {
|
method: $scope.config('motions_poll_default_majority_method'),
|
||||||
return true;
|
base: $scope.config('motions_poll_100_percent_base')
|
||||||
} else if ($scope.base == 'YES_NO_ABSTAIN' &&
|
|
||||||
(!$scope.poll.yes || $scope.poll.yes >= 0) &&
|
|
||||||
(!$scope.poll.no ||$scope.poll.no >= 0) &&
|
|
||||||
(!$scope.poll.abstain || $scope.poll.abstain >= 0) &&
|
|
||||||
($scope.poll.yes + $scope.poll.no + $scope.poll.abstain > 0)) {
|
|
||||||
return true;
|
|
||||||
} else if ($scope.base == 'YES_NO' &&
|
|
||||||
(!$scope.poll.yes || $scope.poll.yes >= 0) &&
|
|
||||||
(!$scope.poll.no ||$scope.poll.no >= 0) &&
|
|
||||||
($scope.poll.yes + $scope.poll.no > 0)) {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
// returns an integer. 0 and positive numbers indicate a success and the amount of votes
|
|
||||||
// in excess, negative numbers are a failure (amount of missing votes), or null in case of error
|
|
||||||
$scope.isReached = function() {
|
|
||||||
var basenr;
|
|
||||||
if ($scope.base == 'CAST' && $scope.poll.votescast > 0) {
|
|
||||||
basenr = $scope.poll.votescast;
|
|
||||||
} else if ($scope.base == 'VALID' && $scope.poll.votesvalid > 0) {
|
|
||||||
basenr = $scope.poll.votesvalid;
|
|
||||||
} else if ($scope.base == 'YES_NO') {
|
|
||||||
basenr = 0;
|
|
||||||
if ($scope.poll.yes > 0) {
|
|
||||||
basenr = $scope.poll.yes;
|
|
||||||
}
|
|
||||||
if ($scope.poll.no > 0) {
|
|
||||||
basenr = basenr + $scope.poll.no;
|
|
||||||
}
|
|
||||||
} else if ($scope.base == 'YES_NO_ABSTAIN') {
|
|
||||||
basenr = 0;
|
|
||||||
if ($scope.poll.yes > 0) {
|
|
||||||
basenr = $scope.poll.yes;
|
|
||||||
}
|
|
||||||
if ($scope.poll.no > 0) {
|
|
||||||
basenr = basenr + $scope.poll.no;
|
|
||||||
}
|
|
||||||
if ($scope.poll.abstain > 0) {
|
|
||||||
basenr = basenr + $scope.poll.abstain;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (basenr > 0) {
|
|
||||||
var needed = Math.ceil(basenr / 100 * $scope.quorum);
|
|
||||||
if ((basenr / 100 * $scope.quorum) % 1 === 0) {
|
|
||||||
//the quorum is exactly reached, not passed
|
|
||||||
needed = needed + 1;
|
|
||||||
}
|
|
||||||
if ($scope.poll.yes >= 0) {
|
|
||||||
var result = $scope.poll.yes - needed;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return 'undefined';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fetch users choices from cache.
|
||||||
|
$scope.isMajorityCalculation = MotionPollDetailCtrlCache[$scope.poll.id].isMajorityCalculation;
|
||||||
|
$scope.isMajorityDetails = MotionPollDetailCtrlCache[$scope.poll.id].isMajorityDetails;
|
||||||
|
$scope.method = MotionPollDetailCtrlCache[$scope.poll.id].method;
|
||||||
|
$scope.base = MotionPollDetailCtrlCache[$scope.poll.id].base;
|
||||||
|
|
||||||
|
// Define result function.
|
||||||
|
$scope.isReached = function () {
|
||||||
|
return MotionMajority.isReached($scope.base, $scope.method, $scope.poll);
|
||||||
};
|
};
|
||||||
//isPlausible: TODO: check if the sums match up
|
|
||||||
|
// Save current values to cache on detroy of this controller.
|
||||||
|
$scope.$on('$destroy', function() {
|
||||||
|
MotionPollDetailCtrlCache[$scope.poll.id] = {
|
||||||
|
isMajorityCalculation: $scope.isMajorityCalculation,
|
||||||
|
isMajorityDetails: $scope.isMajorityDetails,
|
||||||
|
method: $scope.method,
|
||||||
|
base: $scope.base
|
||||||
|
};
|
||||||
|
});
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
|
||||||
@ -1224,12 +1193,11 @@ angular.module('OpenSlidesApp.motions.site', [
|
|||||||
.controller('MotionPollUpdateCtrl', [
|
.controller('MotionPollUpdateCtrl', [
|
||||||
'$scope',
|
'$scope',
|
||||||
'gettextCatalog',
|
'gettextCatalog',
|
||||||
'Config',
|
|
||||||
'MotionPoll',
|
'MotionPoll',
|
||||||
'MotionPollForm',
|
'MotionPollForm',
|
||||||
'motionpoll',
|
'motionpoll',
|
||||||
'voteNumber',
|
'voteNumber',
|
||||||
function($scope, gettextCatalog, Config, MotionPoll, MotionPollForm, motionpoll, voteNumber) {
|
function($scope, gettextCatalog, MotionPoll, MotionPollForm, motionpoll, voteNumber) {
|
||||||
// set initial values for form model by create deep copy of motionpoll object
|
// set initial values for form model by create deep copy of motionpoll object
|
||||||
// so detail view is not updated while editing poll
|
// so detail view is not updated while editing poll
|
||||||
$scope.model = angular.copy(motionpoll);
|
$scope.model = angular.copy(motionpoll);
|
||||||
|
@ -166,11 +166,13 @@
|
|||||||
<li ng-controller="MotionPollDetailCtrl" ng-repeat="poll in motion.polls" class="spacer"
|
<li ng-controller="MotionPollDetailCtrl" ng-repeat="poll in motion.polls" class="spacer"
|
||||||
ng-if="poll.has_votes || operator.hasPerms('motions.can_manage')">
|
ng-if="poll.has_votes || operator.hasPerms('motions.can_manage')">
|
||||||
<strong translate-comment='ballot of a motion' translate>Vote</strong>
|
<strong translate-comment='ballot of a motion' translate>Vote</strong>
|
||||||
|
|
||||||
<!-- Edit poll -->
|
<!-- Edit poll -->
|
||||||
<button os-perms="motions.can_manage" ng-click="openPollDialog(poll, $index+1)"
|
<button os-perms="motions.can_manage" ng-click="openPollDialog(poll, $index+1)"
|
||||||
class="btn btn-default btn-xs" title="{{ 'Edit' | translate }}">
|
class="btn btn-default btn-xs" title="{{ 'Edit' | translate }}">
|
||||||
<i class="fa fa-pencil"></i>
|
<i class="fa fa-pencil"></i>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<!-- Delete poll -->
|
<!-- Delete poll -->
|
||||||
<button os-perms="motions.can_manage" class="btn btn-default btn-xs"
|
<button os-perms="motions.can_manage" class="btn btn-default btn-xs"
|
||||||
ng-bootbox-confirm="{{ 'Are you sure you want to delete this poll?' | translate }}"
|
ng-bootbox-confirm="{{ 'Are you sure you want to delete this poll?' | translate }}"
|
||||||
@ -178,31 +180,31 @@
|
|||||||
title="{{ 'Delete' | translate }}">
|
title="{{ 'Delete' | translate }}">
|
||||||
<i class="fa fa-times"></i>
|
<i class="fa fa-times"></i>
|
||||||
</button>
|
</button>
|
||||||
<!-- print poll PDF -->
|
|
||||||
|
<!-- Print poll PDF -->
|
||||||
<a ng-click="makePollPDF()" class="btn btn-default btn-xs"
|
<a ng-click="makePollPDF()" class="btn btn-default btn-xs"
|
||||||
title="{{ 'Print ballot paper' | translate }}">
|
title="{{ 'Print ballot paper' | translate }}">
|
||||||
<i class="fa fa-file-pdf-o"></i>
|
<i class="fa fa-file-pdf-o"></i>
|
||||||
</a>
|
</a>
|
||||||
<!-- template hook for motion poll buttons -->
|
|
||||||
|
<!-- Template hook for motion poll buttons -->
|
||||||
<template-hook hook-name="motionPollSmallButtons"></template-hook>
|
<template-hook hook-name="motionPollSmallButtons"></template-hook>
|
||||||
<!--setting for majority calculations-->
|
|
||||||
|
<!-- Settings for majority calculations -->
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input type="checkbox" ng-model="isMajorityCalculation">
|
<input type="checkbox" ng-model="isMajorityCalculation">
|
||||||
<span translate> calculate majorities</span>
|
<span translate>Calculate majority</span>
|
||||||
<a href="#" ng-click="isMajorityDetails = !isMajorityDetails">
|
<a href="#" ng-click="isMajorityDetails = !isMajorityDetails">
|
||||||
<i class="fa toggle-icon" ng-class="isMajorityDetails ? 'fa-angle-up' : 'fa-angle-down'"></i>
|
<i class="fa toggle-icon" ng-class="isMajorityDetails ? 'fa-angle-up' : 'fa-angle-down'"></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div uib-collapse="!isMajorityDetails" ng-cloak>
|
<div uib-collapse="!isMajorityDetails" ng-cloak>
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<span translate> Minimal percentage needed:</span> <input type="number" min=0 max=100 step="any" ng-model="quorum"
|
<span translate>Majority method:</span>
|
||||||
ng-model-options="{debounce: 1000}" maxlength=9> %
|
<select ng-model="method" ng-options="option.value as option.display_name for option in methodChoices" />
|
||||||
</div>
|
|
||||||
<div class="input-group">
|
|
||||||
<span translate> Calculation base:</span>
|
|
||||||
<select ng-model="base" ng-options="option.value as option.display_name for option in basechoices"/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Poll results -->
|
<!-- Poll results -->
|
||||||
<div ng-show="poll.has_votes" class="pollresults">
|
<div ng-show="poll.has_votes" class="pollresults">
|
||||||
<table class="table">
|
<table class="table">
|
||||||
@ -273,14 +275,14 @@
|
|||||||
<tr ng-if="isMajorityCalculation">
|
<tr ng-if="isMajorityCalculation">
|
||||||
<td class="icon">
|
<td class="icon">
|
||||||
<td>
|
<td>
|
||||||
<span class="text-warning" ng-if="isPossible() === false" translate>Calculation impossible</span>
|
<span class="text-warning" ng-if="isReached() === undefined" translate>Calculation impossible</span>
|
||||||
<span class="text-success" ng-if=" isReached() >= 0">
|
<span class="text-success" ng-if="isReached() >= 0">
|
||||||
<translate>Quorum reached, </translate>
|
<translate>Quorum reached,</translate>
|
||||||
{{isReached()}} <translate>votes more than needed.</translate>
|
{{ isReached() }} <translate>votes more than needed.</translate>
|
||||||
</span>
|
</span>
|
||||||
<span class="text-danger" ng-if="isReached() < 0">
|
<span class="text-danger" ng-if="isReached() < 0">
|
||||||
<translate>Quorum not reached, </translate>
|
<translate>Quorum not reached,</translate>
|
||||||
{{-(isReached())}} <translate> votes missing.</translate>
|
{{ -(isReached()) }} <translate>votes missing.</translate>
|
||||||
</span>
|
</span>
|
||||||
</table>
|
</table>
|
||||||
</ol>
|
</ol>
|
||||||
|
81
openslides/poll/static/js/poll/majority.js
Normal file
81
openslides/poll/static/js/poll/majority.js
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
(function () {
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
angular.module('OpenSlidesApp.poll.majority', [])
|
||||||
|
|
||||||
|
.value('MajorityMethodChoices', [
|
||||||
|
{'value': 'simple_majority', 'display_name': 'Simple majority'},
|
||||||
|
{'value': 'two-thirds_majority', 'display_name': 'Two-thirds majority'},
|
||||||
|
{'value': 'three-quarters_majority', 'display_name': 'Three-quarters majority'},
|
||||||
|
])
|
||||||
|
|
||||||
|
.factory('MajorityMethods', [
|
||||||
|
function () {
|
||||||
|
return {
|
||||||
|
'simple_majority': function (vote, base) {
|
||||||
|
return Math.ceil(-(base / 2 - vote)) - 1;
|
||||||
|
},
|
||||||
|
'two-thirds_majority': function (vote, base) {
|
||||||
|
var result = -(base * 2 - vote * 3) / 3;
|
||||||
|
if (result % 1 !== 0) {
|
||||||
|
result = Math.ceil(result) - 1;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
'three-quarters_majority': function (vote, base) {
|
||||||
|
var result = -(base * 3 - vote * 4) / 4;
|
||||||
|
if (result % 1 !== 0) {
|
||||||
|
result = Math.ceil(result) - 1;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
.factory('MotionMajority', [
|
||||||
|
'MajorityMethods',
|
||||||
|
function (MajorityMethods) {
|
||||||
|
return {
|
||||||
|
isReached: function (base, method, votes) {
|
||||||
|
var methodFunction = MajorityMethods[method];
|
||||||
|
var yes = parseInt(votes.yes);
|
||||||
|
var no = parseInt(votes.no);
|
||||||
|
var abstain = parseInt(votes.abstain);
|
||||||
|
var valid = parseInt(votes.votesvalid);
|
||||||
|
var cast = parseInt(votes.votescast);
|
||||||
|
var result;
|
||||||
|
var isValid = function (vote) {
|
||||||
|
return !isNaN(vote) && vote >= 0;
|
||||||
|
};
|
||||||
|
switch (base) {
|
||||||
|
case 'YES_NO_ABSTAIN':
|
||||||
|
if (isValid(yes) && isValid(no) && isValid(abstain)) {
|
||||||
|
result = methodFunction(yes, yes + no + abstain);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'YES_NO':
|
||||||
|
if (isValid(yes) && isValid(no)) {
|
||||||
|
result = methodFunction(yes, yes + no);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'VALID':
|
||||||
|
if (isValid(yes) && isValid(valid)) {
|
||||||
|
result = methodFunction(yes, valid);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'CAST':
|
||||||
|
if (isValid(yes) && isValid(cast)) {
|
||||||
|
result = methodFunction(yes, cast);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
// case 'DISABLED': result remains undefined
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
}());
|
Loading…
Reference in New Issue
Block a user