mirror of
https://github.com/status-im/spiff-arena.git
synced 2025-02-12 01:36:58 +00:00
105 lines
3.1 KiB
ReStructuredText
105 lines
3.1 KiB
ReStructuredText
Tasks
|
|
=====
|
|
|
|
User Tasks
|
|
----------
|
|
|
|
Creating a User Task
|
|
^^^^^^^^^^^^^^^^^^^^
|
|
|
|
When you click on a user task in the BPMN modeler, the Properties Panel includes a form tab. Use this
|
|
tab to build your questions.
|
|
|
|
The following example shows how a form might be set up in Camumda.
|
|
|
|
.. figure:: figures/user_task.png
|
|
:scale: 30%
|
|
:align: center
|
|
|
|
User Task configuration
|
|
|
|
|
|
Manual Tasks
|
|
------------
|
|
|
|
Creating a Manual Task
|
|
^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
We can use the BPMN element Documentation field to display more information about the context of the item.
|
|
|
|
Spiff is set up in a way that you could use any templating library you want, but we have used
|
|
`Jinja <https://jinja.palletsprojects.com/en/3.0.x/>`_.
|
|
|
|
In this example, we'll present an order summary to our customer.
|
|
|
|
.. figure:: figures/documentation.png
|
|
:scale: 30%
|
|
:align: center
|
|
|
|
Element Documentation
|
|
|
|
Running The Model
|
|
-----------------
|
|
|
|
If you have set up our example repository, this model can be run with the
|
|
following command:
|
|
|
|
.. code-block:: console
|
|
|
|
./camunda-bpmn-runner.py -p order_product -d bpmn/tutorial/product_prices.dmn -b bpmn/camunda/task_types.bpmn
|
|
|
|
Example Application Code
|
|
------------------------
|
|
|
|
Handling the User Task
|
|
^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
.. code:: python
|
|
|
|
dct = {}
|
|
for field in task.task_spec.form.fields:
|
|
if isinstance(field, EnumFormField):
|
|
option_map = dict([ (opt.name, opt.id) for opt in field.options ])
|
|
options = "(" + ', '.join(option_map) + ")"
|
|
prompt = f"{field.label} {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"{field.label} ")
|
|
if field.type == "long":
|
|
response = int(response)
|
|
update_data(dct, field.id, response)
|
|
DeepMerge.merge(task.data, dct)
|
|
|
|
The list of form fields for a task is stored in :code:`task.task_spec.form_fields`.
|
|
|
|
For Enumerated fields, we want to get the possible options and present them to the
|
|
user. The variable names of the fields were stored in :code:`field.id`, but since
|
|
we set labels for each of the fields, we'd like to display those instead, and map
|
|
the user's selection back to the variable name.
|
|
|
|
For other fields, we'll just store whatever the user enters, although in the case
|
|
where the data type was specified to be a :code:`long`, we'll convert it to a
|
|
number.
|
|
|
|
Finally, we need to explicitly store the user-provided response in a variable
|
|
with the expected name with :code:`update_data(dct, field.id, response)` and merge
|
|
the newly collected data into our task data with :code:`DeepMerge.merge(task.data, dct)`.
|
|
|
|
Our :code:`update_data` function handles "dot notation" in field names, which creates
|
|
nested dictionaries based on the path components.
|
|
|
|
.. code:: python
|
|
|
|
def update_data(dct, name, value):
|
|
path = name.split('.')
|
|
current = dct
|
|
for component in path[:-1]:
|
|
if component not in current:
|
|
current[component] = {}
|
|
current = current[component]
|
|
current[path[-1]] = value
|