Version 4 (modified by Fran Boon, 9 years ago) ( diff )



S3Workflow Tutorial

This tutorial is on S3Workflow and will briefly explain how workflows works and created in Sahana Eden.

Starting a workflow

Starting a workflow in Sahana Eden is very easy.
To start a workflow just append ?wf_id=workflowname where workflowname will be name of the workflow.
Each workflow has a unique name.
When you start a workflow a special uid get appended to the URL this uid is used to identify the current workflow.

Eg -


here ‘a123b-asdc23’ is a special uid that identify current workflow.

Note - When we start workflow it enters the workflow hook in and then everything is handed over to S3Workflow class and then the request get processed normally.

this is how the request get processed during a workflow - flow diagram

Defining Workflow

All the workflows are defined in -


(where current_temp is the current template.)

This file contains all the workflow under a class S3WorkflowConfig.

An small workflow example defnation can be-

N = S3Workflow
Exit = S3WorkflowExitNode

def reqmanagement(self):
    return N(“first node”).handle(next_status = “create request”
                                  controller = “req”,
                                  function = “req”,
                                  args = “create”) & \
           N(“first node”).handle(next_status = “add items to req 1”
                                  controller = "req",
                                  function = "req",
                                  args = [1,"req_items"],) & \
           N(“first node”).handle(next_status = “view reqs”
                                  controller = "req",
                                  function = "req",

This will create a basic three steps workflow with name reqmanagement and having three steps as follows -

  • Create request
  • Add item to a particular req
  • view requests

Next section will try to explain how to make complex workflows with more configuration options.

Options to Configure Workflows

Each node is defined using a S3WorkflowNode class.
We provide different option to configure each node.
for making things simple let us take

N = S3WorkflowNode

Defining nodes

N(“first node”)

This defines a node with status “first node”

N(“first node”).handle(next_status = “second node”)

handle is used to add configuration options to each node.
above code snippet defines a node with status “first node”
and state that one possible next status that can be achived will be “first node”.

But Now the question is how to achieve this next status?

For that we can define actions in handle.

N(“first node”).handle(next_status = “create request”,
     controller = “req”,
     function = “req”,
     args = “create”)

This code snippet defines a node with status “first node” and whose next status is “second node”
which can be achieved by clicking on eden/req/req/create action.

These actions are defined the same way we define our a url using URL() function.

All arguments that handle accepts are -

  • Controller - used to define controller.
  • Function - used to define function.
  • args - used to define args.
  • next_status - used to define next status.
  • http - used to define the http for the action. eg. get / post
  • action_text - text to display on the action button (Default next_status)

All arguments that S3WorkflowNode class accepts are-

  • status - current status of the node.
  • postp - used to define postp for the node.
  • prep - used to define prep for the node.
  • display_text - used to define some information text in the middle of the node.

You can define multiple handle to each node.

Connecting Nodes

Now let us point above node to a new node.
In the above example while defining first node we defined next_status as “create request”.
Now we will create a node with status “create node” which will create request for us. And then we will connect them.

N(“first node”).handle(next_status = “create request”,
     controller = “req”,
     function = “req”,
     args = “create”) & \
N(“create request”).handle(next_status = “add items”,
     controller = “req”,
     function = “req”,
     args = [1, “req_item”])

when we will start this workflow, the status will be "first node"
So to achieve “create request” node from “first node” user will have to click on “req/req/create”
and then to achieve “add items” from “create request” node user will have to click on “req/req/1/req_items” and so on.

(Action button to navigate from one node to another get generated on the page using actions defined in handle.)


In above example we connected nodes together.
Now let’s see how to create branches or choice nodes.

This can be achieved by using multiple handles for each node.

N(“create req”).handle(next_status = “add item”,
                       controller = “req”,
                       function = “req”,
                        args = [1, “req_item”]) \
               .handle(next_status = “manage commitment”,
                       controller = “req”,
                       function = “req”,
                       args = [1, “commit”] )

The above code snippet say when at “create req” user can click on “req/req/1/req_items” to change the status to “add item”
OR user can click on “req/req/1/commit” to change the workflow status to “manage commitments”

Receiving branched Nodes

Now lets receive these branched nodes.

To merge the branches and point them to one node do this.

N(“create req”).handle(next_status = “add item”,
                       controller = “req”,
                       function = “req”,
                       args = [1, “req_item”]) \
               .handle(next_status = “manage commitment”,
                       controller = “req”,
                       function = “req”,
                       args = [1, “commit”] ) & \
( N(“add items”).handle(next_status = “view req ”)  |  
  N(“manage commitments”).handle(next_status = “view req”) )

Here “|” is the “or” operator and “&” is “and operator”

Loop node

We can make loop nodes which will keep on looping until special actions is pressed.


N(“add items”).handle(next_status = “add items”,
                      controller = “req”,
                      function = “req”,
                      args = [1,“req_items”]) \
              .handle(next_node = “view req”,
                      controller = “req”,
                      function = “req”)

Adding Prep and Postp to Node

Just like we define Prep and Postp for controller we can define one for each node too.

def postp(r, output):
    return output

now add this postp to node.

N(“create request”,   postp = postp).handle(next_status = “view req”,
                                            controller = “req”,
                                            function = “req”)

Customising Workflow header

Workflow Header is used to inject special buttons and progress bar during the workflow. Developers have full permission to customise these. Customising the workflow header can be done by adding wheader in controllers, like we add rheader. So there can be different wheader for different nodes

We have Default wheader defined in modules/s3/


  • When we specify action like org/index they redirect to URL(f = organization) and don’t catch the workflow id. so we have to be careful with this till this issue is not solved
Note: See TracWiki for help on using the wiki.