From ee49d66e0d8b3ea94c387dfda77659c9b503848b Mon Sep 17 00:00:00 2001 From: helmutm Date: Mon, 22 Dec 2008 12:41:20 +0000 Subject: [PATCH] work in progress: work item (task) management git-svn-id: svn://svn.cy55.de/Zope3/src/cybertools/trunk@3079 fd906abe-77d9-0310-91a1-e0d9ade77398 --- organize/README.txt | 34 ++++++++++++++++++++++++++- organize/configure.zcml | 10 +++++++- organize/interfaces.py | 22 +++++++++++++---- organize/work.py | 52 ++++++++++++++++++++++++++++++++++++++++- tracking/btree.py | 3 +-- 5 files changed, 112 insertions(+), 9 deletions(-) diff --git a/organize/README.txt b/organize/README.txt index 39ecde6..aa22d3f 100644 --- a/organize/README.txt +++ b/organize/README.txt @@ -4,6 +4,9 @@ Organizations: Persons, Institutions, Addresses... ($Id$) + >>> from zope import component + + Persons and Addresses ===================== @@ -48,6 +51,12 @@ Let's create an address and assign it to a person: u'Broadway 1' +Tasks +===== + + >>> from cybertools.organize.task import Task + + Service Management ================== @@ -59,5 +68,28 @@ Service Management Work ==== - >>> from cybertools.organize.work import WorkItem +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 WorkItemTrack, 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=WorkItemTrack) + >>> workItems = component.getAdapter(tracks, IWorkItems) + +The work management only deals with the IDs or names of tasks and persons, +so we do not have to set up real objects. + + >>> workItems.add('001', 'john') + diff --git a/organize/configure.zcml b/organize/configure.zcml index 1ce3841..2746e4d 100644 --- a/organize/configure.zcml +++ b/organize/configure.zcml @@ -3,8 +3,16 @@ + i18n_domain="cybertools.organize"> + + + + + + diff --git a/organize/interfaces.py b/organize/interfaces.py index 49f74f2..e7b167a 100644 --- a/organize/interfaces.py +++ b/organize/interfaces.py @@ -425,19 +425,24 @@ class IWorkItem(Interface): done by exactly one party (usually a person). """ - task = Attribute('The task this work item belongs to.') - runId = Attribute('Used for recurring tasks: identifies the run ' + task = Attribute('The task this work item belongs to, identified by ' + 'its name or ID.') + run = Attribute('Used for recurring tasks: identifies the run ' '(execution instance) of the task the work item belongs to.') - party = Attribute('Whoever does the work, usually a person.') + party = Attribute('Whoever does the work, usually a person, identified ' + 'by its name or ID.') state = Attribute('The current state the work item is in.') + description = Attribute('A note about what has to be done, and why...') comment = Attribute('A note about what has been done, and why...') # optional plan fields; duration (and effort) may be derived from start and end + # all date/time fields are timeStamp values, all duration and effort + # fields are in seconds planStart = Attribute('When the work should start.') planEnd = Attribute('When the work should be finished.') planDuration = Attribute('How long it may take to finish the work.') planEffort = Attribute('How much effort (time units) it might take ' 'to finish the work.') - # real stuff; duration (and effort) may be derived from start and end + # real stuff start = Attribute('When the work was started.') end = Attribute('When the work was finished.') duration = Attribute('How long it took to finish the work.') @@ -450,3 +455,12 @@ class IWorkItem(Interface): newTask = Attribute('Optional: a new task that has been created based ' 'on this work item.') + +class IWorkItems(Interface): + """ A collection (manager, container) of work items. + """ + + def add(task, party, run=0, **kw): + """ Create and register a work item; return it. Additional properties + may be specified via keyword arguments. + """ diff --git a/organize/work.py b/organize/work.py index 274073b..a1561b3 100644 --- a/organize/work.py +++ b/organize/work.py @@ -22,14 +22,17 @@ Planning and recording activities (work items). $Id$ """ +from zope import component from zope.component import adapts from zope.interface import implementer, implements -from cybertools.organize.interfaces import IWorkItem +from cybertools.organize.interfaces import IWorkItem, IWorkItems from cybertools.stateful.base import Stateful from cybertools.stateful.definition import StatesDefinition from cybertools.stateful.definition import State, Transition from cybertools.stateful.interfaces import IStatesDefinition +from cybertools.tracking.btree import Track +from cybertools.tracking.interfaces import ITrackingStorage @implementer(IStatesDefinition) @@ -56,3 +59,50 @@ class WorkItem(Stateful): statesDefinition = 'organize.workItemStates' + def getStatesDefinition(self): + return component.getUtility(IStatesDefinition, name=self.statesDefinition) + + # work item attributes (except state that is provided by stateful + + +class WorkItemTrack(WorkItem, Track): + """ A work item that may be stored as a track in a tracking storage. + """ + + metadata_attributes = Track.metadata_attributes + ('state',) + index_attributes = metadata_attributes + typeName = 'WorkItem' + + initAttributes = set(['description', 'predecessor', + 'planStart', 'planEnd', 'planDuration', 'planEffort']) + + def __init__(self, taskId, runId, userName, data={}): + for k in data: + if k not in initAttributes: + raise ValueError("Illegal initial attribute: '%s'." % k) + super(WorkItemTrack, self).__init__(taskId, runId, userName, data) + self.state = self.getState() # make initial state persistent + + def __getattr__(self, attr): + value = self.data.get(attr, _not_found) + if value is _not_found: + raise AttributeError(attr) + return value + + +class WorkItems(object): + """ A tracking storage adapter managing work items. + """ + + implements(IWorkItems) + adapts(ITrackingStorage) + + def __init__(self, context): + self.context = context + + def __getitem__(self, key): + return self.context[key] + + def add(self, task, party, run=0, **kw): + trackId = self.context.saveUserTrack(task, run, party, kw) + return self[trackId] diff --git a/tracking/btree.py b/tracking/btree.py index fdcbdc9..b946805 100644 --- a/tracking/btree.py +++ b/tracking/btree.py @@ -92,14 +92,13 @@ class TrackingStorage(BTreeContainer): implements(ITrackingStorage) trackFactory = Track + indexAttributes = trackFactory.index_attributes trackNum = runId = 0 runs = None # currently active runs finishedRuns = None # finished runs currentRuns = None # the currently active run for each task - indexAttributes = Track.index_attributes - def __init__(self, *args, **kw): trackFactory = kw.pop('trackFactory', None) if trackFactory is not None: