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:
helmutm 2008-12-25 12:11:13 +00:00
parent 3cdffe1937
commit e7c3f8ac56
2 changed files with 83 additions and 13 deletions

View file

@ -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}>

View file

@ -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