OpenSlides/openslides/assignments/static/js/assignments/base.js
Emanuel Schütze 0036567f7d Fixed TypeError in Motion and Assignment Slides (Fixed#3813)
in MotionPollDecimalPlaces and AssignmentPollDecimalPlaces
2018-08-23 15:39:15 +02:00

501 lines
21 KiB
JavaScript

(function () {
'use strict';
angular.module('OpenSlidesApp.assignments', [])
.factory('AssignmentPollOption', [
'DS',
'jsDataModel',
'gettextCatalog',
'Config',
'MajorityMethods',
function (DS, jsDataModel, gettextCatalog, Config, MajorityMethods) {
return DS.defineResource({
name: 'assignments/polloption',
useClass: jsDataModel,
// Change the stringified numbers to floats.
beforeInject: function (resource, instance) {
_.forEach(instance.votes, function (vote) {
vote.weight = parseFloat(vote.weight);
});
},
methods: {
getVotes: function () {
if (!this.poll.has_votes) {
// Return undefined if this poll has no votes.
return;
}
// 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' && base !== null) {
// this.poll.pollmethod === 'yna'
base = base[this.id];
}
_.forEach(this.votes, function (vote) {
// Initial values for the vote
var order = '',
value = '',
percentStr = '',
percentNumber;
// Check for special value
switch (vote.weight) {
case -1:
value = gettextCatalog.getString('majority');
break;
case -2:
value = gettextCatalog.getString('undocumented');
break;
default:
if (vote.weight >= 0) {
value = vote.weight;
} else {
value = 0; // Vote was not defined. Set value to 0.
}
}
switch (vote.value) {
case "Yes":
order = 1;
break;
case "No":
order = 2;
break;
case "Abstain":
order = 3;
break;
default:
order = 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 * 100) / 100;
if (percentNumber >= 0) {
percentStr = '(' + percentNumber + ' %)';
}
}
votes.push({
'order': order,
'label': gettextCatalog.getString(vote.value),
'value': value,
'percentStr': percentStr,
'percentNumber': percentNumber
});
});
return _.sortBy(votes, 'order');
},
// 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' && base !== null) {
// 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: {
belongsTo: {
'assignments/poll': {
localField: 'poll',
localKey: 'poll_id',
},
'users/user': {
localField: 'candidate',
localKey: 'candidate_id',
}
}
},
});
}
])
.factory('AssignmentPoll', [
'$http',
'DS',
'jsDataModel',
'gettextCatalog',
'AssignmentPollOption',
'Config',
function ($http, DS, jsDataModel, gettextCatalog, AssignmentPollOption, Config) {
var name = 'assignments/poll';
return DS.defineResource({
name: name,
useClass: jsDataModel,
// Change the stringified numbers to floats.
beforeInject: function (resource, instance) {
var attrs = ['votescast', 'votesinvalid', 'votesvalid', 'votesabstain', 'votesno'];
_.forEach(attrs, function (attr) {
if (instance[attr] !== null) {
instance[attr] = parseFloat(instance[attr]);
}
});
},
methods: {
getResourceName: function () {
return name;
},
// 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 !== 'votesabstain' &&
type !== 'votesno' && 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) {
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 'votesabstain':
vote = this.votesabstain;
break;
case 'votesno':
vote = this.votesno;
break;
case 'votesinvalid':
vote = this.votesinvalid;
break;
case 'votesvalid':
vote = this.votesvalid;
break;
case 'votescast':
vote = this.votescast;
break;
}
// 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;
} else {
value = 0; // value was not defined
}
}
// 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,
'percentNumber': percentNumber,
'display': value + ' ' + percentStr
};
}
},
relations: {
belongsTo: {
'assignments/assignment': {
localField: 'assignment',
localKey: 'assignment_id',
}
},
hasMany: {
'assignments/polloption': {
localField: 'options',
foreignKey: 'poll_id',
}
}
},
});
}
])
.provider('AssignmentPollDecimalPlaces', [
function () {
this.$get = ['$q', function ($q) {
return {
getPlaces: function (poll, find) {
if (find) {
return $q(function (resolve) {
resolve(0);
});
} else {
return 0;
}
},
};
}];
}
])
.factory('AssignmentRelatedUser', [
'DS',
function (DS) {
return DS.defineResource({
name: 'assignments/relateduser',
relations: {
belongsTo: {
'users/user': {
localField: 'user',
localKey: 'user_id',
}
}
}
});
}
])
.factory('Assignment', [
'$http',
'DS',
'Projector',
'ProjectHelper',
'AssignmentRelatedUser',
'AssignmentPoll',
'jsDataModel',
'gettext',
function ($http, DS, Projector, ProjectHelper, AssignmentRelatedUser, AssignmentPoll,
jsDataModel, gettext) {
var name = 'assignments/assignment';
return DS.defineResource({
name: name,
useClass: jsDataModel,
verboseName: gettext('Election'),
verboseNamePlural: gettext('Elections'),
methods: {
getResourceName: function () {
return name;
},
getTitle: function () {
return this.title;
},
getAgendaTitle: function () {
return this.getTitle();
},
// link name which is shown in search result
getSearchResultName: function () {
return this.getAgendaTitle();
},
// return true if a specific relation matches for given searchquery
// (here: related_users/candidates)
hasSearchResult: function (results) {
var assignment = this;
// search for related users (check if any user.id from already found users matches)
return _.some(results, function(result) {
if (result.getResourceName() === "users/user") {
if (_.some(assignment.assignment_related_users, {'user_id': result.id})) {
return true;
}
}
});
},
// override project function of jsDataModel factory
project: function (projectorId, pollId) {
var isProjectedIds = this.isProjected(pollId);
var requestData = {
clear_ids: isProjectedIds,
};
if (_.indexOf(isProjectedIds, projectorId) == -1) {
requestData.prune = {
id: projectorId,
element: {
name: 'assignments/assignment',
id: this.id,
poll: pollId
},
};
}
return ProjectHelper.project(requestData);
},
// override isProjected function of jsDataModel factory
isProjected: function (poll_id, anyPoll) {
// Returns the ids of all projectors with an element
// with the name 'assignments/assignment'. Else returns an empty list.
// Provide a poll_id to query a specific poll or set anyPoll to true, to
// query whether any poll (but not the assignment itself) is projected.
var self = this;
var predicate = function (element) {
var value;
if (typeof poll_id === 'undefined') {
// Assignment detail slide without poll
value = element.name == 'assignments/assignment' &&
typeof element.id !== 'undefined' &&
element.id == self.id &&
typeof element.poll === 'undefined';
} else if (anyPoll) {
// Assignment detail slide with any poll
value = element.name == 'assignments/assignment' &&
typeof element.id !== 'undefined' &&
element.id == self.id &&
typeof element.poll !== 'undefined';
} else {
// Assignment detail slide with specific poll
value = element.name == 'assignments/assignment' &&
typeof element.id !== 'undefined' &&
element.id == self.id &&
typeof element.poll !== 'undefined' &&
element.poll == poll_id;
}
return value;
};
var isProjectedIds = [];
Projector.getAll().forEach(function (projector) {
if (typeof _.findKey(projector.elements, predicate) === 'string') {
isProjectedIds.push(projector.id);
}
});
return isProjectedIds;
},
isRelatedProjected: function () {
var listOfSpeakers = [];
if (this.agenda_item) {
listOfSpeakers = this.agenda_item.isListOfSpeakersProjected();
}
return listOfSpeakers.concat(this.isProjected(null, true));
},
},
relations: {
belongsTo: {
'agenda/item': {
localKey: 'agenda_item_id',
localField: 'agenda_item',
}
},
hasMany: {
'core/tag': {
localField: 'tags',
localKeys: 'tags_id',
},
'assignments/relateduser': {
localField: 'assignment_related_users',
foreignKey: 'assignment_id',
},
'assignments/poll': {
localField: 'polls',
foreignKey: 'assignment_id',
}
}
},
beforeInject: function (resource, instance) {
AssignmentRelatedUser.ejectAll({where: {assignment_id: {'==': instance.id}}});
}
});
}
])
.run(['Assignment', function(Assignment) {}]);
}());