diff --git a/organize/README.txt b/organize/README.txt index ee3d0e2..036139e 100644 --- a/organize/README.txt +++ b/organize/README.txt @@ -93,7 +93,7 @@ so we do not have to set up real objects. >>> wi01 = workItems.add('001', 'john') >>> wi01 - Properties that have not been set explicitly have a default of None; properties @@ -110,20 +110,16 @@ Certain (not all) properties may be set after creation. >>> wi01.setInitData(planStart=1229955772, planDuration=600, party='annie') >>> wi01 - -It's not possible to change a value after it has been set, even if it is -set via an automatic calculation like for the ``planEffort`` field. +It's possible to change a value after it has been set as long as the work +item is in state 'new'. - >>> wi01.setInitData(planEffort=400) - Traceback (most recent call last): - ... - ValueError: Attribute 'planEffort' already set to '600'. - -There is one exception to this rule: The party may be changed as long as -the work item is not in the ``assigned`` state. + >>> wi01.setInitData(planEffort=700) + >>> wi01.planEffort + 700 >>> wi01.setInitData(party='jim') >>> wi01.userName @@ -141,7 +137,7 @@ that the work item is assigned to may not be changed any more. >>> wi01.setInitData(party='annie') Traceback (most recent call last): ... - ValueError: Attribute 'party' may not be set in state 'assigned'. + ValueError: Attribute 'party' already set to 'jim'. Jim now really starts to work. The start time is usually set automatically but may also be specified explicitly. @@ -151,7 +147,7 @@ but may also be specified explicitly. + 'creator': 'john', 'planEffort': 700}> Stopping work ------------- diff --git a/organize/interfaces.py b/organize/interfaces.py index 88c09dd..3dd6472 100644 --- a/organize/interfaces.py +++ b/organize/interfaces.py @@ -476,6 +476,17 @@ class IWorkItem(Interface): as keyword arguments. """ + def doAction(action, **kw): + """ Execute an action. + + Actions are usually a sequence of transitions together with + setting some properties depending on the action and + the starting state. + + Available actions are: plan, delegate, accept, start, finish, + cancel, continue, transfer. + """ + class IWorkItems(Interface): """ A collection (manager, container) of work items. diff --git a/organize/work.py b/organize/work.py index c89a88c..4ead40d 100644 --- a/organize/work.py +++ b/organize/work.py @@ -41,7 +41,7 @@ _not_found = object() @implementer(IStatesDefinition) def workItemStates(): return StatesDefinition('workItemStates', - State('created', 'created', ('assign', 'cancel',), color='red'), + State('new', 'new', ('assign', 'cancel',), color='red'), State('assigned', 'assigned', ('start', 'finish', 'cancel', 'transfer'), color='yellow'), State('running', 'running', ('finish', 'continue', 'cancel', 'transfer'), @@ -57,7 +57,7 @@ def workItemStates(): Transition('continue', 'continue', 'continued'), Transition('transfer', 'transfer', 'transferred'), Transition('cancel', 'cancel', 'cancelled'), - initialState='created') + initialState='new') class WorkItem(Stateful): @@ -99,20 +99,16 @@ class WorkItemTrack(WorkItem, Track): return self.userName def setInitData(self, **kw): + indexChanged = False + updatePlanData = False for k in kw: if k not in self.initAttributes: raise ValueError("Illegal initial attribute: '%s'." % k) + self.checkOverwrite(kw) party = kw.pop('party', None) if party is not None: - if self.state != 'created': - raise ValueError("Attribute 'party' may not be set in state '%s'." % - self.state) - else: - self.userName = party - indexChanged = True - self.checkOverwrite(kw) - updatePlanData = False - indexChanged = False + self.userName = party + indexChanged = True data = self.data for k, v in kw.items(): data[k] = v @@ -174,10 +170,36 @@ class WorkItemTrack(WorkItem, Track): self.data['successor'] = getName(new) return new + # actions + + def doAction(self, action, **kw): + # TODO: check if action is allowed + m = getattr(self, 'action_' + action) + m(**kw) + + def action_start(self, **kw): + if self.state == 'new': + self.assign(kw.pop('party', None)) + self.startWork(**kw) + + def action_finish(self, **kw): + if self.state == 'new': + self.assign(kw.pop('party', None)) + self.stopWork(**kw) + #pred = self.predecessor # better to finish predecessors manually? + #while pred is not None: + # wi = getParent(self)[pred] + # wi.doTransition('finish') + # pred = wi.pred + + # auxiliary methods + def reindex(self): getParent(self).updateTrack(self, {}) # force reindex def checkOverwrite(self, kw): + if self.state == 'new': + return for k, v in kw.items(): old = getattr(self, k, None) if old is not None and old != v: