Skip to content

/ Zope / gocept svn checkins / Archive / 2005 / 2005-01 / CVS: AlphaFlow/examples - routing_example.xml:1.1

[ << ] [ >> ]

[ CVS: glome/skins/glome_widgets - ... ] [ CVS: CMFLinkChecker/doc/features ... ]

CVS: AlphaFlow/examples - routing_example.xml:1.1
Christian Theune <ct(at)gocept.com>
2005-01-27 14:56:26 [ FULL ]
Update of /cvs/gocept/AlphaFlow/examples
In directory amy:/tmp/cvs-serv12058/examples

Added Files:
	routing_example.xml 
Log Message:
 - advanced routing


=== Added File AlphaFlow/examples/routing_example.xml ===
<?xml version="1.0" encoding="iso-8859-1"?>

<workflow title=""  
    startActivity="review"
    description=""
    onlyAllowRoles="Manager,Reviewer">

    <route id="review"
        title="Decisions by Marketing, Medical and Legal Departments">

        <rolebased-review id="review_legal" 
                title="Check legal issues"
                review_notice="Please check any legal issues that might occur."
                roles="Member" 
                reject_activity="reject"
                accept_activity="accept"/>

        <rolebased-review id="review_mkt" 
                title="Check Marketing"
                review_notice="Check if this marketing works."
                roles="Member" 
                reject_activity="reject"
                accept_activity="accept"/>

        <rolebased-review id="review_medical" 
                title="Check medical issues"
                review_notice="Please check for medical issues."
                roles="Member" 
                reject_activity="reject"
                accept_activity="accept"/>

        <gate id="reject" 
            title="Reject review"
            mode="discriminate"
            continue_activity="private"/>

        <gate id="accept"
            title="Accept review"
            mode="synchronizing-merge"
            continue_activity="public"/>
    </route>

    <dcworkflow id="private" title="" status="private"/>
    <dcworkflow id="public" title="" status="published"/>

</workflow>

CVS: AlphaFlow/tests - test_routing.py:1.1
Christian Theune <ct(at)gocept.com>
2005-01-27 15:05:55 [ FULL ]
Update of /cvs/gocept/AlphaFlow/tests
In directory amy:/tmp/cvs-serv12058/tests

Added Files:
	test_routing.py 
Log Message:
 - advanced routing


=== Added File AlphaFlow/tests/test_routing.py ===
# Unit test for workflow definitions

import os, sys
if __name__ == '__main__':
    execfile(os.path.join(sys.path[0], 'framework.py'))

import unittest

from Testing import ZopeTestCase

from Products.Archetypes.tests.utils import *
from Products.Archetypes.tests.common import *

from Products.CMFCore.utils import getToolByName

from Products.AlphaFlow.tests.AlphaFlowTestCase import AlphaFlowTestCase

class RoutingTest(AlphaFlowTestCase):

    def _import_wf(self):
        portal = self.getPortal()
        wftool = getToolByName(portal, "workflow_manager")
        f = file(os.path.os.path.dirname(__file__) +
            "../examples/routing_example.xml")
        wftool.importWorkflowFromXML("test", f)

    def test_definition(self):
        # Creates a simple workflow
        portal = self.getPortal()
        self._create_test_users()
        self.loginPortalOwner()
        self._import_wf()

        wftool = getToolByName(portal, 'workflow_manager')

        # Create object for instanciation of this process
        portal.createObject("testdocument", "DummyContent")

        # Initialize the process
        doc = portal.testdocument
        doc.assignProcess(doc.getSuitableProcesses()[0].getId())

        process = doc.getInstance()
        process.start("testing")
        self.assertEquals(process.state, "active")

        self.login("author")
        doc.getWorkItemsForCurrentUser()[0].accept()
        doc.getWorkItemsForCurrentUser()[0].accept()
        doc.getWorkItemsForCurrentUser()[0].accept()

        self.assertEquals(process.state, "complete")

        wis = process.getWorkItems(state=None)
        for wi in wis:
            self.failIfEqual(wi.state, "fallout")

        # Do the same process but reject this time
        self.loginPortalOwner()
        doc.assignProcess(doc.getSuitableProcesses()[0].getId())

        process = doc.getInstance()
        process.start("testing")
        self.assertEquals(process.state, "active")

        self.login("author")
        doc.getWorkItemsForCurrentUser()[0].accept()
        doc.getWorkItemsForCurrentUser()[0].reject()

        self.assertEquals(process.state, "complete")

        wis = process.getWorkItems(state=None)
        for wi in wis:
            self.failIfEqual(wi.state, "fallout")


       
    
def test_suite():
    suite = unittest.TestSuite()
    suite.addTest(unittest.makeSuite(RoutingTest))
    return suite 

if __name__ == '__main__':
    framework()

CVS: AlphaFlow/activities - gates.py:1.1 routing.py:1.1
Christian Theune <ct(at)gocept.com>
2005-01-27 15:15:56 [ FULL ]
Update of /cvs/gocept/AlphaFlow/activities
In directory amy:/tmp/cvs-serv12058/activities

Added Files:
	gates.py routing.py 
Log Message:
 - advanced routing


=== Added File AlphaFlow/activities/gates.py ===
# Copyright (c) 2004 gocept gmbh & co. kg
# See also LICENSE.txt
# $Id: gates.py,v 1.1 2005/01/27 13:36:59 ctheune Exp $
"""Gates to support routing mechanisms
"""

# Sibling imports
from Products.AlphaFlow.workitem import registerWorkItem, BaseWorkItem
from Products.AlphaFlow.activity import registerActivity, BaseAutomaticActivity
from Products.AlphaFlow.exception import ConfigurationError
from Products.AlphaFlow.interfaces import IDaemonActivity


class GateActivity(BaseAutomaticActivity):

    __implements__ = BaseAutomaticActivity.__implements__ + (IDaemonActivity,)

    meta_type = "AlphaFlow Gate Activity"
    activity_type = "gate"

    mode = ""

    mode_types = ('multi-merge', 'discriminate', 'synchronizing-merge')

    _properties = BaseAutomaticActivity._properties + \
        ({'id': 'mode', 'type': 'selection', 'mode': 'w',
          'select_variable': 'mode_types'},
         )

    # Discriminate is like "XOR" (trigger on first, ignore others)
    # Multimerge is like "OR" (trigger on each)
    # Synchronize is like "AND" (trigger once when all completed)

    def configureFromDOMNode(self, node):
        """Configures the activity from a given DOM-Node"""
        GateActivity.inheritedAttribute('configureFromDOMNode')(self, node)
        if not node.hasAttribute("mode"):
            raise ConfigurationError, "Mode required in %r" % (
                    self.activity_type, )
        self.mode = node.getAttribute('mode').encode("ascii")
        if self.mode not in self.mode_types:
            raise ConfigurationError, "Mode must be one of %s for %r" % (
                    self.mode_types, self.activity_type)


class GateWorkItem(BaseWorkItem):

    activity_type  = "gate"

    ####################
    # IWorkItem

    def onStart(self):
        self.open_routes = self._getAllRoutes()

    def beforeCreationItems(self, items, parent):
        vote_no = []
        my_parent = self.getParent()

        # Do the new items belong to a route?
        route = self._findRouteForWI(parent)
        if route is None:
            return []

        # Don't allow this gate to be generated again
        for wi in items:
            if wi == self.activity_id:
                vote_no.append(wi)
                self._rememberTrigger(parent, route)
        return vote_no

    def _findRouteForWI(self, workitem):
        """Identifies to which route this workitem belongs that leads to this
gate.

        Returns one of self.getParent().getActivity().routes or None
        """
        routing_start = self.getParent()
        if not workitem.isChildOf(workitem=routing_start):
            return None
        
        possible_routes = [ self.getInstance()[x] for x in
routing_start.generated_workitems ]
        for candidate in possible_routes:
            if not candidate.state == "active":
                continue
            if not candidate.activity_id in routing_start.getActivity().routes:
                continue
            if (workitem == candidate) or \
                    workitem.isChildOf(workitem=candidate):
                return candidate.activity_id
        
    # Private methods
    def _rememberTrigger(self, triggering_workitem, route):
        mode = self.getActivity().mode
        open_routes = self.open_routes
        if not route in open_routes:
            return
        if mode == "multi-merge":
            open_routes.remove(route)
            self._doTrigger(triggering_workitem, route)
            if len(open_routes) == 0:
                self.changeState("complete", "Gate '%s' found by route '%s'" %
                        (self.activity_id, route))
        elif mode == "discriminate":
            if len(open_routes) == len(self._getAllRoutes()):
                self._doTrigger(triggering_workitem, route)
                self.changeState("complete", "Gate '%s' found by route '%s'" %
                        (self.activity_id, route))
            open_routes.remove(route)
        elif mode == "synchronizing-merge":
            open_routes.remove(route)
            if len(open_routes) == 0:
                self._doTrigger(triggering_workitem, route)
                self.changeState("complete", "Gate '%s' found by route '%s'" %
                        (self.activity_id, route))
        else:
            self.changeState("fallout", "Unknown gate mode: '%s'" % mode)
        self.open_routes = open_routes

    def _doTrigger(self, triggering_workitem, route):
        open_routes = self.open_routes
        self.createWorkItems(self.getActivity().continue_activity)
        self.recordAction("Gate triggered", 
                "Route '%s' triggered '%s-gate' by workitem '%s'" % 
                (route, self.getActivity().mode, triggering_workitem.getId()))

    def _getAllRoutes(self):
        return list(tuple(self.getParent().getActivity().routes))

    def estimateRelevantUsers(self):
        return ""


# register the stuff
registerActivity(GateActivity)
registerWorkItem(GateWorkItem)


=== Added File AlphaFlow/activities/routing.py ===
# Copyright (c) 2004 gocept gmbh & co. kg
# See also LICENSE.txt
# $Id: routing.py,v 1.1 2005/01/27 13:36:59 ctheune Exp $
"""Complex routing mechanisms
"""

# Zope imports
from AccessControl import ClassSecurityInfo

# Sibling imports
from Products.AlphaFlow.workitem import registerWorkItem, BaseWorkItem
from Products.AlphaFlow.activity import registerActivity, BaseActivity
from Products.AlphaFlow import config
from Products.AlphaFlow.utils import killWorkItemRecursively


class RoutingActivity(BaseActivity):
    """Routing activity

    A routing activity  controls complex routing within a workflow over
    multiple work items and work item branches.

    The 'continue_activity' starts one or more routes. When a route reaches a
    gate (by calling createWorkItems with a gate as an activity) the gate
    notices that and might trigger. When a gate triggers all active routes and
    all other active gates are terminated. Only routes and gates that are
    offsprings from this routing activity will be controlled by the gates of
    this routing activity.  
    """
    
    meta_type = "AlphaFlow Routing Activity"
    activity_type = "route"

    security = ClassSecurityInfo()

    gates = ()
    routes = ()

    _properties = BaseActivity._properties + \
        ({'id': 'gates', 'type': 'multiple selection', 'mode': 'w',
          'select_variable': 'listActivityIds'},
         {'id': 'routes', 'type': 'multiple selection', 'mode': 'w',
          'select_variable': 'listActivityIds'},
         )
    
    def configureFromDOMNode(self, node):
        """Configures the activity from a given DOM-Node"""
        BaseActivity.configureFromDOMNode(self, node)

        process = self.getParentNode()
        for child in node.childNodes:
            if child.nodeType in (child.TEXT_NODE,
                    child.COMMENT_NODE):
                # Ignore whitespace
                continue
            act_id = child.getAttribute('id').encode('ascii')
            act_type = child.nodeName
            activity = process.addActivity(act_id, act_type)
            activity.configureFromDOMNode(child)
            if act_type == "gate":
                self.gates = self.gates + (act_id,)
            else:
                self.routes = self.routes + (act_id,)

    security.declareProtected(config.MANAGE_WORKFLOW, "getPossibleChildren")
    def getPossibleChildren(self):
        """Return a list of possible following activities. (List of ids)"""
        return self.routes

    security.declareProtected(config.MANAGE_WORKFLOW,
"graphGetPossibleChildren")
    def graphGetPossibleChildren(self):
        """Return a list of possible following activities. (List of ids)"""
        acts = []
        for act in self.routes:
            acts.append({'id':act,
                    'label':'Route start'})
        return acts
           

class RoutingWorkItem(BaseWorkItem):

    activity_type  = "route"

    ######################
    # IWorkItem

    def onStart(self):
        """Start the gate daemons and route work items
        """
        self.createWorkItems(self.getActivity().gates)
        self.createWorkItems(self.getActivity().routes)

    def notifyWorkItemStateChange(self, workitem):
        """Check if routes have to be closed."""
        # check if workitem is one of our gates
        if not workitem.getId() in self.generated_workitems:
            return
        if not workitem.activity_type == "gate":
            return
        
        # check if gate is completed
        if workitem.state != "complete":
            return

        finished_gate = workitem.activity_id

        workitems = self.getGeneratedWorkItems()
        # kill other gates
        for wi in workitems:
            if wi.activity_type != "gate":
                continue
            if wi.state != "active":
                continue
            wi.changeState("terminated", "Competing gate '%s' successfully "
                "completed." % finished_gate)

        # kill open routes
        for wi in workitems:
            if wi.activity_id == "gate":
                continue
            if wi.state != "active":
                continue
            killWorkItemRecursively(wi, "Competing route successfully "
                "completed at gate '%s'." % finished_gate)

        # complete routing
        self.changeState("complete", "Gate %s completed" %
workitem.activity_id)

    def getShortInfo(self):
        """Short information"""     #XXX
        return "Route is open"

    def getStatusInfo(self):
        """Short status information"""  # XXX
        return "Route is open"


# register the stuff
registerActivity(RoutingActivity)
registerWorkItem(RoutingWorkItem)

CVS: AlphaFlow/examples - alphaflow.dtd:1.1
Roman Joost <rj(at)gocept.com>
2005-01-27 16:57:55 [ FULL ]
Update of /cvs/gocept/AlphaFlow/examples
In directory amy:/tmp/cvs-serv5599/examples

Added Files:
	alphaflow.dtd 
Log Message:
- added DTD for alphaflow workflow definition XML


=== Added File AlphaFlow/examples/alphaflow.dtd ===
<!-- 
    Copyright (c) 2004 gocept gmbh & co. kg
    See also LICENSE.txt
-->
<!ELEMENT workflow ( permission-change |
                    dcworkflow |
                    task |
                    review |
                    rolebased-review |
                    recursion |
                    route )+>
                    
<!ATTLIST workflow
    title               CDATA               #IMPLIED
    startActivity       IDREFS              #REQUIRED
    description         CDATA               #IMPLIED
    onlyAllowRoles      CDATA               #REQUIRED
    >

<!ELEMENT permission-change (permission+)>
<!ATTLIST permission-change
    id                  ID                  #REQUIRED
    title               CDATA               #IMPLIED
    continue_activity   IDREFS              #REQUIRED
    >
<!ELEMENT permission EMPTY>
<!ATTLIST permission
    id                  ID                  #REQUIRED
    acquire         ( False | True )        #REQUIRED
    roles               CDATA               #REQUIRED
    >

<!ELEMENT rolebased-review EMPTY>
<!ATTLIST rolebased-review
    id                  ID                  #REQUIRED
    title               CDATA               #IMPLIED
    sort                NMTOKEN             #IMPLIED
    review_notice       CDATA               #REQUIRED
    roles               CDATA               #REQUIRED
    reject_activity     IDREF               #REQUIRED
    accept_activity     IDREF               #REQUIRED
    >

<!ELEMENT dcworkflow EMPTY>
<!ATTLIST dcworkflow
    id                  ID                  #REQUIRED
    title               CDATA               #IMPLIED
    status  ( private | published | pending ) #REQUIRED
    >

<!ELEMENT review EMPTY>
<!ATTLIST review
    id                  ID                  #REQUIRED
    title               CDATA               #IMPLIED
    sort                NMTOKEN             #IMPLIED
    review_notice       CDATA               #REQUIRED
    roles               CDATA               #REQUIRED
    reject_activity     IDREF               #REQUIRED
    accept_activity     IDREF               #REQUIRED
    >

<!ELEMENT recursion EMPTY>
<!ATTLIST recursion
    id                  ID                  #REQUIRED
    recursion_activity  IDREFS              #REQUIRED
    block_activities    IDREFS              #REQUIRED
    optional_recursion  CDATA               #IMPLIED
    >

<!ELEMENT task EMPTY>
<!ATTLIST task
    id                  ID                  #REQUIRED
    title               CDATA               #IMPLIED
    sort                NMTOKEN             #IMPLIED
    completion_activity IDREF               #REQUIRED
    roles               CDATA               #REQUIRED
    >

<!ELEMENT route (rolebased-review |
                 gate |
                 review )+ >
<!ATTLIST route
    id                  ID                  #REQUIRED
    title               CDATA               #IMPLIED
    >

<!ELEMENT gate EMPTY>
<!ATTLIST gate
    id                  ID                  #REQUIRED
    title               CDATA               #IMPLIED
    mode    ( synchronizing-merge | discriminate | multi-merge ) #REQUIRED
    continue_activity   IDREF               #REQUIRED
    >

CVS: AlphaFlow/examples - alphaflow.dtd:1.2 routing_example.xml:1.2
Roman Joost <rj(at)gocept.com>
2005-01-28 12:02:01 [ FULL ]
Update of /cvs/gocept/AlphaFlow/examples
In directory amy:/tmp/cvs-serv16175

Modified Files:
	alphaflow.dtd routing_example.xml 
Log Message:
- added entities to reuse declarations
- added doctype to routing_example


=== AlphaFlow/examples/alphaflow.dtd 1.1 => 1.2 ===
--- AlphaFlow/examples/alphaflow.dtd:1.1	Thu Jan 27 17:03:03 2005
+++ AlphaFlow/examples/alphaflow.dtd	Fri Jan 28 12:07:14 2005
(at)(at) -2,6 +2,39 (at)(at)
     Copyright (c) 2004 gocept gmbh & co. kg
     See also LICENSE.txt
 -->
+<!ENTITY % id.attrib 
+    "id         ID          #REQUIRED">
+
+<!ENTITY % title.attrib
+    "title      CDATA       #IMPLIED">
+
+<!ENTITY % sort.attrib
+    "sort       NMTOKEN     #IMPLIED">
+
+<!ENTITY % review_notice.attrib
+    "review_notice  CDATA   #IMPLIED">
+
+<!ENTITY % roles.attrib
+    "roles      CDATA       #REQUIRED">
+
+<!ENTITY % reject_activity.attrib
+    "reject_activity     IDREF               #REQUIRED">
+
+<!ENTITY % accept_activity.attrib
+    "accept_activity     IDREF               #REQUIRED">
+
+<!ENTITY % common.attlist
+    "%id.attrib;
+    %title.attrib;"
+    >
+<!ENTITY % review.attlist
+    "%sort.attrib;
+    %review_notice.attrib;
+    %roles.attrib;
+    %reject_activity.attrib;
+    %accept_activity.attrib;"
+    >
+
 <!ELEMENT workflow ( permission-change |
                     dcworkflow |
                     task |
(at)(at) -11,7 +44,7 (at)(at)
                     route )+>
                     
 <!ATTLIST workflow
-    title               CDATA               #IMPLIED
+    %title.attrib;
     startActivity       IDREFS              #REQUIRED
     description         CDATA               #IMPLIED
     onlyAllowRoles      CDATA               #REQUIRED
(at)(at) -19,49 +52,37 (at)(at)
 
 <!ELEMENT permission-change (permission+)>
 <!ATTLIST permission-change
-    id                  ID                  #REQUIRED
-    title               CDATA               #IMPLIED
+    %common.attlist;
     continue_activity   IDREFS              #REQUIRED
     >
 <!ELEMENT permission EMPTY>
 <!ATTLIST permission
-    id                  ID                  #REQUIRED
+    %id.attrib;
     acquire         ( False | True )        #REQUIRED
     roles               CDATA               #REQUIRED
     >
 
 <!ELEMENT rolebased-review EMPTY>
 <!ATTLIST rolebased-review
-    id                  ID                  #REQUIRED
-    title               CDATA               #IMPLIED
-    sort                NMTOKEN             #IMPLIED
-    review_notice       CDATA               #REQUIRED
-    roles               CDATA               #REQUIRED
-    reject_activity     IDREF               #REQUIRED
-    accept_activity     IDREF               #REQUIRED
+    %common.attlist;
+    %review.attlist;
     >
 
 <!ELEMENT dcworkflow EMPTY>
 <!ATTLIST dcworkflow
-    id                  ID                  #REQUIRED
-    title               CDATA               #IMPLIED
+    %common.attlist;
     status  ( private | published | pending ) #REQUIRED
     >
 
 <!ELEMENT review EMPTY>
 <!ATTLIST review
-    id                  ID                  #REQUIRED
-    title               CDATA               #IMPLIED
-    sort                NMTOKEN             #IMPLIED
-    review_notice       CDATA               #REQUIRED
-    roles               CDATA               #REQUIRED
-    reject_activity     IDREF               #REQUIRED
-    accept_activity     IDREF               #REQUIRED
+    %common.attlist;
+    %review.attlist;
     >
 
 <!ELEMENT recursion EMPTY>
 <!ATTLIST recursion
-    id                  ID                  #REQUIRED
+    %id.attrib;
     recursion_activity  IDREFS              #REQUIRED
     block_activities    IDREFS              #REQUIRED
     optional_recursion  CDATA               #IMPLIED
(at)(at) -69,8 +90,7 (at)(at)
 
 <!ELEMENT task EMPTY>
 <!ATTLIST task
-    id                  ID                  #REQUIRED
-    title               CDATA               #IMPLIED
+    %common.attlist;
     sort                NMTOKEN             #IMPLIED
     completion_activity IDREF               #REQUIRED
     roles               CDATA               #REQUIRED
(at)(at) -80,14 +100,12 (at)(at)
                  gate |
                  review )+ >
 <!ATTLIST route
-    id                  ID                  #REQUIRED
-    title               CDATA               #IMPLIED
+    %common.attlist;
     >
 
 <!ELEMENT gate EMPTY>
 <!ATTLIST gate
-    id                  ID                  #REQUIRED
-    title               CDATA               #IMPLIED
+    %common.attlist;
     mode    ( synchronizing-merge | discriminate | multi-merge ) #REQUIRED
     continue_activity   IDREF               #REQUIRED
     >


=== AlphaFlow/examples/routing_example.xml 1.1 => 1.2 ===
--- AlphaFlow/examples/routing_example.xml:1.1	Thu Jan 27 14:37:00 2005
+++ AlphaFlow/examples/routing_example.xml	Fri Jan 28 12:07:14 2005
(at)(at) -1,4 +1,5 (at)(at)
 <?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE workflow SYSTEM "alphaflow.dtd">
 
 <workflow title=""  
     startActivity="review"

MailBoxer