diff --git a/CHANGELOG b/CHANGELOG index 70c28644e..925a060b2 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -15,6 +15,7 @@ Agenda: Assignments: - Remove unused assignment config to publish winner election results only. - Added options to calculate percentages on different bases. +- Added majority calculation. Core: - Added support for big assemblies with lots of users. diff --git a/openslides/assignments/config_variables.py b/openslides/assignments/config_variables.py index 16831c49e..5fa2bef05 100644 --- a/openslides/assignments/config_variables.py +++ b/openslides/assignments/config_variables.py @@ -44,6 +44,17 @@ def get_config_variables(): group='Elections', subgroup='Ballot and ballot papers') + # TODO: Add server side validation of the choices. + yield ConfigVariable( + name='assignments_poll_default_majority_method', + default_value='simple_majority', + input_type='majorityMethod', + label='Required majority', + help_text='Default method to check whether a candidate has reached the required majority.', + weight=425, + group='Elections', + subgroup='Ballot and ballot papers') + yield ConfigVariable( name='assignments_pdf_ballot_papers_selection', default_value='CUSTOM_NUMBER', diff --git a/openslides/assignments/static/js/assignments/base.js b/openslides/assignments/static/js/assignments/base.js index c362db260..c6be1e718 100644 --- a/openslides/assignments/static/js/assignments/base.js +++ b/openslides/assignments/static/js/assignments/base.js @@ -9,97 +9,55 @@ angular.module('OpenSlidesApp.assignments', []) 'jsDataModel', 'gettextCatalog', 'Config', - function (DS, jsDataModel, gettextCatalog, Config) { + 'MajorityMethods', + function (DS, jsDataModel, gettextCatalog, Config, MajorityMethods) { return DS.defineResource({ name: 'assignments/polloption', useClass: jsDataModel, methods: { getVotes: function () { if (!this.poll.has_votes) { + // Return undefined if this poll has no votes. return; } - var poll = this.poll; - var votes = []; - 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; + + // Initial values for the option + var votes = [], + config = Config.get('assignments_poll_100_percent_base').value; + + var base = this.poll.getPercentBase(config); + if (typeof base === 'object') { + // this.poll.pollmethod === 'yna' + base = base[this.id]; } - 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) { - // check for special value - var value; + + _.forEach(this.votes, function (vote) { + // Initial values for the vote + var value = '', + percentStr = '', + percentNumber; + + // Check for special value switch (vote.weight) { case -1: value = gettextCatalog.getString('majority'); - impossible = true; break; case -2: value = gettextCatalog.getString('undocumented'); - impossible = true; break; default: if (vote.weight >= 0) { value = vote.weight; } else { - value = 0; + value = 0; // Vote was not defined. Set value to 0. } - break; } - // calculate percent value - var percentStr, percentNumber, base; - if (config == "VALID") { - if (poll.votesvalid && poll.votesvalid > 0) { - base = poll.votesvalid; - } - } 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) { + + // Special case where to skip percents + var skipPercents = config === 'YES_NO' && vote.value === 'Abstain'; + + if (base && !skipPercents) { percentNumber = Math.round(vote.weight * 100 / base * 10) / 10; - } - if (percentNumber >= 0 && percentNumber !== 'undefined') { percentStr = "(" + percentNumber + "%)"; } votes.push({ @@ -110,6 +68,45 @@ angular.module('OpenSlidesApp.assignments', []) }); }); return votes; + }, + + // Returns 0 or positive integer if quorum is reached or surpassed. + // Returns negativ integer if quorum is not reached. + // Returns undefined if we can not calculate the quorum. + isReached: function (method) { + if (!this.poll.has_votes) { + // Return undefined if this poll has no votes. + return; + } + var isReached; + var config = Config.get('assignments_poll_100_percent_base').value; + var base = this.poll.getPercentBase(config); + if (typeof base === 'object') { + // this.poll.pollmethod === 'yna' + base = base[this.id]; + } + if (base) { + // Provide result only if base is not undefined and not 0. + isReached = MajorityMethods[method](this.getVoteYes(), base); + } + return isReached; + }, + + // Returns the weight for the vote or the vote 'yes' in case of YNA poll method. + getVoteYes: function () { + var voteYes = 0; + if (this.poll.pollmethod === 'yna') { + var voteObj = _.find(this.votes, function (vote) { + return vote.value === 'Yes'; + }); + if (voteObj) { + voteYes = voteObj.weight; + } + } else { + // pollmethod === 'votes' + voteYes = this.votes[0].weight; + } + return voteYes; } }, relations: { @@ -144,10 +141,88 @@ angular.module('OpenSlidesApp.assignments', []) getResourceName: function () { return name; }, - // returns object with value and percent (for votes valid/invalid/cast only) + + // Returns percent base. Returns undefined if calculation is not possible in general. + getPercentBase: function (config, type) { + var base; + switch (config) { + case 'CAST': + if (this.votescast <= 0 || this.votesinvalid < 0) { + // It would be OK to check only this.votescast < 0 because 0 + // is checked again later but this is a little bit faster. + break; + } + base = this.votescast; + /* falls through */ + case 'VALID': + if (this.votesvalid < 0) { + base = void 0; + break; + } + if (typeof base === 'undefined' && type !== 'votescast' && type !== 'votesinvalid') { + base = this.votesvalid; + } + /* falls through */ + case 'YES_NO_ABSTAIN': + case 'YES_NO': + if (this.pollmethod === 'yna') { + if (typeof base === 'undefined' && type !== 'votescast' && type !== 'votesinvalid' && type !== 'votesvalid') { + base = {}; + _.forEach(this.options, function (option) { + var allVotes = option.votes; + if (config === 'YES_NO') { + allVotes = _.filter(allVotes, function (vote) { + // Extract abstain votes in case of YES_NO percent base. + // Do not extract abstain vote if it is set to majority so the predicate later + // fails and therefor we get an undefined base. Reason: It should not be possible + // to set abstain to majority and nevertheless calculate percents of yes and no. + return vote.value !== 'Abstain' || vote.weight === -1; + }); + } + var predicate = function (vote) { + return vote.weight < 0; + }; + if (_.findIndex(allVotes, predicate) === -1) { + base[option.id] = _.reduce(allVotes, function (sum, vote) { + return sum + vote.weight; + }, 0); + } + }); + } + } else { + // this.pollmethod === 'votes' + var predicate = function (option) { + return option.votes[0].weight < 0; + }; + if (_.findIndex(this.options, predicate) !== -1) { + base = void 0; + } else { + if (typeof base === 'undefined' && type !== 'votescast' && type !== 'votesinvalid' && type !== 'votesvalid') { + base = _.reduce(this.options, function (sum, option) { + return sum + option.votes[0].weight; + }, 0); + } + } + } + } + return base; + }, + + // Returns object with value and percent for this poll (for votes valid/invalid/cast only). getVote: function (type) { - var value, percentStr, vote; - switch(type) { + if (!this.has_votes) { + // Return undefined if this poll has no votes. + return; + } + + // Initial values + var value = '', + percentStr = '', + percentNumber, + vote, + config = Config.get('assignments_poll_100_percent_base').value; + + switch (type) { case 'votesinvalid': vote = this.votesinvalid; break; @@ -158,35 +233,34 @@ angular.module('OpenSlidesApp.assignments', []) vote = this.votescast; break; } - if (this.has_votes && vote) { - switch (vote) { - case -1: - value = gettextCatalog.getString('majority'); - break; - case -2: - value = gettextCatalog.getString('undocumented'); - break; - default: + + // Check special values + switch (vote) { + case -1: + value = gettextCatalog.getString('majority'); + break; + case -2: + value = gettextCatalog.getString('undocumented'); + break; + default: + if (vote >= 0) { value = vote; - } - if (vote >= 0) { - var config = Config.get('assignments_poll_100_percent_base').value; - var percentNumber; - if (config == "CAST" && this.votescast && this.votescast > 0) { - percentNumber = Math.round(vote * 100 / this.votescast * 10) / 10; - } else if (config == "VALID" && this.votesvalid && this.votesvalid >= 0) { - if (type === 'votesvalid'){ - percentNumber = Math.round(vote * 100 / this.votesvalid * 10) / 10; - } + } else { + value = 0; // value was not defined } - if (percentNumber !== 'undefined' && percentNumber >= 0 && percentNumber <=100) { - percentStr = "(" + percentNumber + "%)"; - } - } + } + + // Calculate percent value + var base = this.getPercentBase(config, type); + if (base) { + percentNumber = Math.round(vote * 100 / (base) * 10) / 10; + percentStr = '(' + percentNumber + ' %)'; } return { 'value': value, - 'percentStr': percentStr + 'percentStr': percentStr, + 'percentNumber': percentNumber, + 'display': value + ' ' + percentStr }; } }, diff --git a/openslides/assignments/static/js/assignments/site.js b/openslides/assignments/static/js/assignments/site.js index 940f6a42b..6fa0367b9 100644 --- a/openslides/assignments/static/js/assignments/site.js +++ b/openslides/assignments/static/js/assignments/site.js @@ -5,7 +5,8 @@ angular.module('OpenSlidesApp.assignments.site', [ 'OpenSlidesApp.assignments', 'OpenSlidesApp.core.pdf', - 'OpenSlidesApp.assignments.pdf' + 'OpenSlidesApp.assignments.pdf', + 'OpenSlidesApp.poll.majority' ]) .config([ @@ -244,6 +245,47 @@ angular.module('OpenSlidesApp.assignments.site', [ } ]) +// Cache for AssignmentPollDetailCtrl so that users choices are keeped during user actions (e. g. save poll form). +.value('AssignmentPollDetailCtrlCache', {}) + +// Child controller of AssignmentDetailCtrl for each single poll. +.controller('AssignmentPollDetailCtrl', [ + '$scope', + 'MajorityMethodChoices', + 'Config', + 'AssignmentPollDetailCtrlCache', + function ($scope, MajorityMethodChoices, Config, AssignmentPollDetailCtrlCache) { + // Define choices. + $scope.methodChoices = MajorityMethodChoices; + // TODO: Get $scope.baseChoices from config_variables.py without copying them. + + // Setup empty cache with default values. + if (typeof AssignmentPollDetailCtrlCache[$scope.poll.id] === 'undefined') { + AssignmentPollDetailCtrlCache[$scope.poll.id] = { + method: $scope.config('assignments_poll_default_majority_method'), + }; + } + + // Fetch users choices from cache. + $scope.method = AssignmentPollDetailCtrlCache[$scope.poll.id].method; + + $scope.recalculateMajorities = function (method) { + $scope.method = method; + _.forEach($scope.poll.options, function (option) { + option.majorityReached = option.isReached(method); + }); + }; + $scope.recalculateMajorities($scope.method); + + // Save current values to cache on destroy of this controller. + $scope.$on('$destroy', function() { + AssignmentPollDetailCtrlCache[$scope.poll.id] = { + method: $scope.method, + }; + }); + } +]) + .controller('AssignmentListCtrl', [ '$scope', 'ngDialog', @@ -883,6 +925,12 @@ angular.module('OpenSlidesApp.assignments.site', [ gettext('Number of all participants'); gettext('Use the following custom number'); gettext('Custom number of ballot papers'); + gettext('Required majority'); + gettext('Default method to check whether a candidate has reached the required majority.'); + gettext('Simple majority'); + gettext('Two-thirds majority'); + gettext('Three-quarters majority'); + gettext('Disabled'); gettext('Title for PDF document (all elections)'); gettext('Preamble text for PDF document (all elections)'); //other translations diff --git a/openslides/assignments/static/templates/assignments/assignment-detail.html b/openslides/assignments/static/templates/assignments/assignment-detail.html index b498f4ae1..cd363e533 100644 --- a/openslides/assignments/static/templates/assignments/assignment-detail.html +++ b/openslides/assignments/static/templates/assignments/assignment-detail.html @@ -126,119 +126,136 @@ - - - -
- - - - - Print ballot paper - - - - - - - - -
- - - - -
- - -
- Candidates - - Election method
- One vote per candidate - Yes/No/Abstain per candidate - Yes/No per candidate + index="$index" heading="{{ 'Ballot' | translate }} {{ assignment.polls.length - $index }}"> +
+ + +
+ + + + + Print ballot paper + + + + + + + +
- - - - + + - - - - + + +
Candidates - Votes
- - - -   - - {{ option.candidate.get_full_name() }} +
- -
-
-
- {{ vote.label }}: - {{ vote.value }} {{ vote.percentStr }} -
- + +
+ Candidates + + Election method
+ One vote per candidate + Yes/No/Abstain per candidate + Yes/No per candidate +
+ + +
+
+ Required majority: + + + + + + + - - -
Candidates + Votes + Quorum +
+ + + +   + + {{ option.candidate.get_full_name() }} + + + +
+
+ {{ vote.label }}: + {{ vote.value }} {{ vote.percentStr }} +
+ +
- +
+ + Quorum ({{ option.getVoteYes() - option.majorityReached }}) reached. + + + Quorum ({{ option.getVoteYes() - option.majorityReached }}) not reached. + - -
- Valid ballots - - {{ poll.getVote('votesvalid').value }} - {{ poll.getVote('votesvalid').percentStr }} -
- Invalid ballots - - {{ poll.getVote('votesinvalid').value }} - {{ poll.getVote('votesinvalid').percentStr }} -
- Casted ballots - - {{ poll.getVote('votescast').value }} - {{ poll.getVote('votescast').percentStr }} -
+ +
+ Valid ballots + + {{ poll.getVote('votesvalid').value }} + {{ poll.getVote('votesvalid').percentStr }} +
+ Invalid ballots + + {{ poll.getVote('votesinvalid').value }} + {{ poll.getVote('votesinvalid').percentStr }} +
+ Casted ballots + + {{ poll.getVote('votescast').value }} + {{ poll.getVote('votescast').percentStr }} +
+
diff --git a/openslides/motions/static/js/motions/base.js b/openslides/motions/static/js/motions/base.js index 5ff619f9d..49d289db3 100644 --- a/openslides/motions/static/js/motions/base.js +++ b/openslides/motions/static/js/motions/base.js @@ -7,6 +7,7 @@ angular.module('OpenSlidesApp.motions', [ 'OpenSlidesApp.motions.lineNumbering', 'OpenSlidesApp.motions.diff', 'OpenSlidesApp.motions.DOCX', + 'OpenSlidesApp.poll.majority', 'OpenSlidesApp.users', ]) @@ -66,7 +67,8 @@ angular.module('OpenSlidesApp.motions', [ 'DS', 'gettextCatalog', 'Config', - function (DS, gettextCatalog, Config) { + 'MajorityMethods', + function (DS, gettextCatalog, Config, MajorityMethods) { return DS.defineResource({ name: 'motions/motionpoll', relations: { @@ -78,61 +80,84 @@ angular.module('OpenSlidesApp.motions', [ } }, methods: { - // returns object with value and percent + // Returns percent base. Returns undefined if calculation is not possible in general. + getPercentBase: function (config, type) { + var base; + switch (config) { + case 'CAST': + if (this.votescast <= 0 || this.votesinvalid < 0) { + // It would be OK to check only this.votescast < 0 because 0 + // is checked again later but this is a little bit faster. + break; + } + base = this.votescast; + /* falls through */ + case 'VALID': + if (this.votesvalid < 0) { + base = void 0; + break; + } + if (typeof base === 'undefined' && type !== 'votescast' && type !== 'votesinvalid') { + base = this.votesvalid; + } + /* falls through */ + case 'YES_NO_ABSTAIN': + if (this.abstain < 0) { + base = void 0; + break; + } + if (typeof base === 'undefined' && type !== 'votescast' && type !== 'votesinvalid' && type !== 'votesvalid') { + base = this.yes + this.no + this.abstain; + } + /* falls through */ + case 'YES_NO': + if (this.yes < 0 || this.no < 0 || this.abstain === -1 ) { + // It is not allowed to set 'Abstain' to 'majority' but exclude it from calculation. + // Setting 'Abstain' to 'undocumented' is possible, of course. + base = void 0; + break; + } + if (typeof base === 'undefined' && (type === 'yes' || type === 'no')) { + base = this.yes + this.no; + } + } + return base; + }, + + // Returns object with value and percent for this poll. getVote: function (vote, type) { if (!this.has_votes) { + // Return undefined if this poll has no votes. return; } - var impossible = false; - var value = ''; + + // Initial values + var value = '', + percentStr = '', + percentNumber, + config = Config.get('motions_poll_100_percent_base').value; + + // Check special values switch (vote) { case -1: value = gettextCatalog.getString('majority'); - impossible = true; break; case -2: value = gettextCatalog.getString('undocumented'); - impossible = true; break; default: if (vote >= 0) { value = vote; } else { - value = 0; //value was not defined + value = 0; // Vote was not defined. Set value to 0. } - break; } - // 100% base impossible if at leat one value has set an - // speacial value (-1 or -2). - if (this.yes < 0 || this.no < 0 || this.abstain < 0) { - impossible = true; - } - // calculate percent value - var config = Config.get('motions_poll_100_percent_base').value; - var percentStr = ''; - var percentNumber = null; - var base = null; - if (!impossible) { - if (config == "YES_NO_ABSTAIN") { - if (type == 'yes' || type == 'no' || type == 'abstain') { - base = this.yes + this.no + this.abstain; - } - } 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) { + + // Calculate percent value + var base = this.getPercentBase(config, type); + if (base) { percentNumber = Math.round(vote * 100 / (base) * 10) / 10; - } - if (percentNumber !== null) { - percentStr = "(" + percentNumber + "%)"; + percentStr = '(' + percentNumber + ' %)'; } return { 'value': value, @@ -140,6 +165,25 @@ angular.module('OpenSlidesApp.motions', [ 'percentNumber': percentNumber, 'display': value + ' ' + percentStr }; + }, + + // Returns 0 or positive integer if quorum is reached or surpassed. + // Returns negativ integer if quorum is not reached. + // Returns undefined if we can not calculate the quorum. + isReached: function (method) { + if (!this.has_votes) { + // Return undefined if this poll has no votes. + return; + } + + var isReached; + var config = Config.get('motions_poll_100_percent_base').value; + var base = this.getPercentBase(config, 'yes'); + if (base) { + // Provide result only if base is not undefined and not 0. + isReached = MajorityMethods[method](this.yes, base); + } + return isReached; } } }); @@ -246,7 +290,7 @@ angular.module('OpenSlidesApp.motions', [ for (var i = 0; i < changes.length; i++) { var change = changes[i]; - if (statusCompareCb === undefined || statusCompareCb(change.rejected)) { + if (typeof statusCompareCb === 'undefined' || statusCompareCb(change.rejected)) { html = lineNumberingService.insertLineNumbers(html, lineLength); html = diffService.replaceLines(html, change.text, change.line_from, change.line_to); } diff --git a/openslides/motions/static/js/motions/site.js b/openslides/motions/static/js/motions/site.js index a5e1e1d38..99b016a93 100644 --- a/openslides/motions/static/js/motions/site.js +++ b/openslides/motions/static/js/motions/site.js @@ -740,42 +740,37 @@ angular.module('OpenSlidesApp.motions.site', [ .controller('MotionPollDetailCtrl', [ '$scope', 'MajorityMethodChoices', - 'MotionMajority', 'Config', 'MotionPollDetailCtrlCache', - function ($scope, MajorityMethodChoices, MotionMajority, Config, MotionPollDetailCtrlCache) { + function ($scope, MajorityMethodChoices, Config, MotionPollDetailCtrlCache) { // Define choices. $scope.methodChoices = MajorityMethodChoices; // TODO: Get $scope.baseChoices from config_variables.py without copying them. // Setup empty cache with default values. - if (MotionPollDetailCtrlCache[$scope.poll.id] === undefined) { + if (typeof MotionPollDetailCtrlCache[$scope.poll.id] === 'undefined') { MotionPollDetailCtrlCache[$scope.poll.id] = { - isMajorityCalculation: true, - isMajorityDetails: false, method: $scope.config('motions_poll_default_majority_method'), - base: $scope.config('motions_poll_100_percent_base') }; } // 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); + return $scope.poll.isReached($scope.method); + }; + + // Define template controll function + $scope.hideMajorityCalculation = function () { + return typeof $scope.isReached() === 'undefined' && $scope.method !== 'disabled'; }; // 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 }; }); } @@ -1146,7 +1141,7 @@ angular.module('OpenSlidesApp.motions.site', [ // open dialog for new amendment $scope.newAmendment = function () { var dialog = MotionForm.getDialog(); - if (dialog.scope === undefined) { + if (typeof dialog.scope === 'undefined') { dialog.scope = {}; } dialog.scope = $scope; diff --git a/openslides/motions/static/templates/motions/motion-detail.html b/openslides/motions/static/templates/motions/motion-detail.html index dfb253778..4ac22c102 100644 --- a/openslides/motions/static/templates/motions/motion-detail.html +++ b/openslides/motions/static/templates/motions/motion-detail.html @@ -295,13 +295,12 @@
+ ng-hide="hideMajorityCalculation()" + ng-cloak>
Required majority: -
@@ -309,10 +308,10 @@
- Quorum reached, {{ isReached() }} votes more than needed. + Quorum ({{ voteYes.value - isReached() }}) reached. - Quorum not reached, {{ -(isReached()) }} votes missing. + Quorum ({{ voteYes.value - isReached() }}) not reached.
diff --git a/openslides/poll/static/js/poll/majority.js b/openslides/poll/static/js/poll/majority.js index d2dd2e1e1..f2461183e 100644 --- a/openslides/poll/static/js/poll/majority.js +++ b/openslides/poll/static/js/poll/majority.js @@ -36,50 +36,6 @@ angular.module('OpenSlidesApp.poll.majority', []) }, }; } -]) - -.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; - }, - }; - } ]); }()); diff --git a/tests/integration/core/test_viewset.py b/tests/integration/core/test_viewset.py index 31b0101e9..35b33e6ae 100644 --- a/tests/integration/core/test_viewset.py +++ b/tests/integration/core/test_viewset.py @@ -144,7 +144,7 @@ class TestConfigDBQueries(TestCase): TODO: The last 57 requests are a bug. """ - with self.assertNumQueries(61): + with self.assertNumQueries(62): self.client.get(reverse('config-list'))