Merge pull request #1964 from emanuelschuetze/assignmentpoll

Assignmentpoll slide
This commit is contained in:
Norman Jäckel 2016-02-15 23:50:43 +01:00
commit 2d5f5c685c
11 changed files with 177 additions and 41 deletions

View File

@ -185,7 +185,6 @@ class Item(RESTModelMixin, models.Model):
An Agenda Item
"""
objects = ItemManager()
slide_callback_name = 'agenda'
AGENDA_ITEM = 1
HIDDEN_ITEM = 2

View File

@ -81,15 +81,22 @@ angular.module('OpenSlidesApp.agenda', ['OpenSlidesApp.users'])
},
// override isProjected function of jsDataModel factory
isProjected: function () {
// Returns true if there is a projector element with the same
// name and the same id.
var projector = Projector.get(1);
if (typeof projector === 'undefined') return false;
var self = this;
var predicate = function (element) {
return element.name == self.content_object.collection &&
typeof element.id !== 'undefined' &&
element.id == self.content_object.id;
};
return typeof _.findKey(projector.elements, predicate) === 'string';
var isProjected;
if (typeof projector !== 'undefined') {
var self = this;
var predicate = function (element) {
return element.name == self.content_object.collection &&
typeof element.id !== 'undefined' &&
element.id == self.content_object.id;
};
isProjected = typeof _.findKey(projector.elements, predicate) === 'string';
} else {
isProjected = false;
}
return isProjected;
},
// project list of speakers
projectListOfSpeakers: function() {

View File

@ -49,7 +49,6 @@ class AssignmentRelatedUser(RESTModelMixin, models.Model):
class Assignment(RESTModelMixin, models.Model):
slide_callback_name = 'assignment'
PHASE_SEARCH = 0
PHASE_VOTING = 1
@ -345,7 +344,6 @@ class AssignmentOption(RESTModelMixin, BaseOption):
class AssignmentPoll(RESTModelMixin, CollectDefaultVotesMixin,
PublishPollMixin, BasePoll):
slide_callback_name = 'assignmentpoll'
option_class = AssignmentOption
assignment = models.ForeignKey(

View File

@ -2,7 +2,7 @@ from openslides.core.exceptions import ProjectorException
from openslides.core.views import TagViewSet
from openslides.utils.projector import ProjectorElement, ProjectorRequirement
from .models import Assignment
from .models import Assignment, AssignmentPoll
from .views import AssignmentViewSet
@ -20,6 +20,11 @@ class AssignmentSlide(ProjectorElement):
# Detail slide.
if not Assignment.objects.filter(pk=pk).exists():
raise ProjectorException('Election does not exist.')
poll_id = self.config_entry.get('poll')
if poll_id is not None:
# Poll slide.
if not AssignmentPoll.objects.filter(pk=poll_id).exists():
raise ProjectorException('Poll does not exist.')
def get_requirements(self, config_entry):
pk = config_entry.get('id')

View File

@ -72,14 +72,21 @@ angular.module('OpenSlidesApp.assignments', [])
])
.factory('AssignmentPoll', [
'$http',
'DS',
'jsDataModel',
'gettextCatalog',
'AssignmentPollOption',
'Config',
function (DS, gettextCatalog, AssignmentPollOption, Config) {
function ($http, DS, jsDataModel, gettextCatalog, AssignmentPollOption, Config) {
var name = 'assignments/poll';
return DS.defineResource({
name: 'assignments/poll',
name: name,
useClass: jsDataModel,
methods: {
getResourceName: function () {
return name;
},
// returns object with value and percent (for votes valid/invalid/cast only)
getVote: function (vote) {
if (!this.has_votes || !vote) {
@ -148,11 +155,12 @@ angular.module('OpenSlidesApp.assignments', [])
.factory('Assignment', [
'$http',
'DS',
'Projector',
'AssignmentRelatedUser',
'AssignmentPoll',
'jsDataModel',
'gettext',
function ($http, DS, AssignmentRelatedUser, AssignmentPoll, jsDataModel, gettext) {
function ($http, DS, Projector, AssignmentRelatedUser, AssignmentPoll, jsDataModel, gettext) {
var name = 'assignments/assignment';
var phases;
return DS.defineResource({
@ -183,6 +191,45 @@ angular.module('OpenSlidesApp.assignments', [])
// subtitle of search result
getSearchResultSubtitle: function () {
return "Election";
},
// override project function of jsDataModel factory
project: function (poll_id) {
return $http.post(
'/rest/core/projector/1/prune_elements/',
[{name: 'assignments/assignment', id: this.id, poll: poll_id}]
);
},
// override isProjected function of jsDataModel factory
isProjected: function (poll_id) {
// Returns true if there is a projector element with the name
// 'assignments/assignment'.
var projector = Projector.get(1);
var isProjected;
if (typeof projector !== 'undefined') {
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 {
// 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;
};
isProjected = typeof _.findKey(projector.elements, predicate) === 'string';
} else {
isProjected = false;
}
return isProjected;
}
},
relations: {

View File

@ -22,6 +22,7 @@ angular.module('OpenSlidesApp.assignments.projector', ['OpenSlidesApp.assignment
// Add it to the coresponding get_requirements method of the ProjectorElement
// class.
var id = $scope.element.id;
var poll = $scope.element.poll;
// load assignemt object and related agenda item
Assignment.find(id).then(function(assignment) {

View File

@ -143,14 +143,22 @@
<button ng-if="!poll.published" ng-click="publishBallot(poll, true)"
class="btn btn-default btn-sm">
<i class="fa fa-toggle-off"></i>
3. <translate>Publish ballot</translate>
3. <translate>Publish</translate>
</button>
<button ng-if="poll.published" ng-click="publishBallot(poll, false)"
class="btn btn-default btn-sm">
<i class="fa fa-toggle-on"></i>
<translate>Published</translate>
3. <translate>Published</translate>
</button>
<a class="btn btn-danger btn-sm"
<i class="fa fa-arrow-right"></i>
<button os-perms="core.can_manage_projector" class="btn btn-default btn-sm"
ng-class="{ 'btn-primary': assignment.isProjected(poll.id) }"
ng-click="assignment.project(poll.id)"
title="{{ 'Project ballot' | translate }}">
<i class="fa fa-video-camera"></i>
4. <translate>Project</translate>
</button>
| <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>

View File

@ -19,13 +19,81 @@
</div>
<!-- Description -->
<div class="white-space-pre-line">{{ assignment.description }}</div>
<div ng-hide="element.poll" class="white-space-pre-line">
{{ assignment.description }}
</div>
<!-- Candidates -->
<h3 translate>Candidates</h3>
<ol>
<li ng-repeat="related_user in assignment.assignment_related_users">
{{ related_user.user.get_full_name() }}
<i ng-if="related_user.elected" class="fa fa-star" title="{{ 'is elected' | translate }}"></i>
</ol>
<div ng-hide="element.poll">
<h3 translate>Candidates</h3>
<ol>
<li ng-repeat="related_user in assignment.assignment_related_users">
{{ related_user.user.get_full_name() }}
<i ng-if="related_user.elected" class="fa fa-star" title="{{ 'is elected' | translate }}"></i>
</ol>
</div>
<!-- vote results -->
<div ng-show="element.poll" class="electionresults spacer" ng-repeat="poll in assignment.polls | filter: {id: element.poll}">
<table class="table table-bordered table-striped minimumTable">
<tr>
<th translate>Candidates
<th ng-if="poll.has_votes" class="col-sm-6" translate>Votes
<!-- candidates (poll options) -->
<tr ng-repeat="option in poll.options">
<!-- candidate name -->
<td>
<i ng-if="option.is_elected" class="fa fa-star" title="{{ 'is elected' | translate }}"></i>
<strong>{{ option.candidate.get_full_name() }}</strong>
<!-- votes -->
<td ng-if="poll.has_votes">
<div ng-init="votes = option.getVotes()">
<div ng-show="poll.yesnoabstain">
<span>
{{ votes[0].label }}: <strong>{{ votes[0].value }}</strong> ·
{{ votes[1].label }}: {{ votes[1].value }} ·
{{ votes[2].label }}: {{ votes[2].value }} </span>
<uib-progress ng-if="votes[0].percentNumber">
<uib-bar value="votes[0].percentNumber" type="success">
<span ng-hide="votes[0].percentNumber < 5">{{votes[0].percentNumber}} %</span>
</uib-bar>
<uib-bar value="votes[1].percentNumber" type="danger">
<span ng-hide="votes[1].percentNumber < 5">{{votes[1].percentNumber}} %</span>
</uib-bar>
<uib-bar value="votes[2].percentNumber" type="warning"></uib-bar>
<span ng-hide="votes[2].percentNumber < 5">{{votes[2].percentNumber}} %</span>
</uib-bar>
</uib-progress>
</div>
<div ng-hide="poll.yesnoabstain">
<div ng-repeat="vote in votes">
{{ vote.value }} {{ vote.percentStr }} - {{vote.percentNumber}}
<div ng-if="vote.percentNumber">
<uib-progressbar value="vote.percentNumber" type="success"></uib-progressbar>
</div>
</div>
</div>
</div>
<!-- total votes (valid/invalid/casts) -->
<tr class="total">
<td>
<translate>Valid votes</translate>
<td ng-if="poll.has_votes" ng-init="vote = poll.getVote(poll.votesvalid)">
{{ vote.value }} {{ vote.percent }}
<tr class="total">
<td>
<translate>Invalid votes</translate>
<td ng-if="poll.has_votes" ng-init="vote = poll.getVote(poll.votesinvalid)">
{{ vote.value }}
<tr class="total bg-info">
<td>
<translate>Votes cast</translate>
<td ng-if="poll.has_votes" ng-init="vote = poll.getVote(poll.votescast)">
{{ vote.value }} {{ vote.percent }}
</table>
</div>
</div>

View File

@ -168,6 +168,12 @@ li {
.result .bold {
font-weight: bold;
}
.electionresults table {
width: calc(100% - 230px);
}
.electionresults .progress {
margin-bottom: 0;
}
hr {
margin: 10px 0;
}

View File

@ -261,14 +261,19 @@ angular.module('OpenSlidesApp.core', [
// Returns true if there is a projector element with the same
// name and the same id.
var projector = Projector.get(1);
if (typeof projector === 'undefined') return false;
var self = this;
var predicate = function (element) {
return element.name == self.getResourceName() &&
typeof element.id !== 'undefined' &&
element.id == self.id;
};
return typeof _.findKey(projector.elements, predicate) === 'string';
var isProjected;
if (typeof projector !== 'undefined') {
var self = this;
var predicate = function (element) {
return element.name == self.getResourceName() &&
typeof element.id !== 'undefined' &&
element.id == self.id;
};
isProjected = typeof _.findKey(projector.elements, predicate) === 'string';
} else {
isProjected = false;
}
return isProjected;
};
return BaseModel;
}

View File

@ -30,11 +30,6 @@ class Motion(RESTModelMixin, models.Model):
This class is the main entry point to all other classes related to a motion.
"""
slide_callback_name = 'motion'
"""
Name of the callback for the slide-system.
"""
active_version = models.ForeignKey(
'MotionVersion',
on_delete=models.SET_NULL,
@ -738,9 +733,6 @@ class MotionOption(RESTModelMixin, BaseOption):
class MotionPoll(RESTModelMixin, CollectDefaultVotesMixin, BasePoll):
"""The Class to saves the vote result for a motion poll."""
slide_callback_name = 'motionpoll'
"""Name of the callback for the slide-system."""
motion = models.ForeignKey(
Motion,
on_delete=models.CASCADE,