| | 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 | |