work in progress: manage work items
git-svn-id: svn://svn.cy55.de/Zope3/src/cybertools/trunk@3083 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
parent
3cdffe1937
commit
e7c3f8ac56
2 changed files with 83 additions and 13 deletions
|
@ -96,7 +96,7 @@ so we do not have to set up real objects.
|
||||||
<WorkItem ['001', 1, 'john', '...', 'created']:
|
<WorkItem ['001', 1, 'john', '...', 'created']:
|
||||||
{'created': ..., 'creator': 'john'}>
|
{'created': ..., 'creator': 'john'}>
|
||||||
|
|
||||||
Properties that have not been set explicitly default to None; properties
|
Properties that have not been set explicitly have a default of None; properties
|
||||||
not specified in the IWorkItem interface will lead to an AttributeError.
|
not specified in the IWorkItem interface will lead to an AttributeError.
|
||||||
|
|
||||||
>>> wi01.description is None
|
>>> wi01.description is None
|
||||||
|
@ -108,18 +108,43 @@ not specified in the IWorkItem interface will lead to an AttributeError.
|
||||||
|
|
||||||
Certain (not all) properties may be set after creation.
|
Certain (not all) properties may be set after creation.
|
||||||
|
|
||||||
>>> wi01.setInitData(planStart=1229955772, planDuration=600, party='jim')
|
>>> wi01.setInitData(planStart=1229955772, planDuration=600, party='annie')
|
||||||
>>> wi01
|
>>> wi01
|
||||||
<WorkItem ['001', 1, 'jim', '2008-12-22 14:22', 'created']:
|
<WorkItem ['001', 1, 'annie', '2008-12-22 14:22', 'created']:
|
||||||
{'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
|
||||||
|
set via an automatic calculation like for the ``planEffort`` field.
|
||||||
|
|
||||||
|
>>> 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(party='jim')
|
||||||
|
>>> wi01.userName
|
||||||
|
'jim'
|
||||||
|
|
||||||
Change work item states
|
Change work item states
|
||||||
-----------------------
|
-----------------------
|
||||||
|
|
||||||
|
Now Jim accepts the work item, i.e. he wants to work on it. Now the party
|
||||||
|
that the work item is assigned to may not be changed any more.
|
||||||
|
|
||||||
>>> wi01.assign()
|
>>> wi01.assign()
|
||||||
>>> wi01.state
|
>>> wi01.state
|
||||||
'assigned'
|
'assigned'
|
||||||
|
>>> wi01.setInitData(party='annie')
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValueError: Attribute 'party' may not be set in state 'assigned'.
|
||||||
|
|
||||||
|
Jim now really starts to work. The start time is usually set automatically
|
||||||
|
but may also be specified explicitly.
|
||||||
|
|
||||||
>>> wi01.startWork(start=1229958000)
|
>>> wi01.startWork(start=1229958000)
|
||||||
>>> wi01
|
>>> wi01
|
||||||
|
@ -127,3 +152,18 @@ Change work item states
|
||||||
{'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': 600}>
|
||||||
|
|
||||||
|
After five minutes of work Jim decides to stop working; but he will
|
||||||
|
continue work later, so he executes a ``continue`` transition that will
|
||||||
|
set up a copy of the work item.
|
||||||
|
|
||||||
|
He also specifies a new plan start and duration for the new work item.
|
||||||
|
Plan end and plan effort are given explicitly as None values so that they
|
||||||
|
won't be taken from the old work item but recalculated.
|
||||||
|
|
||||||
|
>>> wi02 = wi01.stopWork('continue', end=1229958300, planStart=1229960000,
|
||||||
|
... planDuration=400, planEnd=None, planEffort=None)
|
||||||
|
>>> wi02
|
||||||
|
<WorkItem ['001', 1, 'jim', '2008-12-22 15:33', 'created']:
|
||||||
|
{'created': ..., 'planEnd': 1229960400, 'planDuration': 400,
|
||||||
|
'planStart': 1229960000, 'creator': 'jim', 'planEffort': 400}>
|
||||||
|
|
|
@ -35,6 +35,8 @@ from cybertools.stateful.interfaces import IStatesDefinition
|
||||||
from cybertools.tracking.btree import Track, getTimeStamp
|
from cybertools.tracking.btree import Track, getTimeStamp
|
||||||
from cybertools.tracking.interfaces import ITrackingStorage
|
from cybertools.tracking.interfaces import ITrackingStorage
|
||||||
|
|
||||||
|
_not_found = object()
|
||||||
|
|
||||||
|
|
||||||
@implementer(IStatesDefinition)
|
@implementer(IStatesDefinition)
|
||||||
def workItemStates():
|
def workItemStates():
|
||||||
|
@ -42,13 +44,16 @@ def workItemStates():
|
||||||
State('created', 'created', ('assign', 'cancel',), color='red'),
|
State('created', 'created', ('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',), color='green'),
|
State('running', 'running', ('finish', 'continue', 'cancel', 'transfer'),
|
||||||
State('finished', 'finished', (), color='blue'),
|
color='orange'),
|
||||||
State('transferred', 'transferred', (), color='grey'),
|
State('finished', 'finished', (), color='green'),
|
||||||
|
State('continued', 'continued', (), color='blue'),
|
||||||
|
State('transferred', 'transferred', (), color='lightblue'),
|
||||||
State('cancelled', 'cancelled', (), color='grey'),
|
State('cancelled', 'cancelled', (), color='grey'),
|
||||||
Transition('assign', 'assign', 'assigned'),
|
Transition('assign', 'assign', 'assigned'),
|
||||||
Transition('start', 'start', 'running'),
|
Transition('start', 'start', 'running'),
|
||||||
Transition('finish', 'finish', 'finished'),
|
Transition('finish', 'finish', 'finished'),
|
||||||
|
Transition('continue', 'continue', 'continued'),
|
||||||
Transition('transfer', 'transfer', 'transferred'),
|
Transition('transfer', 'transfer', 'transferred'),
|
||||||
Transition('cancel', 'cancel', 'cancelled'),
|
Transition('cancel', 'cancel', 'cancelled'),
|
||||||
initialState='created')
|
initialState='created')
|
||||||
|
@ -75,12 +80,13 @@ class WorkItemTrack(WorkItem, Track):
|
||||||
initAttributes = set(['party', 'description', 'predecessor',
|
initAttributes = set(['party', 'description', 'predecessor',
|
||||||
'planStart', 'planEnd', 'planDuration', 'planEffort'])
|
'planStart', 'planEnd', 'planDuration', 'planEffort'])
|
||||||
|
|
||||||
def __init__(self, taskId, runId, userName, data={}):
|
closeAttributes = set(['end', 'duration', 'effort', 'comment'])
|
||||||
super(WorkItemTrack, self).__init__(taskId, runId, userName)
|
|
||||||
|
def __init__(self, taskId, runId, userName, data):
|
||||||
|
super(WorkItemTrack, self).__init__(taskId, runId, userName, data)
|
||||||
self.state = self.getState() # make initial state persistent
|
self.state = self.getState() # make initial state persistent
|
||||||
self.data['creator'] = userName
|
self.data['creator'] = userName
|
||||||
self.data['created'] = self.timeStamp
|
self.data['created'] = self.timeStamp
|
||||||
self.setInitData(**data)
|
|
||||||
|
|
||||||
def __getattr__(self, attr):
|
def __getattr__(self, attr):
|
||||||
if attr not in IWorkItem:
|
if attr not in IWorkItem:
|
||||||
|
@ -98,7 +104,7 @@ class WorkItemTrack(WorkItem, Track):
|
||||||
party = kw.pop('party', None)
|
party = kw.pop('party', None)
|
||||||
if party is not None:
|
if party is not None:
|
||||||
if self.state != 'created':
|
if self.state != 'created':
|
||||||
raise ValueError("Attribute 'party' may not be set in the state '%s'" %
|
raise ValueError("Attribute 'party' may not be set in state '%s'." %
|
||||||
self.state)
|
self.state)
|
||||||
else:
|
else:
|
||||||
self.userName = party
|
self.userName = party
|
||||||
|
@ -136,9 +142,31 @@ class WorkItemTrack(WorkItem, Track):
|
||||||
self.reindex()
|
self.reindex()
|
||||||
|
|
||||||
def stopWork(self, transition='finish', **kw):
|
def stopWork(self, transition='finish', **kw):
|
||||||
self.checkOverwrite(kw)
|
|
||||||
self.doTransition(transition)
|
self.doTransition(transition)
|
||||||
|
data = self.data
|
||||||
|
for k in self.closeAttributes:
|
||||||
|
v = kw.pop(k, None)
|
||||||
|
if v is not None:
|
||||||
|
data[k] = v
|
||||||
|
if self.start:
|
||||||
|
data['end'], data['duration'], data['effort'] = \
|
||||||
|
recalcTimes(self.start, self.end, self.duration, self.effort)
|
||||||
|
self.timeStamp = self.end or getTimeStamp()
|
||||||
self.reindex()
|
self.reindex()
|
||||||
|
if transition in ('continue', 'transfer'):
|
||||||
|
if transition == 'continue' and kw.get('party') is not None:
|
||||||
|
raise ValueError("Setting 'party' is not allowed when continuing.")
|
||||||
|
newData = {}
|
||||||
|
for k in self.initAttributes:
|
||||||
|
v = kw.pop(k, _not_found)
|
||||||
|
if v is _not_found:
|
||||||
|
v = data.get(k)
|
||||||
|
if v is not None:
|
||||||
|
newData[k] = v
|
||||||
|
if transition == 'transfer' and 'party' not in newData:
|
||||||
|
raise ValueError("Property 'party' must be set when transferring.")
|
||||||
|
workItems = IWorkItems(getParent(self))
|
||||||
|
return workItems.add(self.taskId, self.userName, self.runId, **newData)
|
||||||
|
|
||||||
def reindex(self):
|
def reindex(self):
|
||||||
getParent(self).updateTrack(self, {}) # force reindex
|
getParent(self).updateTrack(self, {}) # force reindex
|
||||||
|
@ -175,5 +203,7 @@ class WorkItems(object):
|
||||||
return self.context[key]
|
return self.context[key]
|
||||||
|
|
||||||
def add(self, task, party, run=0, **kw):
|
def add(self, task, party, run=0, **kw):
|
||||||
trackId = self.context.saveUserTrack(task, run, party, kw)
|
trackId = self.context.saveUserTrack(task, run, party, {})
|
||||||
return self[trackId]
|
track = self[trackId]
|
||||||
|
track.setInitData(**kw)
|
||||||
|
return track
|
||||||
|
|
Loading…
Add table
Reference in a new issue