Fixed motion states, handled workflow field.

This commit is contained in:
Oskar Hahn 2015-11-03 10:03:44 +01:00 committed by Emanuel Schuetze
parent 5b37a21c87
commit c379544e97
8 changed files with 97 additions and 44 deletions

View File

@ -79,7 +79,7 @@ angular.module('OpenSlidesApp.assignments.site', ['OpenSlidesApp.assignments'])
.controller('AssignmentDetailCtrl', function($scope, Assignment, assignment) { .controller('AssignmentDetailCtrl', function($scope, Assignment, assignment) {
Assignment.bindOne(assignment.id, $scope, 'assignment'); Assignment.bindOne(assignment.id, $scope, 'assignment');
Assignment.loadRelations(assignment); Assignment.loadRelations(assignment, 'agenda_item');
}) })
.controller('AssignmentCreateCtrl', function($scope, $state, Assignment) { .controller('AssignmentCreateCtrl', function($scope, $state, Assignment) {

View File

@ -111,7 +111,7 @@ angular.module('OpenSlidesApp.core', [
// Load the global data on startup // Load the global data on startup
.run([ .run([
'loadGlobalData', 'loadGlobalData',
function(loadGlobalData, operator) { function(loadGlobalData) {
loadGlobalData(); loadGlobalData();
} }
]) ])

View File

@ -646,7 +646,7 @@ angular.module('OpenSlidesApp.core.site', [
.controller('CustomslideDetailCtrl', function($scope, Customslide, customslide) { .controller('CustomslideDetailCtrl', function($scope, Customslide, customslide) {
Customslide.bindOne(customslide.id, $scope, 'customslide'); Customslide.bindOne(customslide.id, $scope, 'customslide');
Customslide.loadRelations(customslide); Customslide.loadRelations(customslide, 'agenda_item');
}) })
.controller('CustomslideCreateCtrl', function($scope, $state, Customslide) { .controller('CustomslideCreateCtrl', function($scope, $state, Customslide) {

View File

@ -409,6 +409,7 @@ class Motion(RESTModelMixin, models.Model):
""" """
Returns the id of the workflow of the motion. Returns the id of the workflow of the motion.
""" """
# TODO: Rename to workflow_id
return self.state.workflow.pk return self.state.workflow.pk
def set_state(self, state): def set_state(self, state):
@ -442,7 +443,7 @@ class Motion(RESTModelMixin, models.Model):
new_state = self.state.workflow.first_state new_state = self.state.workflow.first_state
else: else:
new_state = (Workflow.objects.get(pk=config['motions_workflow']).first_state or new_state = (Workflow.objects.get(pk=config['motions_workflow']).first_state or
Workflow.objects.get(pk=config['motions_workflow']).state_set.all()[0]) Workflow.objects.get(pk=config['motions_workflow']).states.all()[0])
self.set_state(new_state) self.set_state(new_state)
def get_agenda_title(self): def get_agenda_title(self):
@ -729,7 +730,7 @@ class State(RESTModelMixin, models.Model):
action_word = models.CharField(max_length=255) action_word = models.CharField(max_length=255)
"""An alternative string to be used for a button to switch to this state.""" """An alternative string to be used for a button to switch to this state."""
workflow = models.ForeignKey('Workflow') workflow = models.ForeignKey('Workflow', related_name='states')
"""A many-to-one relation to a workflow.""" """A many-to-one relation to a workflow."""
next_states = models.ManyToManyField('self', symmetrical=False) next_states = models.ManyToManyField('self', symmetrical=False)

View File

@ -1,7 +1,6 @@
from django.db import transaction from django.db import transaction
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from openslides.core.config import config
from openslides.utils.rest_api import ( from openslides.utils.rest_api import (
CharField, CharField,
DictField, DictField,
@ -58,19 +57,20 @@ class StateSerializer(ModelSerializer):
'versioning', 'versioning',
'leave_old_version_active', 'leave_old_version_active',
'dont_set_identifier', 'dont_set_identifier',
'next_states',) 'next_states',
'workflow')
class WorkflowSerializer(ModelSerializer): class WorkflowSerializer(ModelSerializer):
""" """
Serializer for motion.models.Workflow objects. Serializer for motion.models.Workflow objects.
""" """
state_set = StateSerializer(many=True, read_only=True) states = StateSerializer(many=True, read_only=True)
first_state = PrimaryKeyRelatedField(read_only=True) first_state = PrimaryKeyRelatedField(read_only=True)
class Meta: class Meta:
model = Workflow model = Workflow
fields = ('id', 'name', 'state_set', 'first_state',) fields = ('id', 'name', 'states', 'first_state',)
class MotionLogSerializer(ModelSerializer): class MotionLogSerializer(ModelSerializer):
@ -180,11 +180,14 @@ class MotionSerializer(ModelSerializer):
log_messages = MotionLogSerializer(many=True, read_only=True) log_messages = MotionLogSerializer(many=True, read_only=True)
polls = MotionPollSerializer(many=True, read_only=True) polls = MotionPollSerializer(many=True, read_only=True)
reason = CharField(allow_blank=True, required=False, write_only=True) reason = CharField(allow_blank=True, required=False, write_only=True)
state = StateSerializer(read_only=True)
text = CharField(write_only=True) text = CharField(write_only=True)
title = CharField(max_length=255, write_only=True) title = CharField(max_length=255, write_only=True)
versions = MotionVersionSerializer(many=True, read_only=True) versions = MotionVersionSerializer(many=True, read_only=True)
workflow = IntegerField(min_value=1, required=False, validators=[validate_workflow_field]) workflow_id = IntegerField(
min_value=1,
required=False,
validators=[validate_workflow_field],
write_only=True)
class Meta: class Meta:
model = Motion model = Motion
@ -201,13 +204,13 @@ class MotionSerializer(ModelSerializer):
'submitters', 'submitters',
'supporters', 'supporters',
'state', 'state',
'workflow', 'workflow_id',
'tags', 'tags',
'attachments', 'attachments',
'polls', 'polls',
'agenda_item_id', 'agenda_item_id',
'log_messages',) 'log_messages',)
read_only_fields = ('parent',) # Some other fields are also read_only. See definitions above. read_only_fields = ('parent', 'state') # Some other fields are also read_only. See definitions above.
@transaction.atomic @transaction.atomic
def create(self, validated_data): def create(self, validated_data):
@ -220,7 +223,7 @@ class MotionSerializer(ModelSerializer):
motion.reason = validated_data.get('reason', '') motion.reason = validated_data.get('reason', '')
motion.identifier = validated_data.get('identifier') motion.identifier = validated_data.get('identifier')
motion.category = validated_data.get('category') motion.category = validated_data.get('category')
motion.reset_state(validated_data.get('workflow', int(config['motions_workflow']))) motion.reset_state(validated_data.get('workflow_id'))
motion.save() motion.save()
if validated_data.get('submitters'): if validated_data.get('submitters'):
motion.submitters.add(*validated_data['submitters']) motion.submitters.add(*validated_data['submitters'])
@ -242,9 +245,9 @@ class MotionSerializer(ModelSerializer):
setattr(motion, key, validated_data[key]) setattr(motion, key, validated_data[key])
# Workflow. # Workflow.
workflow = validated_data.get('workflow') workflow_id = validated_data.get('workflow_id')
if workflow is not None and workflow != motion.workflow: if workflow_id is not None and workflow_id != motion.workflow:
motion.reset_state(workflow) motion.reset_state(workflow_id)
# Decide if a new version is saved to the database. # Decide if a new version is saved to the database.
if (motion.state.versioning and if (motion.state.versioning and

View File

@ -2,14 +2,58 @@
angular.module('OpenSlidesApp.motions', []) angular.module('OpenSlidesApp.motions', [])
.factory('WorkflowState', [
'DS',
function (DS) {
return DS.defineResource({
name: 'motions/workflowstate',
methods: {
getNextStates: function () {
var states = [];
_.forEach(this.next_states_id, function (stateId) {
states.push(DS.get('motions/workflowstate', stateId));
})
return states;
}
}
})
}
])
.factory('Workflow', [
'DS',
'jsDataModel',
'WorkflowState',
function (DS, jsDataModel, WorkflowState) {
return DS.defineResource({
name: 'motions/workflow',
useClass: jsDataModel,
relations: {
hasMany: {
'motions/workflowstate': {
localField: 'states',
foreignKey: 'workflow_id',
}
}
}
})
}
])
// Load all MotionWorkflows at stateup
.run([
'Workflow',
function (Workflow) {
Workflow.findAll();
}
])
.factory('MotionPoll', [ .factory('MotionPoll', [
'DS', 'DS',
'Config', 'Config',
'jsDataModel', function (DS, Config) {
function(DS, Config, jsDataModel) {
return DS.defineResource({ return DS.defineResource({
name: 'motions/motionpoll', name: 'motions/poll',
useClass: jsDataModel,
relations: { relations: {
belongsTo: { belongsTo: {
'motions/motion': { 'motions/motion': {
@ -149,10 +193,16 @@ angular.module('OpenSlidesApp.motions', [])
localKeys: 'supporters_id', localKeys: 'supporters_id',
} }
], ],
'motions/motionpoll': { 'motions/poll': {
localField: 'polls', localField: 'polls',
foreignKey: 'motion_id', foreignKey: 'motion_id',
} }
},
hasOne: {
'motions/workflowstate': {
localField: 'state',
localKey: 'state_id',
}
} }
} }
}); });
@ -165,13 +215,7 @@ angular.module('OpenSlidesApp.motions', [])
}); });
}]) }])
.factory('Workflow', ['DS', function(DS) { .run(['Motion', 'Category', function(Motion, Category) {}]);
return DS.defineResource({
name: 'motions/workflow',
});
}])
.run(['Motion', 'Category', 'Workflow', function(Motion, Category, Workflow) {}]);
angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions']) angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions'])
@ -221,9 +265,6 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions'])
categories: function(Category) { categories: function(Category) {
return Category.findAll(); return Category.findAll();
}, },
workflows: function(Workflow) {
return Workflow.findAll();
},
tags: function(Tag) { tags: function(Tag) {
return Tag.findAll(); return Tag.findAll();
}, },
@ -262,9 +303,6 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions'])
categories: function(Category) { categories: function(Category) {
return Category.findAll(); return Category.findAll();
}, },
workflows: function(Workflow) {
return Workflow.findAll();
},
tags: function(Tag) { tags: function(Tag) {
return Tag.findAll(); return Tag.findAll();
}, },
@ -405,7 +443,9 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions'])
Category.bindAll({}, $scope, 'categories'); Category.bindAll({}, $scope, 'categories');
Workflow.bindAll({}, $scope, 'workflows'); Workflow.bindAll({}, $scope, 'workflows');
User.bindAll({}, $scope, 'users'); User.bindAll({}, $scope, 'users');
Motion.loadRelations(motion); Motion.loadRelations(motion, 'agenda_item');
var state = motion.state;
state.getNextStates()
$scope.alert = {}; // TODO: show alert in template $scope.alert = {}; // TODO: show alert in template
$scope.update_state = function (state_id) { $scope.update_state = function (state_id) {

View File

@ -6,7 +6,11 @@
</small> </small>
</h1> </h1>
{{ motion.agenda_item }} agenda_id: {{ motion.agenda_item }}
<br><br>state: {{ motion.state }}
<br><br>next states: {{ motion.state.getNextStates() }}
<div id="submenu"> <div id="submenu">
<a ui-sref="motions.motion.list" class="btn btn-sm btn-default"> <a ui-sref="motions.motion.list" class="btn btn-sm btn-default">

View File

@ -108,15 +108,17 @@ class CreateMotion(TestCase):
self.assertEqual(motion.tags.get().name, 'test_tag_iRee3kiecoos4rorohth') self.assertEqual(motion.tags.get().name, 'test_tag_iRee3kiecoos4rorohth')
def test_with_workflow(self): def test_with_workflow(self):
self.assertEqual(config['motions_workflow'], '1') """
Test to create a motion with a specific workflow.
"""
response = self.client.post( response = self.client.post(
reverse('motion-list'), reverse('motion-list'),
{'title': 'test_title_eemuR5hoo4ru2ahgh5EJ', {'title': 'test_title_eemuR5hoo4ru2ahgh5EJ',
'text': 'test_text_ohviePopahPhoili7yee', 'text': 'test_text_ohviePopahPhoili7yee',
'workflow': '2'}) 'workflow_id': '2'})
self.assertEqual(response.status_code, status.HTTP_201_CREATED) self.assertEqual(response.status_code, status.HTTP_201_CREATED)
motion = Motion.objects.get() self.assertEqual(Motion.objects.get().state.workflow_id, 2)
self.assertEqual(motion.state.workflow.pk, 2)
class UpdateMotion(TestCase): class UpdateMotion(TestCase):
@ -141,12 +143,15 @@ class UpdateMotion(TestCase):
self.assertEqual(motion.identifier, 'test_identifier_jieseghohj7OoSah1Ko9') self.assertEqual(motion.identifier, 'test_identifier_jieseghohj7OoSah1Ko9')
def test_patch_workflow(self): def test_patch_workflow(self):
self.assertEqual(config['motions_workflow'], '1') """
Tests to only update the workflow of a motion.
"""
response = self.client.patch( response = self.client.patch(
reverse('motion-detail', args=[self.motion.pk]), reverse('motion-detail', args=[self.motion.pk]),
{'workflow': '2'}) {'workflow_id': '2'})
self.assertEqual(response.status_code, status.HTTP_200_OK)
motion = Motion.objects.get() motion = Motion.objects.get()
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(motion.title, 'test_title_aeng7ahChie3waiR8xoh') self.assertEqual(motion.title, 'test_title_aeng7ahChie3waiR8xoh')
self.assertEqual(motion.workflow, 2) self.assertEqual(motion.workflow, 2)