reorganize cybertools.process; add simple action handler and work item class
git-svn-id: svn://svn.cy55.de/Zope3/src/cybertools/trunk@1280 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
		
							parent
							
								
									9774638a5e
								
							
						
					
					
						commit
						b978abe473
					
				
					 6 changed files with 174 additions and 205 deletions
				
			
		|  | @ -5,50 +5,79 @@ Business Process Management | ||||||
| 
 | 
 | ||||||
| We start with the definition of a simple process: | We start with the definition of a simple process: | ||||||
| 
 | 
 | ||||||
|   startNode --> n01 --> endNode |   startActivity --> n01 --> endActivity | ||||||
| 
 | 
 | ||||||
|   >>> from cybertools.process.definition import ProcessDefinition, Node, Transition |   >>> from cybertools.process.definition import Process, Activity | ||||||
|   >>> process = ProcessDefinition() |   >>> process = Process() | ||||||
|   >>> n01 = Node() |   >>> n01 = Activity() | ||||||
|   >>> process.startNode.addTransition(n01) |   >>> process.startActivity.add(n01) | ||||||
|   >>> n01.addTransition(process.endNode) |   >>> n02 = Activity() | ||||||
|  |   >>> n01.add(n02) | ||||||
| 
 | 
 | ||||||
| Now let's execute the process: | Now let's execute the process: | ||||||
| 
 | 
 | ||||||
|   >>> from cybertools.process.execution import ProcessInstance |   >>> execution = process.execute() | ||||||
|   >>> instance = ProcessInstance(process) |  | ||||||
|   >>> execution = instance.execute() |  | ||||||
| 
 | 
 | ||||||
| As there aren't any interactions with the outside world in our process we | As there aren't any interactions with the outside world in our process we | ||||||
| don't see anything. But we can check if the process instance has reached the | don't see anything. But we can check if the process instance has reached the | ||||||
| process' end node: | process' end activity: | ||||||
| 
 | 
 | ||||||
|   >>> execution.currentNode is process.endNode |   >>> execution.currentActivity is n02 | ||||||
|   True |   True | ||||||
| 
 | 
 | ||||||
| So let's now associate an action handler with the process' nodes: | So let's now associate an action handler with the process' activitys: | ||||||
| 
 | 
 | ||||||
|   >>> from zope.component import provideAdapter, adapts |   >>> from zope.component import provideAdapter, adapts | ||||||
|   >>> from zope.interface import implements |   >>> from zope.interface import implements | ||||||
|   >>> from cybertools.process.interfaces import INode, IActionHandler |   >>> from cybertools.process.interfaces import IActivity, IActionHandler | ||||||
| 
 | 
 | ||||||
|   >>> class DummyHandler(object): |   >>> class DummyHandler(object): | ||||||
|   ...     implements(IActionHandler) |   ...     implements(IActionHandler) | ||||||
|   ...     adapts(INode) |   ...     adapts(IActivity) | ||||||
|   ...     def __init__(self, context): pass |   ...     def __init__(self, context): pass | ||||||
|   ...     def handle(self, execution): |   ...     def handle(self, execution): | ||||||
|   ...         print 'working.' |   ...         print 'working.' | ||||||
| 
 | 
 | ||||||
|   >>> provideAdapter(DummyHandler) |   >>> provideAdapter(DummyHandler) | ||||||
|   >>> execution = instance.execute() |   >>> execution = process.execute() | ||||||
|   working. |   working. | ||||||
|   >>> execution.currentNode is process.startNode |   >>> execution.currentActivity is process.startActivity | ||||||
|   True |   True | ||||||
|   >>> execution.trigger() |   >>> execution.trigger() | ||||||
|   working. |   working. | ||||||
|   >>> execution.currentNode is n01 |   >>> execution.currentActivity is n01 | ||||||
|   True |   True | ||||||
|   >>> execution.trigger() |   >>> execution.trigger() | ||||||
|   working. |   working. | ||||||
|   >>> execution.currentNode is process.endNode |   >>> execution.currentActivity is n02 | ||||||
|   True |   True | ||||||
|  | 
 | ||||||
|  | Next we'll use a predefined action handler that creates a work item. As this | ||||||
|  | makes only sense if the action handler can give the outside world access | ||||||
|  | to the work item somehow, we have to subclass this generic, abstract class: | ||||||
|  | 
 | ||||||
|  |   >>> workItems = [] | ||||||
|  |   >>> from cybertools.process.execution import WorkActionHandler | ||||||
|  |   >>> class MyActionHandler(WorkActionHandler): | ||||||
|  |   ...     def handle(self, execution): | ||||||
|  |   ...         super(MyActionHandler, self).handle(execution) | ||||||
|  |   ...         workItems.append(self.workItem) | ||||||
|  |   >>> provideAdapter(MyActionHandler) | ||||||
|  | 
 | ||||||
|  |   >>> execution = process.execute() | ||||||
|  | 
 | ||||||
|  | Now the process is waiting for somebody to pick up the work item and | ||||||
|  | submit it: | ||||||
|  | 
 | ||||||
|  |   >>> execution.currentActivity is process.startActivity | ||||||
|  |   True | ||||||
|  |   >>> workItem = workItems[0] | ||||||
|  |   >>> workItem.done | ||||||
|  |   False | ||||||
|  |   >>> workItem.submit() | ||||||
|  |   >>> execution.currentActivity is n01 | ||||||
|  |   True | ||||||
|  |   >>> workItem.done | ||||||
|  |   False | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |  | ||||||
|  | @ -24,93 +24,52 @@ $Id$ | ||||||
| 
 | 
 | ||||||
| from zope import component | from zope import component | ||||||
| from zope.interface import implements | from zope.interface import implements | ||||||
| from cybertools.process.interfaces import INode, ITransition, IProcessDefinition | from cybertools.process.interfaces import IActivity, IProcess | ||||||
| from cybertools.process.interfaces import IActionHandler | from cybertools.process.interfaces import IActionHandler | ||||||
|  | from cybertools.process.execution import Execution | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class Node(object): | class Activity(object): | ||||||
| 
 | 
 | ||||||
|     implements(INode) |     implements(IActivity) | ||||||
| 
 | 
 | ||||||
|     def __init__(self, process=None, name=u'', title=u'', handlerName=''): |     def __init__(self, name=u'', title=u'', handlerName=''): | ||||||
|         self._process = process |         self._successors = set() | ||||||
|         self._incoming = set() |  | ||||||
|         self._outgoing = set() |  | ||||||
|         self._handlerName = handlerName |         self._handlerName = handlerName | ||||||
|         self.__name__ = name |         self.__name__ = name | ||||||
|         self.title = title |         self.title = title | ||||||
| 
 | 
 | ||||||
|     def getProcess(self): return self._process |  | ||||||
|     def setProcess(self, prc): self._process = prc |  | ||||||
|     process = property(getProcess, setProcess) |  | ||||||
| 
 |  | ||||||
|     @property |     @property | ||||||
|     def outgoing(self): return self._outgoing |     def successors(self): return self._successors | ||||||
| 
 |  | ||||||
|     @property |  | ||||||
|     def incoming(self): return self._incoming |  | ||||||
| 
 | 
 | ||||||
|     def getHandlerName(self): return self._handlerName |     def getHandlerName(self): return self._handlerName | ||||||
|     def setHandlerName(self, name): self._handlerName = name |     def setHandlerName(self, name): self._handlerName = name | ||||||
|     handlerName = property(getHandlerName, setHandlerName) |     handlerName = property(getHandlerName, setHandlerName) | ||||||
| 
 | 
 | ||||||
|     def addTransition(self, destination): |     def add(self, activity): | ||||||
|         transition = Transition(destination) |         self.successors.add(activity) | ||||||
|         self.outgoing.add(transition) |  | ||||||
|         transition.source = self |  | ||||||
|         if transition.destination.process is None: |  | ||||||
|             transition.destination.process = self.process |  | ||||||
| 
 | 
 | ||||||
|     def execute(self, execution): |     def execute(self, execution=None): | ||||||
|         execution.currentNode = self |         if execution is None: | ||||||
|  |             execution = Execution() | ||||||
|  |         execution.currentActivity = self | ||||||
|         handler = component.queryAdapter(self, IActionHandler, name=self.handlerName) |         handler = component.queryAdapter(self, IActionHandler, name=self.handlerName) | ||||||
|         if handler is not None: |         if handler is not None: | ||||||
|             handler.handle(execution) # creates work item; work item triggers execution |             handler.handle(execution) # creates work item; work item triggers execution | ||||||
|         else: |         else: | ||||||
|             execution.trigger() |             execution.trigger() | ||||||
|  |         return execution | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class Transition(object): | class Process(object): | ||||||
| 
 | 
 | ||||||
|     implements(ITransition) |     implements(IProcess) | ||||||
| 
 |  | ||||||
|     def __init__(self, destination, qualifier=u''): |  | ||||||
|         self._source = None |  | ||||||
|         self._destination = destination |  | ||||||
|         self._qualifier = qualifier |  | ||||||
| 
 |  | ||||||
|     def getSource(self): return self._source |  | ||||||
|     def setSource(self, node): self._source = node |  | ||||||
|     source = property(getSource, setSource) |  | ||||||
| 
 |  | ||||||
|     def getDestination(self): return self._destination |  | ||||||
|     def setDestination(self, node): self._destination = node |  | ||||||
|     destination = property(getDestination, setDestination) |  | ||||||
| 
 |  | ||||||
|     def getQualifier(self): return self._qualifier |  | ||||||
|     def setQualifier(self, node): self._qualifier = node |  | ||||||
|     qualifier = property(getQualifier, setQualifier) |  | ||||||
| 
 |  | ||||||
|     def take(self, execution): |  | ||||||
|         self.destination.execute(execution) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class ProcessDefinition(object): |  | ||||||
| 
 |  | ||||||
|     implements(IProcessDefinition) |  | ||||||
| 
 | 
 | ||||||
|     def __init__(self): |     def __init__(self): | ||||||
|         self._instances = set() |         self._startActivity = Activity() | ||||||
|         self._startNode = Node(self) |  | ||||||
|         self._endNode = Node(self) |  | ||||||
| 
 | 
 | ||||||
|     @property |     @property | ||||||
|     def instances(self): return self._instances |     def startActivity(self): return self._startActivity | ||||||
| 
 | 
 | ||||||
|     def getStartNode(self): return self._startNode |     def execute(self): | ||||||
|     def setStartNode(self, node): self._startNode = node |         return self.startActivity.execute() | ||||||
|     startNode = property(getStartNode, setStartNode) |  | ||||||
| 
 |  | ||||||
|     def getEndNode(self): return self._endNode |  | ||||||
|     def setEndNode(self, node): self._endNode = node |  | ||||||
|     endNode = property(getEndNode, setEndNode) |  | ||||||
|  |  | ||||||
|  | @ -23,52 +23,81 @@ $Id$ | ||||||
| """ | """ | ||||||
| 
 | 
 | ||||||
| from zope.interface import implements | from zope.interface import implements | ||||||
| from cybertools.process.interfaces import IExecution, IProcessInstance | from zope.component import adapts | ||||||
|  | from cybertools.process.interfaces import IActivity, IExecution | ||||||
|  | from cybertools.process.interfaces import IWorkItem, IActionHandler | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class Execution(object): | class Execution(object): | ||||||
| 
 | 
 | ||||||
|     implements(IExecution) |     implements(IExecution) | ||||||
| 
 | 
 | ||||||
|     def __init__(self, instance): |     def __init__(self, parent=None): | ||||||
|         self._instance = instance |         self._currentActivity = None | ||||||
|         self._currentNode = None |  | ||||||
|         self._workItem = None |         self._workItem = None | ||||||
|  |         self._parent = parent | ||||||
|  |         self._children = set() | ||||||
| 
 | 
 | ||||||
|     @property |     def getCurrentActivity(self): return self._currentActivity | ||||||
|     def instance(self): return self._instance |     def setCurrentActivity(self, activity): self._currentActivity = activity | ||||||
| 
 |     currentActivity = property(getCurrentActivity, setCurrentActivity) | ||||||
|     def getCurrentNode(self): return self._currentNode |  | ||||||
|     def setCurrentNode(self, node): self._currentNode = node |  | ||||||
|     currentNode = property(getCurrentNode, setCurrentNode) |  | ||||||
| 
 | 
 | ||||||
|     def getWorkItem(self): return self._workItem |     def getWorkItem(self): return self._workItem | ||||||
|     def setWorkItem(self, item): self._workItem = item |     def setWorkItem(self, item): self._workItem = item | ||||||
|     workItem = property(getWorkItem, setWorkItem) |     workItem = property(getWorkItem, setWorkItem) | ||||||
| 
 | 
 | ||||||
|     def trigger(self, transitionPattern=None): |     @property | ||||||
|         outgoing = self.currentNode.outgoing |     def parent(self): return self._parent | ||||||
|         if outgoing: |  | ||||||
|             transition = iter(outgoing).next() |  | ||||||
|             transition.take(self) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class ProcessInstance(object): |  | ||||||
| 
 |  | ||||||
|     implements(IProcessInstance) |  | ||||||
| 
 |  | ||||||
|     def __init__(self, process): |  | ||||||
|         self._process = process |  | ||||||
|         self._executions = set() |  | ||||||
| 
 | 
 | ||||||
|     @property |     @property | ||||||
|     def process(self): return self._process |     def children(self): return self._children | ||||||
|  | 
 | ||||||
|  |     def trigger(self, qualifiers=set()): | ||||||
|  |         successors = [s for s in self.currentActivity.successors | ||||||
|  |                         if not qualifiers | ||||||
|  |                             or qualifiers.union(successor.qualifiers)] | ||||||
|  |         for successor in successors: | ||||||
|  |             if len(successors) == 1: | ||||||
|  |                 execution = self | ||||||
|  |             else: | ||||||
|  |                 execution = Execution(self) | ||||||
|  |                 self.children.add(execution) | ||||||
|  |             successor.execute(execution) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class WorkItem(object): | ||||||
|  | 
 | ||||||
|  |     implements(IWorkItem) | ||||||
|  | 
 | ||||||
|  |     def __init__(self, execution): | ||||||
|  |         self._execution = execution | ||||||
|  |         self._activity = execution.currentActivity | ||||||
| 
 | 
 | ||||||
|     @property |     @property | ||||||
|     def executions(self): return self._executions |     def execution(self): return self._execution | ||||||
|  | 
 | ||||||
|  |     @property | ||||||
|  |     def activity(self): return self._activity | ||||||
|  | 
 | ||||||
|  |     _done = False | ||||||
|  |     @property | ||||||
|  |     def done(self): return self._done | ||||||
|  | 
 | ||||||
|  |     def submit(self, data={}): | ||||||
|  |         _done = True | ||||||
|  |         self.execution.trigger() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class WorkActionHandler(object): | ||||||
|  |     """ A simple action handler that creates a work item. | ||||||
|  |     """ | ||||||
|  | 
 | ||||||
|  |     implements(IActionHandler) | ||||||
|  |     adapts(IActivity) | ||||||
|  | 
 | ||||||
|  |     def __init__(self, context): | ||||||
|  |         self.context = context | ||||||
|  | 
 | ||||||
|  |     def handle(self, execution): | ||||||
|  |         self.workItem = WorkItem(execution) | ||||||
| 
 | 
 | ||||||
|     def execute(self): |  | ||||||
|         execution = Execution(self) |  | ||||||
|         self.executions.add(execution) |  | ||||||
|         self.process.startNode.execute(execution) |  | ||||||
|         return execution |  | ||||||
|  |  | ||||||
|  | @ -31,64 +31,53 @@ _ = MessageFactory('zope') | ||||||
| 
 | 
 | ||||||
| # process/workflow definitions | # process/workflow definitions | ||||||
| 
 | 
 | ||||||
| class INode(Interface): | class IActivity(Interface): | ||||||
|     """ A step of a process - typically a state that lets the process wait |     """ A step of a process - typically a state that lets the process wait | ||||||
|         for a user interaction or an action that is executed automatically. |         for a user interaction or an action that is executed automatically. | ||||||
|     """ |     """ | ||||||
| 
 | 
 | ||||||
|     process = Attribute('The process this node belongs to') |     successors = Attribute('A set of activities following this activity') | ||||||
|     incoming = Attribute('Transitions that lead to this node') |  | ||||||
|     outgoing = Attribute('Transitions that lead to the next nodes') |  | ||||||
|     handlerName = Attribute('Name of an adapter that may handle the ' |     handlerName = Attribute('Name of an adapter that may handle the ' | ||||||
|                             'execution of this node') |                             'execution of this activity') | ||||||
|  |     qualifier = Attribute('A collection of strings giving additional ' | ||||||
|  |                           'information about an activity. ' | ||||||
|  |                           'May be used by an execution context ' | ||||||
|  |                           'for deciding which activity it will execute next') | ||||||
| 
 | 
 | ||||||
|     def addTransition(destination): |     def add(successor): | ||||||
|         """ Append a transition to the destination node given |         """ Append an activity to the collection of following activities. | ||||||
|             to the collection of outgoing transitions. |  | ||||||
|         """ |         """ | ||||||
| 
 | 
 | ||||||
|     def execute(execution): |     def execute(execution=None): | ||||||
|         """ Execute a node in an execution context of a process instance; |         """ Execute a activity in an execution context of a process instance; | ||||||
|             if this node signifies a wait state this will create a work item. |             if this activity signifies a wait state this will create a work item. | ||||||
|  |             If the execution argument is None a new execution context will | ||||||
|  |             be created. The execution context is returned. | ||||||
|         """ |         """ | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class ITransition(Interface): | class IProcess(Interface): | ||||||
|     """ A transition leading from one node (activity, state, action) to |  | ||||||
|         the next. |  | ||||||
|     """ |  | ||||||
|     source = Attribute('The node that triggered this transition') |  | ||||||
|     destination = Attribute('The destination node of this transition') |  | ||||||
|     qualifier = Attribute('A string giving a hint for the meaning of the ' |  | ||||||
|                           'transition. May be used by an execution context ' |  | ||||||
|                           'for deciding which transition it will transfer ' |  | ||||||
|                           'control to') |  | ||||||
| 
 |  | ||||||
|     def take(execution): |  | ||||||
|         """ Pass over the execution context from the source node to the |  | ||||||
|             destination node. |  | ||||||
|         """ |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class IProcessDefinition(Interface): |  | ||||||
|     """ The definition of a process or workflow. |     """ The definition of a process or workflow. | ||||||
|     """ |     """ | ||||||
| 
 | 
 | ||||||
|     instances = Attribute('A collection of process instances created from ' |     startActivity = Attribute('The start activity of this process') | ||||||
|                           'this process definition') | 
 | ||||||
|     startNode = Attribute('The start node of this process') |     def execute(): | ||||||
|     endNode = Attribute('The end node of this process') |         """ Start the process (typically by executing its start activity). | ||||||
|  |             Return the execution context. | ||||||
|  |         """ | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class IActionHandler(Interface): | class IActionHandler(Interface): | ||||||
|     """ Will be called for handling process executions. Is typically |     """ Will be called for handling process executions. Is typically | ||||||
|         implemented as an adapter for INode. |         implemented as an adapter for IActivity. | ||||||
|     """ |     """ | ||||||
| 
 | 
 | ||||||
|     def handle(execution): |     def handle(execution): | ||||||
|         """ Handles the execution of a node in the execution context given. |         """ Handles the execution of a activity in the execution context given. | ||||||
|         """ |         """ | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| # process execution | # process execution | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -97,41 +86,36 @@ class IExecution(Interface): | ||||||
|         current states) of a process instance. |         current states) of a process instance. | ||||||
|     """ |     """ | ||||||
| 
 | 
 | ||||||
|     instance = Attribute('The process instance this execution context belongs to') |     currentActivity = Attribute('The activity this execution is currently in') | ||||||
|     currentNode = Attribute('The node the process instance is currently in') |  | ||||||
|     workItem = Attribute('The work item the process instance is currently ' |     workItem = Attribute('The work item the process instance is currently ' | ||||||
|                          'waiting for; None if the current node is not in a ' |                          'waiting for; None if the current activity is not in a ' | ||||||
|                          'waiting state') |                          'waiting state') | ||||||
|  |     parent = Attribute('The execution context that has created this one ' | ||||||
|  |                        'e.g. because of a forking operation') | ||||||
|  |     children = Attribute('A collection of execution contexts that have been ' | ||||||
|  |                          'created by this one') | ||||||
| 
 | 
 | ||||||
|     def trigger(transitionQualifiers=None): |     def trigger(qualifiers=None): | ||||||
|         """ A callback (handler) that will may be called by an action handler. |         """ A callback (handler) that will may be called by an action handler. | ||||||
|             This will typically lead to moving on the execution context |             This will typically lead to moving on the execution context | ||||||
|             to an outgoing transition of the current node. The execution |             to a successor activity of the current activity. The execution | ||||||
|             context will use the transitionQualifiers to decide which outgoing |             context will use the qualifiers argument to decide which | ||||||
|             transition(s) to transfer control to. |             activities to transfer control to. | ||||||
|         """ |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class IProcessInstance(Interface): |  | ||||||
|     """ An executing process, i.e. an execution context that keeps track of |  | ||||||
|         the currently active node(s) of the process. |  | ||||||
|     """ |  | ||||||
| 
 |  | ||||||
|     process = Attribute('The process definition this instance is created from') |  | ||||||
|     executions = Attribute('A collection of currently active execution contexts') |  | ||||||
| 
 |  | ||||||
|     def execute(): |  | ||||||
|         """ Start the execution of the process with its start node; |  | ||||||
|             return the execution context. |  | ||||||
|         """ |         """ | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class IWorkItem(Interface): | class IWorkItem(Interface): | ||||||
|     """ An instance of an activity from a process definition. |     """ A work item tells some external entity - typically a user - to | ||||||
|  |         do something in order to let the process proceed. | ||||||
|     """ |     """ | ||||||
| 
 | 
 | ||||||
|     node = Attribute('The node this work item has been created from') |     activity = Attribute('The activity this work item has been created from') | ||||||
|     execution = Attribute('The execution context (and thus the process ' |     execution = Attribute('The execution context that has created this work item') | ||||||
|                           'instance) that has create this work item') |     done = Attribute('A Boolean attribute that is true if the work ' | ||||||
| 
 |                      'item has been submitted') | ||||||
| 
 | 
 | ||||||
|  |     def submit(data={}): | ||||||
|  |         """ Provide the work item with some data (optional) and have it | ||||||
|  |             continue the process by triggering the execution context. | ||||||
|  |             This should also set the `done` attribute to True. | ||||||
|  |         """ | ||||||
|  |  | ||||||
|  | @ -8,13 +8,13 @@ $Id$ | ||||||
| 
 | 
 | ||||||
| import unittest, doctest | import unittest, doctest | ||||||
| from zope.testing.doctestunit import DocFileSuite | from zope.testing.doctestunit import DocFileSuite | ||||||
| from cybertools.process.definition import ProcessDefinition | from cybertools.process.definition import Process | ||||||
| 
 | 
 | ||||||
| class TestProcess(unittest.TestCase): | class TestProcess(unittest.TestCase): | ||||||
|     "Basic tests for the process package." |     "Basic tests for the process package." | ||||||
| 
 | 
 | ||||||
|     def testBasicStuff(self): |     def testBasicStuff(self): | ||||||
|         p = ProcessDefinition() |         p = Process() | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def test_suite(): | def test_suite(): | ||||||
|  |  | ||||||
|  | @ -1,32 +0,0 @@ | ||||||
| # |  | ||||||
| #  Copyright (c) 2006 Helmut Merz helmutm@cy55.de |  | ||||||
| # |  | ||||||
| #  This program is free software; you can redistribute it and/or modify |  | ||||||
| #  it under the terms of the GNU General Public License as published by |  | ||||||
| #  the Free Software Foundation; either version 2 of the License, or |  | ||||||
| #  (at your option) any later version. |  | ||||||
| # |  | ||||||
| #  This program is distributed in the hope that it will be useful, |  | ||||||
| #  but WITHOUT ANY WARRANTY; without even the implied warranty of |  | ||||||
| #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the |  | ||||||
| #  GNU General Public License for more details. |  | ||||||
| # |  | ||||||
| #  You should have received a copy of the GNU General Public License |  | ||||||
| #  along with this program; if not, write to the Free Software |  | ||||||
| #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA |  | ||||||
| # |  | ||||||
| 
 |  | ||||||
| """ |  | ||||||
| Work items (tasks) and similar stuff. |  | ||||||
| 
 |  | ||||||
| $Id$ |  | ||||||
| """ |  | ||||||
| 
 |  | ||||||
| from zope.interface import implements |  | ||||||
| from cybertools.process.interfaces import IWorkItem |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class WorkItem(object): |  | ||||||
| 
 |  | ||||||
|     implements(IWorkItem) |  | ||||||
| 
 |  | ||||||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 helmutm
						helmutm