mirror of
https://github.com/status-im/spiff-arena.git
synced 2025-01-31 04:05:01 +00:00
235 lines
7.9 KiB
ReStructuredText
235 lines
7.9 KiB
ReStructuredText
Tasks
|
|
=====
|
|
|
|
BPMN Model
|
|
----------
|
|
|
|
In this example, we'll model a customer selecting a product to illustrate the basic task types that
|
|
can be used with SpiffWorkflow.
|
|
|
|
We'll be using the following files from `spiff-example-cli <https://github.com/sartography/spiff-example-cli>`_:
|
|
|
|
- `task_types <https://github.com/sartography/spiff-example-cli/blob/main/bpmn/tutorial/task_types.bpmn>`_ workflow
|
|
- `product_prices <https://github.com/sartography/spiff-example-cli/blob/main/bpmn/tutorial/product_prices.dmn>`_ DMN table
|
|
|
|
User Tasks
|
|
^^^^^^^^^^
|
|
|
|
User Tasks would typically be used in the case where the task would be completed from within the
|
|
application. Our User tasks present forms that collect data from users.
|
|
|
|
We'll ask our hypothetical user to choose a product and quantity.
|
|
|
|
.. figure:: figures/tasks/user_task.png
|
|
:scale: 30%
|
|
:align: center
|
|
|
|
User Task
|
|
|
|
We can use the form builder to create the form.
|
|
|
|
.. figure:: figures/tasks/user_task_form.png
|
|
:scale: 30%
|
|
:align: center
|
|
|
|
User Task form
|
|
|
|
See the `Handling User Tasks`_ section for a discussion of sample code.
|
|
|
|
We have also retained some limited support for the now deprecated
|
|
camunda forms, which you can read about in our Camunda Specific section on :doc:`camunda/tasks`.
|
|
|
|
|
|
Business Rule Tasks
|
|
^^^^^^^^^^^^^^^^^^^
|
|
|
|
In our Business Rule Task, we'll use a DMN table to look up the price of the
|
|
product the user chose.
|
|
|
|
We'll need to create a DMN table.
|
|
|
|
What is DMN?
|
|
++++++++++++
|
|
|
|
Decision Model and Notation (DMN) is a standard for business decision
|
|
modeling. DMN allows modelers to separate decision logic from process logic
|
|
and maintain it in a table format. DMN is linked into BPMN with a *decision
|
|
task*.
|
|
|
|
With DMN, business analysts can model the rules that lead to a decision
|
|
in an easy to read table. Those tables can be executed directly by SpiffWorkflow.
|
|
|
|
This minimizes the risk of misunderstandings between business analysts and
|
|
developers, and allows rapid changes in production.
|
|
|
|
BPMN includes a decision task that refers to the decision table. The outcome of
|
|
the decision lookup allows the next gateway or activity to route the flow.
|
|
|
|
Our Business Rule Task will make use of a DMN table.
|
|
|
|
.. figure:: figures/tasks/dmn_table.png
|
|
:scale: 30%
|
|
:align: center
|
|
|
|
DMN Table
|
|
|
|
.. note::
|
|
We add quote marks around the product names in the table. Spiff will
|
|
create an expression based on the exact contents of the table, so if
|
|
the quotes are omitted, the content will be interpreted as a variable
|
|
rather than a string.
|
|
|
|
Then we'll refer to this table in the task configuration.
|
|
|
|
.. figure:: figures/tasks/business_rule_task.png
|
|
:scale: 30%
|
|
:align: center
|
|
|
|
Business Rule Task configuration
|
|
|
|
Script Tasks
|
|
^^^^^^^^^^^^
|
|
|
|
The total order cost will need to be calculated on the fly. We can do this in
|
|
a Script Task. We'll configure the task with some simple Python code.
|
|
|
|
.. figure:: figures/tasks/script_task.png
|
|
:scale: 30%
|
|
:align: center
|
|
|
|
Script Task configuration
|
|
|
|
The code in the script will have access to the task data, so variables that
|
|
have been defined previously will be available to it.
|
|
|
|
Manual Tasks
|
|
^^^^^^^^^^^^
|
|
|
|
Our final task type is a Manual Task. Manual Tasks represent work that occures
|
|
outside of SpiffWorkflow's control. Say that you need to include a step in a
|
|
process where the participant needs to stand up, walk over to the coffee maker,
|
|
and poor the cup of coffee. Manual Tasks pause the process, and wait for
|
|
confirmation that the step was completed.
|
|
|
|
Text that will be displayed to the user is added in the "Instructions" panel.
|
|
|
|
.. figure:: figures/tasks/manual_task.png
|
|
:scale: 30%
|
|
:align: center
|
|
|
|
Manual Task
|
|
|
|
Spiff's manual tasks may contain references to data inside the workflow. We have used
|
|
`Jinja <https://jinja.palletsprojects.com/en/3.0.x/>`_, but Spiff is set up in a way that
|
|
you could use any templating library you want, as well as Markdown formatting directives
|
|
(we won't implement those here though, because it doesn't make sense for a command
|
|
line app).
|
|
|
|
.. figure:: figures/tasks/manual_task_instructions.png
|
|
:scale: 30%
|
|
:align: center
|
|
|
|
Editing Instructions
|
|
|
|
See the `Handling Manual Tasks`_ section for a discussion of sample code.
|
|
|
|
For information about how Spiff handles Manual Tasks created with Camunda please
|
|
refer to the Camunda Specific section on :doc:`camunda/tasks`.
|
|
|
|
Running The Model
|
|
^^^^^^^^^^^^^^^^^
|
|
|
|
If you have set up our example repository, this model can be run with the following command:
|
|
|
|
.. code-block:: console
|
|
|
|
./spiff-bpmn-runner.py -p order_product -d bpmn/tutorial/product_prices.dmn -b bpmn/tutorial/task_types.bpmn
|
|
|
|
Example Application Code
|
|
------------------------
|
|
|
|
Handling User Tasks
|
|
^^^^^^^^^^^^^^^^^^^
|
|
|
|
We will need to provide a way to display the form data and collect the user's
|
|
responses.
|
|
|
|
.. code:: python
|
|
|
|
filename = task.task_spec.extensions['properties']['formJsonSchemaFilename']
|
|
schema = json.load(open(os.path.join(forms_dir, filename)))
|
|
for field, config in schema['properties'].items():
|
|
if 'oneOf' in config:
|
|
option_map = dict([ (v['title'], v['const']) for v in config['oneOf'] ])
|
|
options = "(" + ', '.join(option_map) + ")"
|
|
prompt = f"{field} {options} "
|
|
option = input(prompt)
|
|
while option not in option_map:
|
|
print(f'Invalid selection!')
|
|
option = input(prompt)
|
|
response = option_map[option]
|
|
else:
|
|
response = input(f"{config['title']} ")
|
|
if config['type'] == 'integer':
|
|
response = int(response)
|
|
task.data[field] = response
|
|
|
|
SpiffWorkflow uses JSON Schema to represent forms, specifically
|
|
`react-jsonschema-form <https://react-jsonschema-form.readthedocs.io/en/latest/>`_.
|
|
Our forms are really intended to be displayed in a browser, and attempting to handle them in a command
|
|
line appliction is a little awkward. The form specifications can be quite complex.
|
|
|
|
This simple implementation will present a list of options for simple enumerated fields and simply
|
|
directly stores whatever the user enters otherwise, with integer conversions if the field is so
|
|
specified. This is robust enough to collect enough information from a user to make it through our example.
|
|
|
|
SpiffWorkflow provides a mechanism for you to provide your own form specification and leaves it up to you
|
|
to decide how to present it.
|
|
|
|
|
|
Handling Business Rule Tasks
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
We do not need to do any special configuration to handle these Business Rule Tasks. SpiffWorkflow does it all for us.
|
|
|
|
Handling Script Tasks
|
|
^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
We do not need to do any special configuration to handle Script Tasks, although it
|
|
is possible to implement a custom script engine. We demonstrate that process in
|
|
Custom Script Engines section :doc:`advanced` features. However, the default script
|
|
engine will be adequate for now.
|
|
|
|
Handling Manual Tasks
|
|
^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Our code for manual tasks simply asks the user to confirm that the task has been
|
|
completed.
|
|
|
|
.. code:: python
|
|
|
|
def complete_manual_task(task):
|
|
display_instructions(task)
|
|
input("Press any key to mark task complete")
|
|
|
|
:code:`display_instructions` handles presenting the task to the user.
|
|
|
|
.. code:: python
|
|
|
|
def display_instructions(task):
|
|
text = task.task_spec.extensions.get('instructionsForEndUser')
|
|
print(f'\n{task.task_spec.bpmn_name}')
|
|
if text is not None:
|
|
template = Template(text)
|
|
print(template.render(task.data))
|
|
|
|
The template string can be obtained from :code:`task.task_spec.extensions.get('instructionsForEndUser')`.
|
|
|
|
As noted above, our template class comes from Jinja. We render the template
|
|
using the task data, which is just a dictionary.
|
|
|
|
.. note::
|
|
|
|
Most of Spiff's task specifications contain this extension, not just Manual Tasks. We also use it to display
|
|
information along with forms, and about certain events.
|