Merge pull request #1942 from emanuelschuetze/assignmentPolls

Refactored AssignmentPoll (Fixed #1848)
This commit is contained in:
Norman Jäckel 2016-02-05 13:46:22 +01:00
commit eaea41564c
3 changed files with 150 additions and 25 deletions

View File

@ -6,9 +6,55 @@ angular.module('OpenSlidesApp.assignments', [])
.factory('AssignmentPollOption', [ .factory('AssignmentPollOption', [
'DS', 'DS',
function (DS) { 'jsDataModel',
'gettextCatalog',
'Config',
function (DS, jsDataModel, gettextCatalog, Config) {
return DS.defineResource({ return DS.defineResource({
name: 'assignments/polloption', 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: { relations: {
belongsTo: { belongsTo: {
'assignments/poll': { 'assignments/poll': {
@ -27,10 +73,43 @@ angular.module('OpenSlidesApp.assignments', [])
.factory('AssignmentPoll', [ .factory('AssignmentPoll', [
'DS', 'DS',
'gettextCatalog',
'AssignmentPollOption', 'AssignmentPollOption',
function (DS, AssignmentPollOption) { 'Config',
function (DS, gettextCatalog, AssignmentPollOption, Config) {
return DS.defineResource({ return DS.defineResource({
name: 'assignments/poll', 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: { relations: {
belongsTo: { belongsTo: {
'assignments/assignment': { 'assignments/assignment': {

View File

@ -254,6 +254,7 @@ angular.module('OpenSlidesApp.assignments.site', ['OpenSlidesApp.assignments'])
.controller('AssignmentDetailCtrl', [ .controller('AssignmentDetailCtrl', [
'$scope', '$scope',
'$http', '$http',
'filterFilter',
'gettext', 'gettext',
'ngDialog', 'ngDialog',
'AssignmentForm', 'AssignmentForm',
@ -262,7 +263,7 @@ angular.module('OpenSlidesApp.assignments.site', ['OpenSlidesApp.assignments'])
'User', 'User',
'assignment', 'assignment',
'phases', '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'); User.bindAll({}, $scope, 'users');
Assignment.bindOne(assignment.id, $scope, 'assignment'); Assignment.bindOne(assignment.id, $scope, 'assignment');
Assignment.loadRelations(assignment, 'agenda_item'); 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/') $http.post('/rest/assignments/assignment/' + assignment.id + '/create_poll/')
.success(function(data){ .success(function(data){
$scope.alert.show = false; $scope.alert.show = false;
if (assignment.phase == 0) {
$scope.updatePhase(1);
}
}) })
.error(function(data){ .error(function(data){
$scope.alert = { type: 'danger', msg: data.detail, show: true }; $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})}) data: JSON.stringify({user: user})})
} else { } else {
$http.post('/rest/assignments/assignment/' + assignment.id + '/mark_elected/', {'user': user}) $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', 'assignmentpoll',
'ballot', 'ballot',
function($scope, gettextCatalog, AssignmentPoll, assignmentpoll, ballot) { function($scope, gettextCatalog, AssignmentPoll, assignmentpoll, ballot) {
// set initial values for form model // set initial values for form model by create deep copy of assignmentpoll object
$scope.model = assignmentpoll; // so detail view is not updated while editing poll
$scope.model = angular.copy(assignmentpoll);
$scope.ballot = ballot; $scope.ballot = ballot;
$scope.formFields = []; $scope.formFields = [];
$scope.alert = {}; $scope.alert = {};
@ -499,6 +513,16 @@ angular.module('OpenSlidesApp.assignments.site', ['OpenSlidesApp.assignments'])
// add dynamic form fields // add dynamic form fields
assignmentpoll.options.forEach(function(option) { assignmentpoll.options.forEach(function(option) {
if (assignmentpoll.yesnoabstain) { 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( $scope.formFields.push(
{ {
noFormControl: true, noFormControl: true,
@ -511,7 +535,8 @@ angular.module('OpenSlidesApp.assignments.site', ['OpenSlidesApp.assignments'])
label: gettextCatalog.getString('Yes'), label: gettextCatalog.getString('Yes'),
type: 'number', type: 'number',
required: true required: true
} },
defaultValue: defaultValue.yes
}, },
{ {
key: 'no_' + option.candidate_id, key: 'no_' + option.candidate_id,
@ -520,7 +545,8 @@ angular.module('OpenSlidesApp.assignments.site', ['OpenSlidesApp.assignments'])
label: gettextCatalog.getString('No'), label: gettextCatalog.getString('No'),
type: 'number', type: 'number',
required: true required: true
} },
defaultValue: defaultValue.no
}, },
{ {
key:'abstain_' + option.candidate_id, key:'abstain_' + option.candidate_id,
@ -529,9 +555,14 @@ angular.module('OpenSlidesApp.assignments.site', ['OpenSlidesApp.assignments'])
label: gettextCatalog.getString('Abstain'), label: gettextCatalog.getString('Abstain'),
type: 'number', type: 'number',
required: true required: true
} },
defaultValue: defaultValue.abstain
}); });
} else { } else {
var defaultValue;
if (option.votes.length) {
defaultValue = option.votes[0].weight;
}
$scope.formFields.push( $scope.formFields.push(
{ {
key: 'vote_' + option.candidate_id, key: 'vote_' + option.candidate_id,
@ -540,7 +571,8 @@ angular.module('OpenSlidesApp.assignments.site', ['OpenSlidesApp.assignments'])
label: option.candidate.get_full_name(), label: option.candidate.get_full_name(),
type: 'number', type: 'number',
required: true required: true
} },
defaultValue: defaultValue
}); });
} }
}); });
@ -599,6 +631,7 @@ angular.module('OpenSlidesApp.assignments.site', ['OpenSlidesApp.assignments'])
}); });
}); });
} }
// save change poll object on server
poll.DSUpdate({ poll.DSUpdate({
assignment_id: poll.assignment_id, assignment_id: poll.assignment_id,
votes: votes, votes: votes,

View File

@ -150,7 +150,9 @@
<i class="fa fa-toggle-on"></i> <i class="fa fa-toggle-on"></i>
<translate>Published</translate> <translate>Published</translate>
</button> </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> <i class="fa fa-times"></i>
<translate>Delete</translate> <translate>Delete</translate>
</a> </a>
@ -160,43 +162,54 @@
<tr> <tr>
<th translate>Elected <th translate>Elected
<th translate>Candidates <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"> <tr ng-repeat="option in poll.options">
<!-- elected -->
<td class="minimum"> <td class="minimum">
<button os-perms="assignments.can_manage" <button os-perms="assignments.can_manage"
ng-click="markElected(option.candidate_id, option.is_elected)" class="btn btn-default btn-xs"> 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" title="{{ 'is elected' | translate }}"></i>
<i ng-if="!option.is_elected" class="fa fa-star-o" title="{{ 'is not elected' | translate }}"></i> <i ng-if="!option.is_elected" class="fa fa-star-o" title="{{ 'is not elected' | translate }}"></i>
</button> </button>
<!-- candidate name -->
<td> <td>
<a ui-sref="users.user.detail({id: option.candidate.id})">{{ option.candidate.get_full_name() }}</a> <a ui-sref="users.user.detail({id: option.candidate.id})">{{ option.candidate.get_full_name() }}</a>
<!-- votes -->
<td ng-if="poll.has_votes"> <td ng-if="poll.has_votes">
<span ng-if="poll.yesnoabstain && option.votes.length > 0"> <div ng-init="votes = option.getVotes()">
{{ option.votes[0].value | translate }}: {{ option.votes[0].weight }}<br> <div ng-repeat="vote in votes">
{{ option.votes[1].value | translate }}: {{ option.votes[1].weight }}<br> <span ng-if="poll.yesnoabstain">{{ vote.label }}:</span>
{{ option.votes[2].value | translate }}: {{ option.votes[2].weight }} {{ vote.value }} {{ vote.percentStr }}
</span> <div ng-if="vote.percentNumber">
<span ng-if="!poll.yesnoabstain && option.votes.length > 0"> <uib-progressbar value="vote.percentNumber" type="success"></uib-progressbar>
{{ option.votes[0].weight}} </div>
</span> </div>
</div>
<!-- total votes (valid/invalid/casts) -->
<tr> <tr>
<td> <td>
<td> <td>
<translate>Valid votes</translate> <translate>Valid votes</translate>
<td ng-if="poll.has_votes"> <td ng-if="poll.has_votes" ng-init="vote = poll.getVote(poll.votesvalid)">
{{ poll.votesvalid }} {{ vote.value }} {{ vote.percent }}
<tr> <tr>
<td> <td>
<td> <td>
<translate>Invalid votes</translate> <translate>Invalid votes</translate>
<td ng-if="poll.has_votes"> <td ng-if="poll.has_votes" ng-init="vote = poll.getVote(poll.votesinvalid)">
{{ poll.votesinvalid }} {{ vote.value }}
<tr class="total bg-info"> <tr class="total bg-info">
<td> <td>
<td> <td>
<translate>Votes cast</translate> <translate>Votes cast</translate>
<td ng-if="poll.has_votes"> <td ng-if="poll.has_votes" ng-init="vote = poll.getVote(poll.votescast)">
{{ poll.votescast }} {{ vote.value }} {{ vote.percent }}
</table> </table>
</div> </div>
</uib-tab> </uib-tab>