229 lines
7 KiB
Text
229 lines
7 KiB
Text
==========================================================
|
|
Organizations: Persons, Institutions, Addresses, Work, ...
|
|
==========================================================
|
|
|
|
($Id$)
|
|
|
|
>>> from zope import component
|
|
|
|
|
|
Basic Work Item Lifecycle
|
|
=========================
|
|
|
|
Work items are stored in a tracking storage; in order to conveniently access
|
|
the work items we have to provide an adapter to the tracking storage.
|
|
|
|
>>> from cybertools.tracking.btree import TrackingStorage
|
|
>>> from cybertools.organize.interfaces import IWorkItems
|
|
>>> from cybertools.organize.work import WorkItem, WorkItems
|
|
>>> component.provideAdapter(WorkItems)
|
|
|
|
The individual work item (a track) is carrying a state attribute that is
|
|
governed by a special states definition. We have to register this states
|
|
definition as a utility.
|
|
|
|
>>> from cybertools.organize.work import workItemStates
|
|
>>> component.provideUtility(workItemStates(), name='organize.workItemStates')
|
|
|
|
We are now ready to set up the tracking storage.
|
|
|
|
>>> tracks = TrackingStorage(trackFactory=WorkItem)
|
|
>>> workItems = IWorkItems(tracks)
|
|
|
|
The work management only deals with the IDs or names of tasks and persons,
|
|
so we do not have to set up real objects.
|
|
|
|
>>> wi01 = workItems.add('001', 'john')
|
|
>>> wi01
|
|
<WorkItem ['001', 1, 'john', '...', 'new']:
|
|
{'created': ..., 'creator': 'john'}>
|
|
|
|
Properties that have not been set explicitly have a default of None; properties
|
|
not specified in the IWorkItem interface will lead to an AttributeError.
|
|
|
|
>>> wi01.description is None
|
|
True
|
|
>>> wi01.something
|
|
Traceback (most recent call last):
|
|
...
|
|
AttributeError: something
|
|
|
|
Properties may be set as long as the work item is in status "new".
|
|
|
|
>>> wi01.setData(start=1229955772, party='annie')
|
|
>>> wi01
|
|
<WorkItem ['001', 1, 'annie', '2008-12-22 15:22', 'new']:
|
|
{'start': 1229955772, 'created': ..., 'creator': 'john'}>
|
|
|
|
The duration value is calculated automatically when start and end are set;
|
|
the effort is taken from the duration if not set explicitly.
|
|
|
|
>>> (wi01.end, wi01.duration, wi01.effort)
|
|
(None, None, None)
|
|
>>> wi01.setData(end=1229956372)
|
|
>>> (wi01.end, wi01.duration, wi01.effort)
|
|
(1229956372, 600, 600)
|
|
|
|
>>> wi01.setData(duration=700)
|
|
>>> wi01.effort
|
|
700
|
|
|
|
>>> w = wi01.doAction('plan', 'john', party='jim')
|
|
>>> wi01.userName
|
|
'jim'
|
|
|
|
Change work item state
|
|
----------------------
|
|
|
|
Now Jim accepts the work item, i.e. he wants to work on it. The previous
|
|
record keeps its state but it is marked by a "_x" suffix so that it may
|
|
be excluded from queries.
|
|
|
|
>>> wi02 = wi01.doAction('accept', 'jim')
|
|
>>> wi01.state
|
|
'planned_x'
|
|
>>> wi02
|
|
<WorkItem ['001', 1, 'jim', '2008-12-22 15:22', 'accepted']:
|
|
{'duration': 700, 'start': 1229955772, 'created': ...,
|
|
'end': 1229956372, 'creator': 'jim'}>
|
|
|
|
It is not possible to change a value of a work item that is not in state "new".
|
|
|
|
>>> wi01.setData(party='annie')
|
|
Traceback (most recent call last):
|
|
...
|
|
ValueError: Attributes may only be changed in state 'new'.
|
|
|
|
Jim now really starts to work. The start time is usually set automatically
|
|
but may also be specified explicitly.
|
|
|
|
>>> wi03 = wi02.doAction('start', 'jim', start=1229958000)
|
|
>>> wi03
|
|
<WorkItem ['001', 1, 'jim', '2008-12-22 16:00', 'running']:
|
|
{'duration': 0, 'start': 1229958000, 'created': ..., 'creator': 'jim'}>
|
|
|
|
Stopping and finishing work
|
|
---------------------------
|
|
|
|
After five minutes of work Jim decides to stop working; but he will
|
|
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('work', 'jim', end=1229958300)
|
|
|
|
>>> wi03
|
|
<WorkItem ['001', 1, 'jim', '2008-12-22 16:00', 'replaced']:
|
|
{'duration': 0, 'start': 1229958000, 'created': ..., 'creator': 'jim'}>
|
|
>>> wi04
|
|
<WorkItem ['001', 1, 'jim', '2008-12-22 16:00', 'done']:
|
|
{'start': 1229958000, 'created': ..., 'end': 1229958300, 'creator': 'jim'}>
|
|
|
|
After another hour Jim works again on the task; he now finishes it within
|
|
ten minutes and records this in one step.
|
|
|
|
>>> wi05 = wi04.doAction('finish', 'jim', start=1229961600, end=1229962200)
|
|
>>> wi05
|
|
<WorkItem ['001', 1, 'jim', '2008-12-22 17:00', 'finished']:
|
|
{'start': 1229961600, 'created': ..., 'end': 1229962200, 'creator': 'jim'}>
|
|
>>> wi05.duration, wi05.effort
|
|
(600, 600)
|
|
|
|
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, 'jim', '... ...', 'closed']:
|
|
{'start': ..., 'created': ..., 'end': ..., 'creator': 'john'}>
|
|
|
|
Let's now check how many work items have been generated.
|
|
|
|
>>> len(list(workItems))
|
|
6
|
|
|
|
|
|
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".
|
|
The new work item is now in "planned" state, its predecessor is market as
|
|
delegated so that it may be selected by queries.
|
|
|
|
>>> wi07 = workItems.add('001', 'john', start=1229970800)
|
|
>>> wi08 = wi07.doAction('delegate', 'john', party='annie')
|
|
|
|
>>> wi07
|
|
<WorkItem ['001', 2, 'john', '2008-12-22 19:33', 'delegated']:
|
|
{'start': 1229970800, 'created': ..., 'creator': 'john'}>
|
|
>>> wi08
|
|
<WorkItem ['001', 3, 'annie', '2008-12-22 19:33', 'planned']:
|
|
{'start': 1229970800, 'created': ..., 'source': '0000007', 'creator': 'john'}>
|
|
>>> len(list(workItems))
|
|
8
|
|
|
|
|
|
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', 3, 'annie', '2008-12-22 19:33', 'replaced']:
|
|
{'start': 1229970800, 'created': ..., 'creator': 'john'}>
|
|
>>> wi09
|
|
<WorkItem ['001', 3, 'annie', '2008-12-22 19:33', 'planned']:
|
|
{'duration': 3600, 'start': 1229970800, 'created': ..., 'creator': 'annie'}>
|
|
|
|
|
|
Moving Work Items to Other Tasks
|
|
================================
|
|
|
|
>>> wi10 = wi07.doAction('move', 'john')
|
|
>>> wi10
|
|
<WorkItem ['001', 4, 'john', '2008-12-22 19:33', 'delegated']:
|
|
{'start': 1229970800, 'created': ..., 'source': '0000010', 'creator': 'john'}>
|
|
|
|
|
|
Queries
|
|
=======
|
|
|
|
Runs
|
|
----
|
|
|
|
>>> list(tracks.runs)
|
|
[2, 3, 4]
|
|
>>> list(tracks.finishedRuns)
|
|
[1]
|
|
|
|
|
|
Some Special Cases
|
|
==================
|
|
|
|
Close a run with some delegated items.
|
|
|
|
>>> wi07a = workItems.query(run=2)[-2]
|
|
>>> wi07b = workItems.query(run=2)[-1]
|
|
>>> wi07b
|
|
<WorkItem ['001', 2, 'john', '2008-12-22 19:33', 'moved']: {'start': 1229970800,
|
|
'created': ..., 'target': '0000011', 'creator': 'john'}>
|
|
|
|
>>> wi07c = wi07b.doAction('close', 'john')
|
|
|
|
>>> wi07a.state
|
|
'delegated_x'
|
|
>>> wi07b.state
|
|
'moved_x'
|
|
|
|
>>> list(tracks.finishedRuns)
|
|
[1, 2]
|