Refactored AssignmentPoll (Fixed #1848)

Add percents and progressbars.
Template fixes and improvements for polls.
This commit is contained in:
Emanuel Schuetze 2016-02-05 00:32:18 +01:00
parent af10737388
commit 494c9aee94
3 changed files with 150 additions and 25 deletions

View File

@ -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': {

View File

@ -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,

View File

@ -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>