| 1 | = S3Workflow Tutorial = |
| 2 | [[TOC]] |
| 3 | |
| 4 | This tutorial is on S3Workflow and will briefly explain how workflows works and created in Sahana Eden. |
| 5 | |
| 6 | == Starting a workflow == |
| 7 | |
| 8 | Starting a workflow in Sahana Eden is very easy.[[br]] |
| 9 | To start a workflow just append {{{?wf_id=workflowname}}} where workflowname will be name of the workflow.[[br]] |
| 10 | Each workflow has a unique name.[[br]] |
| 11 | When you start a workflow a special uid get appended to the URL this uid is used to identify the current workflow. |
| 12 | |
| 13 | Eg - |
| 14 | |
| 15 | {{{ |
| 16 | eden/req/req?wf_id=reqmanagement:a123b-asd23 |
| 17 | }}} |
| 18 | |
| 19 | |
| 20 | here ‘a123b-asdc23’ is a special uid that identify current workflow. |
| 21 | |
| 22 | '''Note - ''' When we start workflow it enters the workflow hook in s3rest.py and then everything is handed over to S3Workflow class and then the request get processed normally. |
| 23 | |
| 24 | |
| 25 | == Defining Workflow == |
| 26 | |
| 27 | All the workflows are defined in - |
| 28 | |
| 29 | {{{ |
| 30 | private/templates/current_temp/workflow.py |
| 31 | }}} |
| 32 | |
| 33 | (where current_temp is the current template.)[[br]] |
| 34 | |
| 35 | This file contains all the workflow under a class |
| 36 | S3WorkflowConfig. [[br]] |
| 37 | |
| 38 | An small workflow example defnation can be- |
| 39 | |
| 40 | {{{ |
| 41 | N = S3Workflow |
| 42 | Exit = S3WorkflowExitNode |
| 43 | |
| 44 | def reqmanagement(self): |
| 45 | return N(“first node”).handle(next_status = “create request” |
| 46 | controller = “req”, |
| 47 | function = “req”, |
| 48 | args = “create”) & \ |
| 49 | N(“first node”).handle(next_status = “add items to req 1” |
| 50 | controller = "req", |
| 51 | function = "req", |
| 52 | args = [1,"req_items"],) & \ |
| 53 | N(“first node”).handle(next_status = “view reqs” |
| 54 | controller = "req", |
| 55 | function = "req", |
| 56 | ) |
| 57 | |
| 58 | }}} |
| 59 | |
| 60 | This will create a basic three steps workflow with name reqmanagement and having three steps as follows - |
| 61 | |
| 62 | * Create request |
| 63 | * Add item to a particular req |
| 64 | * view requests |
| 65 | |
| 66 | Next section will try to explain how to make complex workflows with more configuration options. |
| 67 | |
| 68 | == Options to Configure Workflows == |
| 69 | |
| 70 | Each node is defined using a S3WorkflowNode class.[[br]] |
| 71 | We provide different option to configure each node. [[br]] |
| 72 | for making things simple let us take [[br]] |
| 73 | |
| 74 | {{{ |
| 75 | N = S3WorkflowNode |
| 76 | }}} |
| 77 | |
| 78 | === Defining nodes === |
| 79 | |
| 80 | {{{ |
| 81 | N(“first node”) |
| 82 | }}} |
| 83 | |
| 84 | This defines a node with status “first node” |
| 85 | |
| 86 | {{{ |
| 87 | N(“first node”).handle(next_status = “second node”) |
| 88 | }}} |
| 89 | |
| 90 | handle is used to add configuration options to each node.[[br]] |
| 91 | above code snippet defines a node with status “first node”[[br]] |
| 92 | and state that one possible next status that can be achived will be “first node”.[[br]] |
| 93 | |
| 94 | But Now the question is how to achieve this next status? [[br]] |
| 95 | |
| 96 | For that we can define actions in handle.[[br]] |
| 97 | |
| 98 | {{{ |
| 99 | N(“first node”).handle(next_status = “create request”, |
| 100 | controller = “req”, |
| 101 | function = “req”, |
| 102 | args = “create”) |
| 103 | }}} |
| 104 | |
| 105 | This code snippet defines a node with status “first node” and whose next status is “second node” [[br]] |
| 106 | which can be achieved by clicking on eden/req/req/create action. |
| 107 | |
| 108 | These actions are defined the same way we define our a url using URL() function. |
| 109 | |
| 110 | '''All arguments that handle accepts are -''' |
| 111 | |
| 112 | * Controller - used to define controller. |
| 113 | * Function - used to define function. |
| 114 | * args - used to define args. |
| 115 | * next_status - used to define next status. |
| 116 | * http - used to define the http for the action. eg. get / post |
| 117 | * action_text - text to display on the action button (Default next_status) |
| 118 | |
| 119 | '''All arguments that S3WorkflowNode class accepts are- ''' |
| 120 | |
| 121 | * status - current status of the node. |
| 122 | * postp - used to define postp for the node. |
| 123 | * prep - used to define prep for the node. |
| 124 | * display_text - used to define some information text in the middle of the node. |
| 125 | |
| 126 | You can define multiple handle to each node. |
| 127 | |
| 128 | === Connecting Nodes === |
| 129 | |
| 130 | Now let us point above node to a new node. [[br]] |
| 131 | In the above example while defining first node we defined next_status as “create request”. [[br]] |
| 132 | Now we will create a node with status “create node” which will create request for us. |
| 133 | And then we will connect them. |
| 134 | |
| 135 | {{{ |
| 136 | N(“first node”).handle(next_status = “create request”, |
| 137 | controller = “req”, |
| 138 | function = “req”, |
| 139 | args = “create”) & \ |
| 140 | N(“create request”).handle(next_status = “add items”, |
| 141 | controller = “req”, |
| 142 | function = “req”, |
| 143 | args = [1, “req_item”]) |
| 144 | }}} |
| 145 | |
| 146 | when we will start this workflow, the status will be "first node" [[br]] |
| 147 | So to achieve “create request” node from “first node” user will have to click on “req/req/create” [[br]] |
| 148 | and then to achieve “add items” from “create request” node user will have to click on “req/req/1/req_items” and so on. |
| 149 | |
| 150 | (Action button to navigate from one node to another get generated on the page using actions defined in handle.) |
| 151 | |
| 152 | |
| 153 | === Branching === |
| 154 | |
| 155 | In above example we connected nodes together.[[br]] |
| 156 | Now let’s see how to create branches or choice nodes.[[br]] |
| 157 | |
| 158 | This can be achieved by using multiple handles for each node. |
| 159 | |
| 160 | {{{ |
| 161 | |
| 162 | N(“create req”).handle(next_status = “add item”, |
| 163 | controller = “req”, |
| 164 | function = “req”, |
| 165 | args = [1, “req_item”]) \ |
| 166 | .handle(next_status = “manage commitment”, |
| 167 | controller = “req”, |
| 168 | function = “req”, |
| 169 | args = [1, “commit”] ) |
| 170 | |
| 171 | }}} |
| 172 | |
| 173 | |
| 174 | |
| 175 | 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” [[br]] |
| 176 | '''OR''' user can click on “req/req/1/commit” to change the workflow status to “manage commitments” [[br]] |
| 177 | |
| 178 | === Receiving branched Nodes === |
| 179 | |
| 180 | Now lets receive these branched nodes. |
| 181 | |
| 182 | To merge the branches and point them to one node do this. |
| 183 | |
| 184 | {{{ |
| 185 | N(“create req”).handle(next_status = “add item”, |
| 186 | controller = “req”, |
| 187 | function = “req”, |
| 188 | args = [1, “req_item”]) \ |
| 189 | .handle(next_status = “manage commitment”, |
| 190 | controller = “req”, |
| 191 | function = “req”, |
| 192 | args = [1, “commit”] ) & \ |
| 193 | ( N(“add items”).handle(next_status = “view req ”) | |
| 194 | N(“manage commitments”).handle(next_status = “view req”) ) |
| 195 | }}} |
| 196 | |
| 197 | Here “|” is the “or” operator and “&” is “and operator” |
| 198 | |
| 199 | === Loop node === |
| 200 | |
| 201 | We can make loop nodes which will keep on looping until special actions is pressed. |
| 202 | |
| 203 | Eg- |
| 204 | |
| 205 | {{{ |
| 206 | N(“add items”).handle(next_status = “add items”, |
| 207 | controller = “req”, |
| 208 | function = “req”, |
| 209 | args = [1,“req_items”]) \ |
| 210 | .handle(next_node = “view req”, |
| 211 | controller = “req”, |
| 212 | function = “req”) |
| 213 | |
| 214 | }}} |
| 215 | |
| 216 | === Adding Prep and Postp to Node === |
| 217 | |
| 218 | Just like we define Prep and Postp for controller we can define one for each node too. |
| 219 | |
| 220 | {{{ |
| 221 | def postp(r, output): |
| 222 | … |
| 223 | … |
| 224 | return output |
| 225 | }}} |
| 226 | |
| 227 | now add this postp to node. |
| 228 | |
| 229 | {{{ |
| 230 | N(“create request”, postp = postp).handle(next_status = “view req”, |
| 231 | controller = “req”, |
| 232 | function = “req”) |
| 233 | }}} |
| 234 | |
| 235 | == Customising Workflow header == |
| 236 | |
| 237 | Workflow Header is used to inject special buttons and progress bar during the workflow. |
| 238 | Developers have full permission to customise these. |
| 239 | Customising the workflow header can be done by adding wheader in controllers, like we add rheader. |
| 240 | So there can be different wheader for different nodes |
| 241 | |
| 242 | We have Default wheader defined in modules/s3/s3workflow.py |
| 243 | |
| 244 | === Limitations === |
| 245 | |
| 246 | * 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 |
| 247 | |
| 248 | |