A CPS Workflow Implementation Procedure

Author: Dave Kuhlman
Address:
dkuhlman@rexx.com
http://www.rexx.com/~dkuhlman
Revision: 1.0a
Date: July 19, 2005
Copyright: Copyright (c) 2005 Dave Kuhlman. All Rights Reserved. This software is subject to the provisions of the Zope Public License, Version 2.1 (ZPL): http://www.zope.org/Resources/License/

Abstract

This document describes a procedure for implementing a business process as a CPS workflow. It is based on the CPSWorkflowHowTo.

Contents

1   Introduction

This document attempts to give guidance toward the implementation of business processes as CPS workflows. The general procedure suggested here is to copy and then modify CPSWorkflowHowTo to implement your workflow.

Licensing -- A reminder -- This document suggests that you proceed by beginning with and then modifying CPSWorkflowHowTo. If you do so, you should preserve and honor the license for that product.

Our business process implementation model assumes that instances of a single document type are managed by CPS workflow.

The instructions given below uses an order execution workflow as an example of how to turn the CPSWorkflowHowTo into a workflow that manages a business process. For our examples, we use the following name mappings:

2   The Procedure and Steps

Follow these steps.

2.1   Copy CPSWorkflowHowTo

Use the original distribution of CPSWorkflowHowTo. It is available at: http://www.cps-project.org/sections/documentation/developers/cpsworkflow. (Look for "Sample product".) Expand it in Products directory of your Zope instance. Then rename the resulting sub-directory.

This should result in a subdirectory under Products that is named for your business process, for example "OrderExecutionWF".

2.2   Rename files

Rename the following file:

  • Rename: documents/schedule_document.py --> order_document.py.
  • Rename: workflows/validation_workflow.py --> order_execution_workflow.py.

2.3   Search and replace

Here are some modifications you will want to make:

  • Extensions/install.py:

    • Replace: "CPSWorkflowHowTo" --> "OrderExecutionWF".
    • Replace: "validation_workflow" --> "order_execution_workflow"
    • Replace: "schedule_document" --> "order_document"
    • Replace: "Validation" --> "OrderExecution"
    • Replace: "Schedule" --> "Order"
  • workflows/__init__.py

  • workflows/order_execution_workflow.py:

    • Replace: "Validation" --> "OrderExecutionWF"

    This module defines the workflow. See below for more notes on defining the workflow

  • documents/__init__.py

  • documents/order_document.py:

    • Replace: "Schedule" --> "Order"

There are also comments in some files that you will want to modify.

Here are other files that you will want to check and possibly modify:

  • HISTORY.txt
  • README.txt
  • __init__.py
  • i18n/en.po
  • i18n/fr.po

Note the license at the top of some of these files. You should preserve and honor the terms of this license.

2.4   Add and change roles

Roles are defined in:

  • ./Extensions/install.py -- Add your new roles in method setupNewRoles.
  • ./workflows/order_execution_workflow.py -- Add the use of your role to function getOrderExecWorkflowTransitions. See below.

2.5   Define the workflow

Suggestion: You may want to install the ZMIntrospection product and use it to inspect existing workflow objects. See section Help from ZMIntrospection for more on this.

2.5.1   Define workflow stacks

Stacks are defined and used in function getOrderExecutionWorkflowStates in module workflows.orderexec_workflow.py.

You can learn more about the definition of stacks by looking in the following places:

  • In the ZMI: mysite/portal_workflow/myworkflow/states/somestate -- Then click on the Workflow Stacks tab.
  • In the source: Products/CPSWorkflow/states.py -- In particular, look at the following methods: (1) addStackDefinition and (2) updateStackDefinition.

A stack is defined with a dictionary. The following keys and values are applicable in the dictionary that defines a stack:

  • stackdef_type -- is the stack definition type. Possible values are:

    • 'Hierarchical Stack Definition'
    • 'Simple Stack Definition'
  • stack_type -- stack type (cf. above for the different available types. Possible values are:

    • 'Hierarchical Stack'
    • 'Simple Stack'
  • var_id -- workflow variable id used to store this new variable.

  • 'managed_role_exprs' -- A dictionary whose keys are role names and whose values are tales expressions that specify "the managed roles, e.g. roles that the stack definition can cope with. Roles have an associated tales expression evaluated within the stack definition context, defining the policy for the given role." (from the CPSWorkflowHowTo document) Here is an example:

    {
    
        # SectionReviewer: top of the stack, and at current level
        'SectionReviewer':
            "python:stack.getAllLevels() and " \
            "level == stack.getAllLevels()[-1] and " \
            "level == stack.getCurrentLevel()",
        # SectionIntermediateReviewer: not top of the stack or not at current level
        'SectionIntermediateReviewer': 
            "python:stack.getAllLevels() and " \
            "level < stack.getAllLevels()[-1] or " \
            "level != stack.getCurrentLevel()",
    },
    
  • 'empty_stack_manage_guard' -- This is the guard and management policy that is applied when the stack is empty. Specify Who can manage the stack when the stack is empty. Here is an example:

    {
        'guard_roles': '; '.join(edit_roles),
    },
    
    • See CPSWorkflow.stackdefinition.setEmptyStackManageGuard for an example of how this policy is defined.
    • See CPSWorkflow.stackdefinitionguard.StackDefinitionGuard.check for information on the check performed.

Note: You can also define new stack definitions, new stacks, and new stack elements. The CPSWorkflowHowTo describes this. And, there are examples:

  • The User and Group stack element types are defined in CPSWorkflow/basicstackelements.py.

2.5.2   Add and change states

States are defined in function getOrderExecutionWorkflowStates in module workflows.orderexec_workflow.py.

You can learn more about the definition of states by looking in the following places:

  • In the ZMI: mysite/portal_workflow/myworkflow/states/mystate
  • In the source: Products/CPSWorkflow/states.py -- In particular, (1) look for the definition of constants (beginning with "STATE_xxx") and (2) look at the the definition of method StateDefinition.setProperties for a list of the keys/properties in the definition of a state.

You can learn most of what you need to know by reading the existing function. In addition, here are a few notes about defining states:

  • The set of states is contained in a dictionary.
  • The keys in this dictionary are the IDs of each state.
  • The values in this dictionary are individual states. Each state is, itself, defined by a dictionary.

Here are the keys and values for a single state dictionary:

  • title -- The title: a string.

  • description -- The description: a string.

  • transitions -- A tuple or list of transitions, for example:

    'transitions': ('accept', 'reject', ),
    
  • state_behaviors -- The behaviors for the state. Possible values are:

    • STATE_BEHAVIOR_PUSH_DELEGATEES
    • STATE_BEHAVIOR_POP_DELEGATEES
    • STATE_BEHAVIOR_WORKFLOW_UP
    • STATE_BEHAVIOR_WORKFLOW_DOWN
    • STATE_BEHAVIOR_WORKFLOW_RESET

    Note that for each behavior type, there is another key that enables you to specify the variables to which the behavior is applied. See below.

  • stackdefs -- A dictionary containing stack definitions. The keys are the workflow variable ID of each variable. The values are the definition of the variable. See below for the definition of a workflow stack.

  • push_on_workflow_variable -- List of variables applicable for behavior STATE_BEHAVIOR_PUSH_DELEGATEES.

  • pop_on_workflow_variable -- List of variables applicable for behavior STATE_BEHAVIOR_POP_DELEGATEES.

  • workflow_up_on_workflow_variable -- List of variables applicable for behavior STATE_BEHAVIOR_WORKFLOW_UP.

  • workflow_down_on_workflow_variable -- List of variables applicable for behavior STATE_BEHAVIOR_WORKFLOW_DOWN.

  • workflow_reset_on_workflow_variable -- List of variables applicable for behavior STATE_BEHAVIOR_WORKFLOW_RESET.

2.5.3   Add and change transitions

Transitions are defined in function getOrderExecutionWorkflowTransitions in module order_execution_workflow.py.

You can learn more about the definition of states by looking in the following places:

  • In the ZMI: mysite/portal_workflow/myworkflow/transitions/mytransition. If you understand how to define a transition in the ZMI, then you should be able to understand the association between the definition there and the constants and keys defined in the source code.
  • In the source: Products/CPSWorkflow/transitions.py -- In particular, (1) look for the definition of constants (beginning with "TRANSITION_xxx") and (2) look at the the definition of method TransitionDefinition.setProperties for a list of the keys/properties in the definition of a transition.

You can learn most of what you need to know by reading the existing function in CPSWorkflowHowTo. In addition, here are a few notes about defining transitions:

  • The set of transitions is contained in a dictionary.
  • The keys in this dictionary are the IDs of each transition.
  • The values in this dictionary are individual transitions. Each transition is, itself, a dictionary.

Here are the keys and values for a single transition dictionary:

  • 'title' -- 'Reviewer rejects publishing',

  • 'description' --

  • 'new_state_id' -- '',

  • 'trigger_type' -- One of:

    • TRIGGER_AUTOMATIC
    • TRIGGER_USER_ACTION
    • TRIGGER_WORKFLOW_METHOD
  • 'transition_behavior' -- A tuple containing one or more of transition flags. These correspond to the flags shown in the properties view of a transition in the ZMI. This tuple can contain the following flags:

    • TRANSITION_ALLOWSUB_CREATE
    • TRANSITION_ALLOWSUB_DELETE
    • TRANSITION_ALLOWSUB_MOVE
    • TRANSITION_ALLOWSUB_COPY
    • TRANSITION_ALLOWSUB_PUBLISHING
    • TRANSITION_ALLOWSUB_CHECKOUT
    • TRANSITION_INITIAL_CREATE
    • TRANSITION_INITIAL_MOVE
    • TRANSITION_INITIAL_COPY
    • TRANSITION_INITIAL_PUBLISHING
    • TRANSITION_INITIAL_CHECKOUT
    • TRANSITION_ALLOW_CHECKIN
    • TRANSITION_BEHAVIOR_DELETE
    • TRANSITION_BEHAVIOR_MOVE
    • TRANSITION_BEHAVIOR_COPY
    • TRANSITION_BEHAVIOR_PUBLISHING
    • TRANSITION_BEHAVIOR_CHECKOUT
    • TRANSITION_BEHAVIOR_CHECKIN
    • TRANSITION_BEHAVIOR_FREEZE
    • TRANSITION_BEHAVIOR_MERGE
    • TRANSITION_BEHAVIOR_PUSH_DELEGATEES
    • TRANSITION_BEHAVIOR_POP_DELEGATEES
    • TRANSITION_BEHAVIOR_WORKFLOW_UP
    • TRANSITION_BEHAVIOR_WORKFLOW_DOWN
    • TRANSITION_BEHAVIOR_WORKFLOW_RESET
  • clone_allowed_transitions -- ???

  • checkout_allowed_initial_transitions --

  • checkin_allowed_transitions --

  • push_on_workflow_variable -- A list/tuple of variables (strings).

  • pop_on_workflow_variable -- A list/tuple of variables (strings).

  • workflow_up_on_workflow_variable -- A list/tuple of variables (strings).

  • workflow_down_on_workflow_variable -- A list/tuple of variables (strings).

  • workflow_reset_on_workflow_variable -- A list/tuple of variables (strings).

  • 'actbox_name' -- Name of the action in the action box.

  • 'actbox_category' -- Usually 'workflow',

  • 'actbox_url' -- The URL of the form file. Examples are:

    'actbox_url': '%(content_url)s/content_move_up_delegatees_form?current_var_id=Reviewers',
    '%(content_url)s/content_reject_form',
    
  • 'script_name' -- Script (before).

  • 'after_script_name' -- Script (after).

  • 'props' -- A dictionary. The keys are:

    • 'guard_roles' -- The list of roles that are allowed to cause this transition. The value might look something like this:

      edit_roles = 'Manager; SectionManager; SectionReviewer; '
      
    • 'guard_expr' -- A procedure which must return a true value in order for the transition to be performed. Here is an example:

      "python:user.has_role(('Manager', 'SectionManager', 'SectionReviewer'), here) or " \
      "user.has_role(('SectionIntermediateReviewer',), here) and " \
      "here.portal_workflow.canManageStack(here, 'Reviewers')"
      

3   Help from ZMIntrospection

It may be helpful to "introspect" the states, transitions, etc. in existing workflows. To do so, install the ZMIntrospection product. I did this by doing the following from the command line from within the Products directory of my Zope instance:

svn co http://svn.nuxeo.org/pub/ZMIntrospection/trunk ZMIntrospection

Then restarting Zope. Now, in the ZMI, click on a state or transition within a workflow and then click on the "Introspection" tab.

For example, when I look at the the pending state in the cps_validation_wf from the CPSWorkflowHowTo (/cps1/portal_workflow/cps_validation_wf/states/pending), then click on the "Introspection" tab, I see the following:

{'Reviewers': <HierarchicalStackDefinition at 0xb3b392ac>,
    '__ac_local_roles__': {'dave': ['Owner']},
    '_objects': ({'id': 'Reviewers',
                  'meta_type': 'Hierarchical Stack Definition'},),
    'description': '',
    'id': 'pending',
    'permission_roles': {
        'Modify portal content': ('Manager',
            'SectionManager', 
            'SectionReviewer', 
            'SectionIntermediateReviewer'),
        'View': ('Manager',
            'SectionManager',
            'SectionReviewer',
            'SectionIntermediateReviewer')
    },
    'pop_on_workflow_variable': ('Reviewers',),
    'push_on_workflow_variable': ('Reviewers',),
    'state_behaviors': (101, 102, 104, 103),
    'title': 'Waiting for reviewer',
    'transitions': ('accept',
                    'reject',
                    'manage_delegatees',
                    'move_up_delegatees',
                    'move_down_delegatees'),
    'workflow_down_on_workflow_variable': ('Reviewers',),
    'workflow_up_on_workflow_variable': ('Reviewers',)
}

(I've done a bit of reformatting for readability of the above.)

Here are a few notes:

4   Define your own workflow

Hopefully, you now know enough to be able to define your own workflow. You might want to follow the instructions in the CPSWorkflowHowTo, which uses the ZMI, but create those definitions in Python source code, as described above, instead.

So, how can you go about designing a workflow. Let's assume that your business process is clearly defined and understood. Then, try following these steps:

  1. Create a state-transition diagram. Create a directed graph (a digraph) that describes your workflow. Use paper and pencil, or consider using a diagram editing tool such as Dia.
  1. Describe each of the states and transitions in your diagram. The information in the previous sections should help you figure out how to encode your stacks, roles, states, and transitions in Python source code for CPSWorkflow. Here are a few things you may want in your annotations:
    • States:
      • Name/ID
      • Title
      • Description
      • Transitions -- Your diagram should make these clear. You will want to give each transition a name/ID.
      • State behavior flags
    • Transitions:
      • Name/ID
      • Title
      • Description
      • Transition type: (1) Automatic, (2) Initiated by user action, (3) Initiated by WorkflowMethod.
      • Transition flags
      • Stack workflow transition flags
      • Script (before)
      • Script (after)
      • Display in actions box: (1) Name (formatted); (2) URL (formatted); (3) Category.
  2. Code your workflow in Python:
    • Create a state for each bubble/node in your diagram -- See Add and change states for help with how to do this.
    • Create a transition for each arrow (directed edge) in your diagram -- See Add and change transitions for help with how to do this.