Refactored AssignmentPoll (Fixed #1848)
Add percents and progressbars. Template fixes and improvements for polls.
This commit is contained in:
parent
af10737388
commit
494c9aee94
@ -6,9 +6,55 @@ angular.module('OpenSlidesApp.assignments', [])
|
||||
|
||||
.factory('AssignmentPollOption', [
|
||||
'DS',
|
||||
function (DS) {
|
||||
'jsDataModel',
|
||||
'gettextCatalog',
|
||||
'Config',
|
||||
function (DS, jsDataModel, gettextCatalog, Config) {
|
||||
return DS.defineResource({
|
||||
name: 'assignments/polloption',
|
||||
useClass: jsDataModel,
|
||||
methods: {
|
||||
getVotes: function () {
|
||||
if (!this.poll.has_votes) {
|
||||
return;
|
||||
}
|
||||
var poll = this.poll;
|
||||
var votes = [];
|
||||
var config = Config.get('assignments_poll_100_percent_base').value;
|
||||
angular.forEach(this.votes, function(vote) {
|
||||
// check for special value
|
||||
var value;
|
||||
switch (vote.weight) {
|
||||
case -1:
|
||||
value = gettextCatalog.getString('majority');
|
||||
break;
|
||||
case -2:
|
||||
value = gettextCatalog.getString('undocumented');
|
||||
break;
|
||||
default:
|
||||
value = vote.weight;
|
||||
break;
|
||||
}
|
||||
// calculate percent value
|
||||
var percentStr, percentNumber;
|
||||
if (config == "WITHOUT_INVALID" && poll.votesvalid > 0 && vote.weight >= 0) {
|
||||
percentNumber = Math.round(vote.weight * 100 / poll.votesvalid * 10) / 10;
|
||||
} else if (config == "WITH_INVALID" && poll.votescast > 0 && vote.weight >= 0) {
|
||||
percentNumber = Math.round(vote.weight * 100 / (poll.votescast) * 10) / 10;
|
||||
}
|
||||
if (percentNumber) {
|
||||
percentStr = "(" + percentNumber + "%)";
|
||||
}
|
||||
votes.push({
|
||||
'label': gettextCatalog.getString(vote.value),
|
||||
'value': value,
|
||||
'percentStr': percentStr,
|
||||
'percentNumber': percentNumber
|
||||
});
|
||||
});
|
||||
return votes;
|
||||
}
|
||||
},
|
||||
relations: {
|
||||
belongsTo: {
|
||||
'assignments/poll': {
|
||||
@ -27,10 +73,43 @@ angular.module('OpenSlidesApp.assignments', [])
|
||||
|
||||
.factory('AssignmentPoll', [
|
||||
'DS',
|
||||
'gettextCatalog',
|
||||
'AssignmentPollOption',
|
||||
function (DS, AssignmentPollOption) {
|
||||
'Config',
|
||||
function (DS, gettextCatalog, AssignmentPollOption, Config) {
|
||||
return DS.defineResource({
|
||||
name: 'assignments/poll',
|
||||
methods: {
|
||||
// returns object with value and percent (for votes valid/invalid/cast only)
|
||||
getVote: function (vote) {
|
||||
if (!this.has_votes || !vote) {
|
||||
return;
|
||||
}
|
||||
var value = '';
|
||||
switch (vote) {
|
||||
case -1:
|
||||
value = gettextCatalog.getString('majority');
|
||||
break;
|
||||
case -2:
|
||||
value = gettextCatalog.getString('undocumented');
|
||||
break;
|
||||
default:
|
||||
value = vote;
|
||||
break;
|
||||
}
|
||||
// calculate percent value
|
||||
var config = Config.get('assignments_poll_100_percent_base').value;
|
||||
var percent;
|
||||
if ((config == "WITHOUT_INVALID" && vote == this.votesvalid && vote >= 0) ||
|
||||
(config == "WITH_INVALID" && vote == this.votescast && vote >= 0)) {
|
||||
percent = '(100%)';
|
||||
}
|
||||
return {
|
||||
'value': value,
|
||||
'percent': percent
|
||||
};
|
||||
},
|
||||
},
|
||||
relations: {
|
||||
belongsTo: {
|
||||
'assignments/assignment': {
|
||||
|
@ -254,6 +254,7 @@ angular.module('OpenSlidesApp.assignments.site', ['OpenSlidesApp.assignments'])
|
||||
.controller('AssignmentDetailCtrl', [
|
||||
'$scope',
|
||||
'$http',
|
||||
'filterFilter',
|
||||
'gettext',
|
||||
'ngDialog',
|
||||
'AssignmentForm',
|
||||
@ -262,7 +263,7 @@ angular.module('OpenSlidesApp.assignments.site', ['OpenSlidesApp.assignments'])
|
||||
'User',
|
||||
'assignment',
|
||||
'phases',
|
||||
function($scope, $http, gettext, ngDialog, AssignmentForm, operator, Assignment, User, assignment, phases) {
|
||||
function($scope, $http, filterFilter, gettext, ngDialog, AssignmentForm, operator, Assignment, User, assignment, phases) {
|
||||
User.bindAll({}, $scope, 'users');
|
||||
Assignment.bindOne(assignment.id, $scope, 'assignment');
|
||||
Assignment.loadRelations(assignment, 'agenda_item');
|
||||
@ -337,6 +338,9 @@ angular.module('OpenSlidesApp.assignments.site', ['OpenSlidesApp.assignments'])
|
||||
$http.post('/rest/assignments/assignment/' + assignment.id + '/create_poll/')
|
||||
.success(function(data){
|
||||
$scope.alert.show = false;
|
||||
if (assignment.phase == 0) {
|
||||
$scope.updatePhase(1);
|
||||
}
|
||||
})
|
||||
.error(function(data){
|
||||
$scope.alert = { type: 'danger', msg: data.detail, show: true };
|
||||
@ -390,6 +394,15 @@ angular.module('OpenSlidesApp.assignments.site', ['OpenSlidesApp.assignments'])
|
||||
data: JSON.stringify({user: user})})
|
||||
} else {
|
||||
$http.post('/rest/assignments/assignment/' + assignment.id + '/mark_elected/', {'user': user})
|
||||
.then(function(success) {
|
||||
var elected = filterFilter(assignment.assignment_related_users,{elected: true});
|
||||
// Set phase to 'finished' if there are enough (= number of open posts) elected users.
|
||||
// Note: The array 'elected' does NOT contains the candidate who is just marked as elected.
|
||||
// So add 1 to length to get the real number of all elected users.
|
||||
if (elected.length + 1 == assignment.open_posts ) {
|
||||
$scope.updatePhase(2);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
@ -490,8 +503,9 @@ angular.module('OpenSlidesApp.assignments.site', ['OpenSlidesApp.assignments'])
|
||||
'assignmentpoll',
|
||||
'ballot',
|
||||
function($scope, gettextCatalog, AssignmentPoll, assignmentpoll, ballot) {
|
||||
// set initial values for form model
|
||||
$scope.model = assignmentpoll;
|
||||
// set initial values for form model by create deep copy of assignmentpoll object
|
||||
// so detail view is not updated while editing poll
|
||||
$scope.model = angular.copy(assignmentpoll);
|
||||
$scope.ballot = ballot;
|
||||
$scope.formFields = [];
|
||||
$scope.alert = {};
|
||||
@ -499,6 +513,16 @@ angular.module('OpenSlidesApp.assignments.site', ['OpenSlidesApp.assignments'])
|
||||
// add dynamic form fields
|
||||
assignmentpoll.options.forEach(function(option) {
|
||||
if (assignmentpoll.yesnoabstain) {
|
||||
var defaultValue = {
|
||||
'yes': '',
|
||||
'no': '',
|
||||
'abstain': ''
|
||||
};
|
||||
if (option.votes.length) {
|
||||
defaultValue.yes = option.votes[0].weight;
|
||||
defaultValue.no = option.votes[1].weight;
|
||||
defaultValue.abstain = option.votes[2].weight;
|
||||
}
|
||||
$scope.formFields.push(
|
||||
{
|
||||
noFormControl: true,
|
||||
@ -511,7 +535,8 @@ angular.module('OpenSlidesApp.assignments.site', ['OpenSlidesApp.assignments'])
|
||||
label: gettextCatalog.getString('Yes'),
|
||||
type: 'number',
|
||||
required: true
|
||||
}
|
||||
},
|
||||
defaultValue: defaultValue.yes
|
||||
},
|
||||
{
|
||||
key: 'no_' + option.candidate_id,
|
||||
@ -520,7 +545,8 @@ angular.module('OpenSlidesApp.assignments.site', ['OpenSlidesApp.assignments'])
|
||||
label: gettextCatalog.getString('No'),
|
||||
type: 'number',
|
||||
required: true
|
||||
}
|
||||
},
|
||||
defaultValue: defaultValue.no
|
||||
},
|
||||
{
|
||||
key:'abstain_' + option.candidate_id,
|
||||
@ -529,9 +555,14 @@ angular.module('OpenSlidesApp.assignments.site', ['OpenSlidesApp.assignments'])
|
||||
label: gettextCatalog.getString('Abstain'),
|
||||
type: 'number',
|
||||
required: true
|
||||
}
|
||||
},
|
||||
defaultValue: defaultValue.abstain
|
||||
});
|
||||
} else {
|
||||
var defaultValue;
|
||||
if (option.votes.length) {
|
||||
defaultValue = option.votes[0].weight;
|
||||
}
|
||||
$scope.formFields.push(
|
||||
{
|
||||
key: 'vote_' + option.candidate_id,
|
||||
@ -540,7 +571,8 @@ angular.module('OpenSlidesApp.assignments.site', ['OpenSlidesApp.assignments'])
|
||||
label: option.candidate.get_full_name(),
|
||||
type: 'number',
|
||||
required: true
|
||||
}
|
||||
},
|
||||
defaultValue: defaultValue
|
||||
});
|
||||
}
|
||||
});
|
||||
@ -599,6 +631,7 @@ angular.module('OpenSlidesApp.assignments.site', ['OpenSlidesApp.assignments'])
|
||||
});
|
||||
});
|
||||
}
|
||||
// save change poll object on server
|
||||
poll.DSUpdate({
|
||||
assignment_id: poll.assignment_id,
|
||||
votes: votes,
|
||||
|
@ -150,7 +150,9 @@
|
||||
<i class="fa fa-toggle-on"></i>
|
||||
<translate>Published</translate>
|
||||
</button>
|
||||
<a ng-click="deleteBallot(poll)" 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-action="deleteBallot(poll)">
|
||||
<i class="fa fa-times"></i>
|
||||
<translate>Delete</translate>
|
||||
</a>
|
||||
@ -160,43 +162,54 @@
|
||||
<tr>
|
||||
<th translate>Elected
|
||||
<th translate>Candidates
|
||||
<th ng-if="poll.has_votes" translate>Votes
|
||||
<th ng-if="poll.has_votes" class="col-sm-6" translate>Votes
|
||||
|
||||
<!-- candidates (poll options) -->
|
||||
<tr ng-repeat="option in poll.options">
|
||||
|
||||
<!-- elected -->
|
||||
<td class="minimum">
|
||||
<button os-perms="assignments.can_manage"
|
||||
ng-click="markElected(option.candidate_id, option.is_elected)" class="btn btn-default btn-xs">
|
||||
<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>
|
||||
</button>
|
||||
|
||||
<!-- candidate name -->
|
||||
<td>
|
||||
<a ui-sref="users.user.detail({id: option.candidate.id})">{{ option.candidate.get_full_name() }}</a>
|
||||
|
||||
<!-- votes -->
|
||||
<td ng-if="poll.has_votes">
|
||||
<span ng-if="poll.yesnoabstain && option.votes.length > 0">
|
||||
{{ option.votes[0].value | translate }}: {{ option.votes[0].weight }}<br>
|
||||
{{ option.votes[1].value | translate }}: {{ option.votes[1].weight }}<br>
|
||||
{{ option.votes[2].value | translate }}: {{ option.votes[2].weight }}
|
||||
</span>
|
||||
<span ng-if="!poll.yesnoabstain && option.votes.length > 0">
|
||||
{{ option.votes[0].weight}}
|
||||
</span>
|
||||
<div ng-init="votes = option.getVotes()">
|
||||
<div ng-repeat="vote in votes">
|
||||
<span ng-if="poll.yesnoabstain">{{ vote.label }}:</span>
|
||||
{{ vote.value }} {{ vote.percentStr }}
|
||||
<div ng-if="vote.percentNumber">
|
||||
<uib-progressbar value="vote.percentNumber" type="success"></uib-progressbar>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- total votes (valid/invalid/casts) -->
|
||||
<tr>
|
||||
<td>
|
||||
<td>
|
||||
<translate>Valid votes</translate>
|
||||
<td ng-if="poll.has_votes">
|
||||
{{ poll.votesvalid }}
|
||||
<td ng-if="poll.has_votes" ng-init="vote = poll.getVote(poll.votesvalid)">
|
||||
{{ vote.value }} {{ vote.percent }}
|
||||
<tr>
|
||||
<td>
|
||||
<td>
|
||||
<translate>Invalid votes</translate>
|
||||
<td ng-if="poll.has_votes">
|
||||
{{ poll.votesinvalid }}
|
||||
<td ng-if="poll.has_votes" ng-init="vote = poll.getVote(poll.votesinvalid)">
|
||||
{{ vote.value }}
|
||||
<tr class="total bg-info">
|
||||
<td>
|
||||
<td>
|
||||
<translate>Votes cast</translate>
|
||||
<td ng-if="poll.has_votes">
|
||||
{{ poll.votescast }}
|
||||
<td ng-if="poll.has_votes" ng-init="vote = poll.getVote(poll.votescast)">
|
||||
{{ vote.value }} {{ vote.percent }}
|
||||
</table>
|
||||
</div>
|
||||
</uib-tab>
|
||||
|
Loading…
Reference in New Issue
Block a user