work in progress: new work item lifecycle

git-svn-id: svn://svn.cy55.de/Zope3/src/cybertools/trunk@3136 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
helmutm 2009-01-11 14:56:12 +00:00
parent cf93ec49d7
commit 2e3fd4f333
2 changed files with 113 additions and 32 deletions

View file

@ -63,6 +63,7 @@ def workItemStates():
('plan', 'accept', 'start', 'stop', 'modify', 'close'), ('plan', 'accept', 'start', 'stop', 'modify', 'close'),
color='grey'), color='grey'),
State('closed', 'closed', (), color='lightblue'), State('closed', 'closed', (), color='lightblue'),
State('replaced', 'replaced', (), color='grey'),
Transition('plan', 'plan', 'planned'), Transition('plan', 'plan', 'planned'),
Transition('accept', 'accept', 'accepted'), Transition('accept', 'accept', 'accepted'),
Transition('start', 'start', 'running'), Transition('start', 'start', 'running'),
@ -127,9 +128,12 @@ class WorkItem(Stateful, Track):
raise AttributeError(attr) raise AttributeError(attr)
return self.data.get(attr) return self.data.get(attr)
def doAction(self, action, **kw): def doAction(self, action, userName, **kw):
currentWorkItems = list(getParent(self).query(runId=self.runId))
if self != currentWorkItems[-1]:
raise ValueError("Actions are only allowed on the last item of a run.")
if action in self.specialActions: if action in self.specialActions:
return self.specialActions[action](self, **kw) return self.specialActions[action](self, userName, **kw)
if action not in [t.name for t in self.getAvailableTransitions()]: if action not in [t.name for t in self.getAvailableTransitions()]:
raise ValueError("Action '%s' not allowed in state '%s'" % raise ValueError("Action '%s' not allowed in state '%s'" %
(action, self.state)) (action, self.state))
@ -138,22 +142,36 @@ class WorkItem(Stateful, Track):
self.doTransition(action) self.doTransition(action)
self.reindex('state') self.reindex('state')
return self return self
new = self.createNew(action, **kw) new = self.createNew(action, userName, **kw)
if self.state == 'running':
new.replace(self)
new.doTransition(action) new.doTransition(action)
new.reindex('state') new.reindex('state')
return new return new
def modify(self, **kw): def modify(self, userName, **kw):
print '*** modifying'
if self.state == 'new': if self.state == 'new':
self.setData(**kw) self.setData(**kw)
return self return self
new = self.createNew('modify', userName, **kw)
new.replace(self, keepState=True)
return new
def delegate(self, **kw): def delegate(self, userName, **kw):
print '*** delegating' new = self.createNew('delegate', userName, **kw)
new.doTransition('plan')
new.reindex('state')
if self.state == 'new':
self.doTransition('plan')
self.reindex('state')
return new
def close(self, **kw): def close(self, userName, **kw):
print '*** closing' new = self.createNew('close', userName, copyData=False, **kw)
new.state = 'closed'
new.reindex('state')
getParent(self).stopRun(runId=self.runId, finish=True)
return new
specialActions = dict(modify=modify, delegate=delegate, close=close) specialActions = dict(modify=modify, delegate=delegate, close=close)
@ -172,8 +190,9 @@ class WorkItem(Stateful, Track):
for k, v in kw.items(): for k, v in kw.items():
data[k] = v data[k] = v
def createNew(self, action, **kw): def createNew(self, action, userName, copyData=True, **kw):
newData = {} newData = {}
if copyData:
for k in self.initAttributes: for k in self.initAttributes:
v = kw.get(k, _not_found) v = kw.get(k, _not_found)
if v is _not_found: if v is _not_found:
@ -185,9 +204,16 @@ class WorkItem(Stateful, Track):
if v is not None: if v is not None:
newData[k] = v newData[k] = v
workItems = IWorkItems(getParent(self)) workItems = IWorkItems(getParent(self))
new = workItems.add(self.taskId, self.userName, self.runId, **newData) new = workItems.add(self.taskId, userName, self.runId, **newData)
return new return new
def replace(self, other, keepState=False):
if keepState:
self.state = other.state
self.reindex('state')
other.state = 'replaced'
other.reindex('state')
def reindex(self, idx=None): def reindex(self, idx=None):
getParent(self).indexTrack(None, self, idx) getParent(self).indexTrack(None, self, idx)
@ -217,10 +243,10 @@ class WorkItems(object):
criteria['runId'] = criteria.pop('run') criteria['runId'] = criteria.pop('run')
return self.context.query(**criteria) return self.context.query(**criteria)
def add(self, task, party, run=0, **kw): def add(self, task, userName, run=0, **kw):
if not run: if not run:
run = self.context.startRun() run = self.context.startRun()
trackId = self.context.saveUserTrack(task, run, party, {}) trackId = self.context.saveUserTrack(task, run, userName, {})
track = self[trackId] track = self[trackId]
track.setData(**kw) track.setData(**kw)
return track return track

View file

@ -28,7 +28,7 @@ definition as a utility.
We are now ready to set up the tracking storage. We are now ready to set up the tracking storage.
>>> tracks = TrackingStorage(trackFactory=WorkItem) >>> tracks = TrackingStorage(trackFactory=WorkItem)
>>> workItems = component.getAdapter(tracks, IWorkItems) >>> workItems = IWorkItems(tracks)
The work management only deals with the IDs or names of tasks and persons, The work management only deals with the IDs or names of tasks and persons,
so we do not have to set up real objects. so we do not have to set up real objects.
@ -68,7 +68,7 @@ the effort is taken from the duration if not set explicitly.
>>> wi01.effort >>> wi01.effort
700 700
>>> w = wi01.doAction('plan', party='jim') >>> w = wi01.doAction('plan', 'john', party='jim')
>>> wi01.userName >>> wi01.userName
'jim' 'jim'
@ -77,7 +77,7 @@ Change work item state
Now Jim accepts the work item, i.e. he wants to work on it. Now Jim accepts the work item, i.e. he wants to work on it.
>>> wi02 = wi01.doAction('accept') >>> wi02 = wi01.doAction('accept', 'jim')
>>> wi01.state >>> wi01.state
'planned' 'planned'
>>> wi02 >>> wi02
@ -95,7 +95,7 @@ It is not possible to change a value of a work item that is not in state "new".
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.
>>> wi03 = wi02.doAction('start', start=1229958000) >>> wi03 = wi02.doAction('start', 'jim', start=1229958000)
>>> wi03 >>> wi03
<WorkItem ['001', 1, 'jim', '2008-12-22 15:00', 'running']: <WorkItem ['001', 1, 'jim', '2008-12-22 15:00', 'running']:
{'duration': 700, 'start': 1229958000, 'created': ..., 'creator': 'jim'}> {'duration': 700, 'start': 1229958000, 'created': ..., 'creator': 'jim'}>
@ -104,9 +104,14 @@ Stopping and finishing work
--------------------------- ---------------------------
After five minutes of work Jim decides to stop working; but he will After five minutes of work Jim decides to stop working; but he will
continue work later, so he executes a ``stop`` action. continue work later, so he executes a ``stop`` action. The work item marked
as "running" will be replaced by a new one.
>>> wi04 = wi03.doAction('stop', end=1229958300) >>> wi04 = wi03.doAction('stop', 'jim', end=1229958300)
>>> wi03
<WorkItem ['001', 1, 'jim', '2008-12-22 15:00', 'replaced']:
{'duration': 700, 'start': 1229958000, 'created': ..., 'creator': 'jim'}>
>>> wi04 >>> wi04
<WorkItem ['001', 1, 'jim', '2008-12-22 15:00', 'stopped']: <WorkItem ['001', 1, 'jim', '2008-12-22 15:00', 'stopped']:
{'start': 1229958000, 'created': ..., 'end': 1229958300, 'creator': 'jim'}> {'start': 1229958000, 'created': ..., 'end': 1229958300, 'creator': 'jim'}>
@ -114,28 +119,78 @@ continue work later, so he executes a ``stop`` action.
After another hour Jim works again on the task; he now finishes it within After another hour Jim works again on the task; he now finishes it within
ten minutes and records this in one step. ten minutes and records this in one step.
>>> wi05 = wi04.doAction('finish', start=1229961600, end=1229967200) >>> wi05 = wi04.doAction('finish', 'jim', start=1229961600, end=1229962200)
>>> wi05 >>> wi05
<WorkItem ['001', 1, 'jim', '2008-12-22 16:00', 'finished']: <WorkItem ['001', 1, 'jim', '2008-12-22 16:00', 'finished']:
{'start': 1229961600, 'created': ..., 'end': 1229967200, 'creator': 'jim'}> {'start': 1229961600, 'created': ..., 'end': 1229962200, 'creator': 'jim'}>
>>> wi05.duration, wi05.effort
(600, 600)
Closing work Closing work
------------ ------------
As the work is now finished, the work item may be closed; the corresponding
"run" (a sequence of work items belonging together) will be finished.
>>> wi06 = wi05.doAction('close', 'john')
>>> wi06
<WorkItem ['001', 1, 'john', '... ...', 'closed']:
{'created': ..., 'creator': 'john'}>
Let's now check how many work items have been generated. Let's now check how many work items have been generated.
>>> len(list(workItems)) >>> len(list(workItems))
5 6
Delegation of Work Items Delegation of Work Items
======================== ========================
A user may delegate a newly created work item to another party. This
will create a new work item even if the initial one is still in state "new".
Both work items are now in "planned" state.
>>> wi07 = workItems.add('001', 'john', start=1229970800)
>>> wi08 = wi07.doAction('delegate', 'john', party='annie')
>>> wi07
<WorkItem ['001', 2, 'john', '2008-12-22 18:33', 'planned']:
{'start': 1229970800, 'created': ..., 'creator': 'john'}>
>>> wi08
<WorkItem ['001', 2, 'annie', '2008-12-22 18:33', 'planned']:
{'start': 1229970800, 'created': ..., 'creator': 'john'}>
>>> len(list(workItems))
8
Modification of Work Items Modification of Work Items
========================== ==========================
Existing work items may never be modified except when still in "new" state.
To allow for correcting errors that may be detected later there is a "modify"
action that will in fact create a new work item. This makes sure that
all changes will be tracked correctly.
Note that nevertheless only the last work item of a run may be modified.
>>> wi09 = wi08.doAction('modify', 'annie', duration=3600)
>>> wi08
<WorkItem ['001', 2, 'annie', '2008-12-22 18:33', 'replaced']:
{'start': 1229970800, 'created': ..., 'creator': 'john'}>
>>> wi09
<WorkItem ['001', 2, 'annie', '2008-12-22 18:33', 'planned']:
{'duration': 3600, 'start': 1229970800, 'created': ..., 'creator': 'annie'}>
Queries Queries
======= =======
Runs
----
>>> list(tracks.runs)
[2]
>>> list(tracks.finishedRuns)
[1]