work in progress: task management with work items

git-svn-id: svn://svn.cy55.de/Zope3/src/cybertools/trunk@3092 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
helmutm 2008-12-28 14:26:12 +00:00
parent 5344a8bb85
commit 7db5c8df68
3 changed files with 53 additions and 24 deletions

View file

@ -93,7 +93,7 @@ so we do not have to set up real objects.
>>> wi01 = workItems.add('001', 'john') >>> wi01 = workItems.add('001', 'john')
>>> wi01 >>> wi01
<WorkItem ['001', 1, 'john', '...', 'created']: <WorkItem ['001', 1, 'john', '...', 'new']:
{'created': ..., 'creator': 'john'}> {'created': ..., 'creator': 'john'}>
Properties that have not been set explicitly have a default of None; properties 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.setInitData(planStart=1229955772, planDuration=600, party='annie')
>>> wi01 >>> wi01
<WorkItem ['001', 1, 'annie', '2008-12-22 14:22', 'created']: <WorkItem ['001', 1, 'annie', '2008-12-22 14:22', 'new']:
{'created': ..., 'planEnd': 1229956372, 'planDuration': 600, {'created': ..., 'planEnd': 1229956372, 'planDuration': 600,
'planStart': 1229955772, 'creator': 'john', 'planEffort': 600}> 'planStart': 1229955772, 'creator': 'john', 'planEffort': 600}>
It's not possible to change a value after it has been set, even if it is It's possible to change a value after it has been set as long as the work
set via an automatic calculation like for the ``planEffort`` field. item is in state 'new'.
>>> wi01.setInitData(planEffort=400) >>> wi01.setInitData(planEffort=700)
Traceback (most recent call last): >>> wi01.planEffort
... 700
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(party='jim') >>> wi01.setInitData(party='jim')
>>> wi01.userName >>> wi01.userName
@ -141,7 +137,7 @@ that the work item is assigned to may not be changed any more.
>>> wi01.setInitData(party='annie') >>> wi01.setInitData(party='annie')
Traceback (most recent call last): 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 Jim now really starts to work. The start time is usually set automatically
but may also be specified explicitly. but may also be specified explicitly.
@ -151,7 +147,7 @@ but may also be specified explicitly.
<WorkItem ['001', 1, 'jim', '2008-12-22 15:00', 'running']: <WorkItem ['001', 1, 'jim', '2008-12-22 15:00', 'running']:
{'created': ..., 'planEnd': 1229956372, 'start': 1229958000, {'created': ..., 'planEnd': 1229956372, 'start': 1229958000,
'assigned': ..., 'planDuration': 600, 'planStart': 1229955772, 'assigned': ..., 'planDuration': 600, 'planStart': 1229955772,
'creator': 'john', 'planEffort': 600}> 'creator': 'john', 'planEffort': 700}>
Stopping work Stopping work
------------- -------------

View file

@ -476,6 +476,17 @@ class IWorkItem(Interface):
as keyword arguments. 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): class IWorkItems(Interface):
""" A collection (manager, container) of work items. """ A collection (manager, container) of work items.

View file

@ -41,7 +41,7 @@ _not_found = object()
@implementer(IStatesDefinition) @implementer(IStatesDefinition)
def workItemStates(): def workItemStates():
return StatesDefinition('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'), State('assigned', 'assigned', ('start', 'finish', 'cancel', 'transfer'),
color='yellow'), color='yellow'),
State('running', 'running', ('finish', 'continue', 'cancel', 'transfer'), State('running', 'running', ('finish', 'continue', 'cancel', 'transfer'),
@ -57,7 +57,7 @@ def workItemStates():
Transition('continue', 'continue', 'continued'), Transition('continue', 'continue', 'continued'),
Transition('transfer', 'transfer', 'transferred'), Transition('transfer', 'transfer', 'transferred'),
Transition('cancel', 'cancel', 'cancelled'), Transition('cancel', 'cancel', 'cancelled'),
initialState='created') initialState='new')
class WorkItem(Stateful): class WorkItem(Stateful):
@ -99,20 +99,16 @@ class WorkItemTrack(WorkItem, Track):
return self.userName return self.userName
def setInitData(self, **kw): def setInitData(self, **kw):
indexChanged = False
updatePlanData = False
for k in kw: for k in kw:
if k not in self.initAttributes: if k not in self.initAttributes:
raise ValueError("Illegal initial attribute: '%s'." % k) raise ValueError("Illegal initial attribute: '%s'." % k)
self.checkOverwrite(kw)
party = kw.pop('party', None) party = kw.pop('party', None)
if party is not None: if party is not None:
if self.state != 'created': self.userName = party
raise ValueError("Attribute 'party' may not be set in state '%s'." % indexChanged = True
self.state)
else:
self.userName = party
indexChanged = True
self.checkOverwrite(kw)
updatePlanData = False
indexChanged = False
data = self.data data = self.data
for k, v in kw.items(): for k, v in kw.items():
data[k] = v data[k] = v
@ -174,10 +170,36 @@ class WorkItemTrack(WorkItem, Track):
self.data['successor'] = getName(new) self.data['successor'] = getName(new)
return 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): def reindex(self):
getParent(self).updateTrack(self, {}) # force reindex getParent(self).updateTrack(self, {}) # force reindex
def checkOverwrite(self, kw): def checkOverwrite(self, kw):
if self.state == 'new':
return
for k, v in kw.items(): for k, v in kw.items():
old = getattr(self, k, None) old = getattr(self, k, None)
if old is not None and old != v: if old is not None and old != v: