Squashed 'SpiffWorkflow/' changes from 0e61be85c..11e4b4f96
11e4b4f96 fix two incorrectly names attributes in node parser abec918a8 Merge pull request #291 from rachfop/fix-grammar a597f9ce9 Fixes grammar, typos, and spellings 00ffaf067 Assure that when something goes wrong calling a service task that we get as much good information about the problem as possible. c044b5646 Fix that dreadful unknown "KeyError" exception that was cropping up. Adding a bit of detail to the spiffworkflow exceptions when a duplicate process model is found. Disable the submit button on tasks after you click submit (avoid the double click and give users a better experience) git-subtree-dir: SpiffWorkflow git-subtree-split: 11e4b4f96f03a036bd29632f1560e347a4e69aae
This commit is contained in:
parent
c237e218b2
commit
fec5934d38
|
@ -7,7 +7,7 @@ Filtering Tasks
|
||||||
In our earlier example, all we did was check the lane a task was in and display
|
In our earlier example, all we did was check the lane a task was in and display
|
||||||
it along with the task name and state.
|
it along with the task name and state.
|
||||||
|
|
||||||
Lets take a look at a sample workflow with lanes:
|
Let's take a look at a sample workflow with lanes:
|
||||||
|
|
||||||
.. figure:: figures/lanes.png
|
.. figure:: figures/lanes.png
|
||||||
:scale: 30%
|
:scale: 30%
|
||||||
|
@ -15,7 +15,7 @@ Lets take a look at a sample workflow with lanes:
|
||||||
|
|
||||||
Workflow with lanes
|
Workflow with lanes
|
||||||
|
|
||||||
To get all of the tasks that are ready for the 'Customer' workflow, we could
|
To get all the tasks that are ready for the 'Customer' workflow, we could
|
||||||
specify the lane when retrieving ready user tasks:
|
specify the lane when retrieving ready user tasks:
|
||||||
|
|
||||||
.. code:: python
|
.. code:: python
|
||||||
|
@ -50,14 +50,14 @@ Logging
|
||||||
Spiff provides several loggers:
|
Spiff provides several loggers:
|
||||||
- the :code:`spiff` logger, which emits messages when a workflow is initialized and when tasks change state
|
- the :code:`spiff` logger, which emits messages when a workflow is initialized and when tasks change state
|
||||||
- the :code:`spiff.metrics` logger, which emits messages containing the elapsed duration of tasks
|
- the :code:`spiff.metrics` logger, which emits messages containing the elapsed duration of tasks
|
||||||
- the :code:`spiff.data` logger, which emits message when task or workflow data is updated.
|
- the :code:`spiff.data` logger, which emits a message when task or workflow data is updated.
|
||||||
|
|
||||||
Log level :code:`INFO` will provide reasonably detailed information about state changes.
|
Log level :code:`INFO` will provide reasonably detailed information about state changes.
|
||||||
|
|
||||||
As usual, log level :code:`DEBUG` will probably provide more logs than you really want
|
As usual, log level :code:`DEBUG` will probably provide more logs than you really want
|
||||||
to see, but the logs will contain the task and task internal data.
|
to see, but the logs will contain the task and task internal data.
|
||||||
|
|
||||||
Data can be included at any level less than :code:`INFO`. In our exmple application,
|
Data can be included at any level less than :code:`INFO`. In our example application,
|
||||||
we define a custom log level
|
we define a custom log level
|
||||||
|
|
||||||
.. code:: python
|
.. code:: python
|
||||||
|
@ -76,7 +76,7 @@ Serialization
|
||||||
|
|
||||||
Serialization Changed in Version 1.1.7.
|
Serialization Changed in Version 1.1.7.
|
||||||
Support for pre-1.1.7 serialization will be dropped in a future release.
|
Support for pre-1.1.7 serialization will be dropped in a future release.
|
||||||
The old serialization method still works but it is deprecated.
|
The old serialization method still works, but it is deprecated.
|
||||||
To migrate your system to the new version, see "Migrating between
|
To migrate your system to the new version, see "Migrating between
|
||||||
serialization versions" below.
|
serialization versions" below.
|
||||||
|
|
||||||
|
@ -131,7 +131,7 @@ To restore the workflow:
|
||||||
with open(args.restore) as state:
|
with open(args.restore) as state:
|
||||||
wf = serializer.deserialize_json(state.read())
|
wf = serializer.deserialize_json(state.read())
|
||||||
|
|
||||||
The workflow serializer is designed to be flexible and modular and as such is a little complicated. It has
|
The workflow serializer is designed to be flexible and modular, and as such is a little complicated. It has
|
||||||
two components:
|
two components:
|
||||||
|
|
||||||
- a workflow spec converter (which handles workflow and task specs)
|
- a workflow spec converter (which handles workflow and task specs)
|
||||||
|
@ -141,7 +141,7 @@ The default workflow spec converter likely to meet your needs, either on its own
|
||||||
:code:`UserTask` and :code:`BusinessRuleTask` in the :code:`camnuda` or :code:`spiff` and :code:`dmn` subpackages
|
:code:`UserTask` and :code:`BusinessRuleTask` in the :code:`camnuda` or :code:`spiff` and :code:`dmn` subpackages
|
||||||
of this library, and all you'll need to do is add them to the list of task converters, as we did above.
|
of this library, and all you'll need to do is add them to the list of task converters, as we did above.
|
||||||
|
|
||||||
However, he default data converter is very simple, adding only JSON-serializable conversions of :code:`datetime`
|
However, the default data converter is very simple, adding only JSON-serializable conversions of :code:`datetime`
|
||||||
and :code:`timedelta` objects (we make these available in our default script engine) and UUIDs. If your
|
and :code:`timedelta` objects (we make these available in our default script engine) and UUIDs. If your
|
||||||
workflow or task data contains objects that are not JSON-serializable, you'll need to extend ours, or extend
|
workflow or task data contains objects that are not JSON-serializable, you'll need to extend ours, or extend
|
||||||
its base class to create one of your own.
|
its base class to create one of your own.
|
||||||
|
@ -245,7 +245,7 @@ The code would then look more like this:
|
||||||
|
|
||||||
Because the serializer is highly customizable, we've made it possible for you to manage your own versions of the
|
Because the serializer is highly customizable, we've made it possible for you to manage your own versions of the
|
||||||
serialization. You can do this by passing a version number into the serializer, which will be embedded in the
|
serialization. You can do this by passing a version number into the serializer, which will be embedded in the
|
||||||
json of all workflows. This allow you to modify the serialization and customize it over time, and still manage
|
json of all workflows. This allows you to modify the serialization and customize it over time, and still manage
|
||||||
the different forms as you make adjustments without leaving people behind.
|
the different forms as you make adjustments without leaving people behind.
|
||||||
|
|
||||||
Versioned Serializer
|
Versioned Serializer
|
||||||
|
@ -273,7 +273,7 @@ security reasons.
|
||||||
and :code:`exec`! If you have security concerns, you should definitely investigate
|
and :code:`exec`! If you have security concerns, you should definitely investigate
|
||||||
replacing the default with your own implementation.
|
replacing the default with your own implementation.
|
||||||
|
|
||||||
We'll cover a simple extension of custom script engine here. There is also an examples of
|
We'll cover a simple extension of custom script engine here. There is also an example of
|
||||||
a similar engine based on `RestrictedPython <https://restrictedpython.readthedocs.io/en/latest/>`_
|
a similar engine based on `RestrictedPython <https://restrictedpython.readthedocs.io/en/latest/>`_
|
||||||
included alongside this example.
|
included alongside this example.
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,7 @@ We'll include examples of all of these types in this section.
|
||||||
Transactions
|
Transactions
|
||||||
^^^^^^^^^^^^
|
^^^^^^^^^^^^
|
||||||
|
|
||||||
We also need to introduce the concept of a Transaction, bceause certain events
|
We also need to introduce the concept of a Transaction because certain events
|
||||||
can only be used in that context. A Transaction is essentially a subprocess, but
|
can only be used in that context. A Transaction is essentially a subprocess, but
|
||||||
it must fully complete before it affects its outer workflow.
|
it must fully complete before it affects its outer workflow.
|
||||||
|
|
||||||
|
@ -147,7 +147,7 @@ this tutorial.
|
||||||
|
|
||||||
We ask the Employee to verify that they were able to retrieve the product; if they
|
We ask the Employee to verify that they were able to retrieve the product; if they
|
||||||
were unable to do so, then we generate an Error End Event, which we will handle
|
were unable to do so, then we generate an Error End Event, which we will handle
|
||||||
with an Interrupting Error Boundary Event (Error events are *always* Interrupting).
|
with an Interrupting Error Boundary Event (Error events are *always* interrupting).
|
||||||
|
|
||||||
If the product is unavailable, our Manager will notify the customer, issue a refund,
|
If the product is unavailable, our Manager will notify the customer, issue a refund,
|
||||||
and cancel the order.
|
and cancel the order.
|
||||||
|
@ -161,7 +161,7 @@ Event, you'll have to use Escalation, because BPMN does not allow Intermediate E
|
||||||
and that Error Events cannot be Non-Interrupting.
|
and that Error Events cannot be Non-Interrupting.
|
||||||
|
|
||||||
In our example, we'll assume that if we failed to ship the product, we can try again later,
|
In our example, we'll assume that if we failed to ship the product, we can try again later,
|
||||||
so we will not end the Subprocess (Escalation events can be either Interrupting or
|
so, we will not end the Subprocess (Escalation events can be either Interrupting or
|
||||||
Non-Interrupting).
|
Non-Interrupting).
|
||||||
|
|
||||||
However, we still want to notify our customer of a delay, so we use a Non-Interrupting
|
However, we still want to notify our customer of a delay, so we use a Non-Interrupting
|
||||||
|
|
|
@ -23,7 +23,7 @@ Exclusive Gateway
|
||||||
Exclusive gateways are used when exactly one alternative can be selected.
|
Exclusive gateways are used when exactly one alternative can be selected.
|
||||||
|
|
||||||
Suppose our products are T-shirts and we offer product C in several colors. After
|
Suppose our products are T-shirts and we offer product C in several colors. After
|
||||||
the user selects a product, we check to see it if is customizable. Our default
|
the user selects a product, we check to see it if is customizable. Our default
|
||||||
branch will be 'Not Customizable', but we'll direct the user to a second form
|
branch will be 'Not Customizable', but we'll direct the user to a second form
|
||||||
if they select 'C'; our condition for choosing this branch is a simple python
|
if they select 'C'; our condition for choosing this branch is a simple python
|
||||||
expression.
|
expression.
|
||||||
|
|
|
@ -28,7 +28,7 @@ selections in a collection.
|
||||||
|
|
||||||
Selecting more than one product
|
Selecting more than one product
|
||||||
|
|
||||||
We'll also need to update our element docmentation to display all products.
|
We'll also need to update our element documentation to display all products.
|
||||||
|
|
||||||
.. figure:: figures/documentation_multi.png
|
.. figure:: figures/documentation_multi.png
|
||||||
:scale: 30%
|
:scale: 30%
|
||||||
|
|
|
@ -17,7 +17,7 @@ instead of the `run.py <https://github.com/sartography/spiff-example-clie/blob/m
|
||||||
Camunda's BPMN editor does not handle data objects in the expected way. You can create data object
|
Camunda's BPMN editor does not handle data objects in the expected way. You can create data object
|
||||||
references, but there is no way to re-use data objects.
|
references, but there is no way to re-use data objects.
|
||||||
|
|
||||||
It also does not support Message Correlations, and the inteface for generating a message payload doesn't work
|
It also does not support Message Correlations, and the interface for generating a message payload doesn't work
|
||||||
well in a Python environment.
|
well in a Python environment.
|
||||||
|
|
||||||
We have extended BPMN.js to correct some of these issues. The examples in this section were created using our
|
We have extended BPMN.js to correct some of these issues. The examples in this section were created using our
|
||||||
|
@ -59,7 +59,7 @@ the 'Enter Payment Info' has been completed.
|
||||||
Configuring Messages
|
Configuring Messages
|
||||||
^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
Messages are handled slightly differently in Spiff Message Events. On an Message Throw Event or Send Task,
|
Messages are handled slightly differently in Spiff Message Events. On a Message Throw Event or Send Task,
|
||||||
we define a payload, which is simply a bit of python code that will be evaluated against the task data and
|
we define a payload, which is simply a bit of python code that will be evaluated against the task data and
|
||||||
sent along with the message. In the corresponding Message Catch Event or Receive Task, we define a
|
sent along with the message. In the corresponding Message Catch Event or Receive Task, we define a
|
||||||
variable name where we'll store the result.
|
variable name where we'll store the result.
|
||||||
|
|
|
@ -58,7 +58,7 @@ will create specs for all executable processes found in every file supplied. Th
|
||||||
the specified process. Both search recursively for subprocesses; the only difference is
|
the specified process. Both search recursively for subprocesses; the only difference is
|
||||||
the latter method limits the search start to the specified process.
|
the latter method limits the search start to the specified process.
|
||||||
|
|
||||||
Our examples are pretty simple and we're not loading any extraneous stuff, so we'll
|
Our examples are pretty simple, and we're not loading any extraneous stuff, so we'll
|
||||||
just always load everything. If your entire workflow is contained in your top-level
|
just always load everything. If your entire workflow is contained in your top-level
|
||||||
process, you can omit the :code:`subprocess` argument, but if your workflow contains
|
process, you can omit the :code:`subprocess` argument, but if your workflow contains
|
||||||
call activities, you'll need to use one of these methods to find the models for any
|
call activities, you'll need to use one of these methods to find the models for any
|
||||||
|
@ -91,7 +91,7 @@ We create a mapping of task type to handler, which we'll pass to our workflow ru
|
||||||
|
|
||||||
This might not be a step you would need to do in an application you build, since
|
This might not be a step you would need to do in an application you build, since
|
||||||
you would likely have only one set of task specs that need to be parsed, handled, and
|
you would likely have only one set of task specs that need to be parsed, handled, and
|
||||||
serialized; however our `run` method is an awful lot of code to maintain in two separate
|
serialized; however, our `run` method is an awful lot of code to maintain in two separate
|
||||||
files.
|
files.
|
||||||
|
|
||||||
Running a Workflow
|
Running a Workflow
|
||||||
|
@ -180,10 +180,10 @@ Examining the Workflow State
|
||||||
----------------------------
|
----------------------------
|
||||||
|
|
||||||
When this application is run and we want to present steps to the user, we'll need
|
When this application is run and we want to present steps to the user, we'll need
|
||||||
to be able to examine the workflow and task states and associated data. We'll cover
|
to be able to examine the workflow and task states and associated data. We'll cover
|
||||||
the basics of this in this section.
|
the basics of this in this section.
|
||||||
|
|
||||||
The code below is a simple method for displaying information about a task. We use
|
The code below is a simple method for displaying information about a task. We use
|
||||||
this in two ways
|
this in two ways
|
||||||
|
|
||||||
- presenting a list of tasks to a user (in this case the state will always be ready, so we won't include it)
|
- presenting a list of tasks to a user (in this case the state will always be ready, so we won't include it)
|
||||||
|
@ -233,7 +233,7 @@ We'll print information about our task as described above, as well as a dump of
|
||||||
We can get a list of all tasks regardless of type or state with :code:`workflow.get_tasks()`.
|
We can get a list of all tasks regardless of type or state with :code:`workflow.get_tasks()`.
|
||||||
|
|
||||||
The actual list of tasks will get quite long (some tasks are expanded internally by Spiff into
|
The actual list of tasks will get quite long (some tasks are expanded internally by Spiff into
|
||||||
multiple tasks, and all gateways and events are also treated as "tasks"). So we're filtering
|
multiple tasks, and all gateways and events are also treated as "tasks"). So we're filtering
|
||||||
the tasks to only display the ones that would have salience to a user here.
|
the tasks to only display the ones that would have salience to a user here.
|
||||||
|
|
||||||
We'll further filter those tasks for :code:`READY` and :code:`WAITING` tasks for a more
|
We'll further filter those tasks for :code:`READY` and :code:`WAITING` tasks for a more
|
||||||
|
|
|
@ -171,7 +171,7 @@ Our :code:`select_option` function simply repeats the prompt until the user
|
||||||
enters a value contained in the option list.
|
enters a value contained in the option list.
|
||||||
|
|
||||||
For other fields, we'll just store whatever the user enters, although in the case
|
For other fields, we'll just store whatever the user enters, although in the case
|
||||||
where they data type was specified to be a :code:`long`, we'll convert it to a
|
where the data type was specified to be a :code:`long`, we'll convert it to a
|
||||||
number.
|
number.
|
||||||
|
|
||||||
Finally, we need to explicitly store the user-provided response in a variable
|
Finally, we need to explicitly store the user-provided response in a variable
|
||||||
|
@ -219,4 +219,3 @@ The template string can be obtained from :code:`task.task_spec.documentation`.
|
||||||
|
|
||||||
As noted above, our template class comes from Jinja. We render the template
|
As noted above, our template class comes from Jinja. We render the template
|
||||||
using the task data, which is just a dictionary.
|
using the task data, which is just a dictionary.
|
||||||
|
|
||||||
|
|
|
@ -64,7 +64,7 @@ The following example also has one task, represented by the rectangle with curve
|
||||||
|
|
||||||
|
|
||||||
The sequence flow is represented with a solid line connector. When the node at
|
The sequence flow is represented with a solid line connector. When the node at
|
||||||
the tail of a sequence flow completes, the node at the arrowhead is enabled to start.
|
the tail of a sequence flow completes, the node at the arrowhead is enabled to start.
|
||||||
|
|
||||||
|
|
||||||
A More Complicated Workflow
|
A More Complicated Workflow
|
||||||
|
@ -78,7 +78,7 @@ A More Complicated Workflow
|
||||||
|
|
||||||
|
|
||||||
In this example, the diamond shape is called a gateway. It represents a branch
|
In this example, the diamond shape is called a gateway. It represents a branch
|
||||||
point in our flow. This gateway is an exclusive data-based gateway (also
|
point in our flow. This gateway is an exclusive data-based gateway (also
|
||||||
called an XOR gateway). With an exclusive gateway, you must take one path or
|
called an XOR gateway). With an exclusive gateway, you must take one path or
|
||||||
the other based on some data condition. BPMN has other gateway types.
|
the other based on some data condition. BPMN has other gateway types.
|
||||||
|
|
||||||
|
@ -122,4 +122,3 @@ attached to will be cancelled if the event is received) or Non-Interrupting (in
|
||||||
which case the task will continue). In both cases, flows may emanate from the
|
which case the task will continue). In both cases, flows may emanate from the
|
||||||
Boundary Event, which will trigger those paths if the events occur while the task
|
Boundary Event, which will trigger those paths if the events occur while the task
|
||||||
is being executed.
|
is being executed.
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ Implementing Custom Tasks
|
||||||
Introduction
|
Introduction
|
||||||
------------
|
------------
|
||||||
|
|
||||||
In this second tutorial we are going to implement our own task, and
|
In this second tutorial, we are going to implement our own task, and
|
||||||
use serialization and deserialization to store and restore it.
|
use serialization and deserialization to store and restore it.
|
||||||
|
|
||||||
If you haven't already, you should complete the first
|
If you haven't already, you should complete the first
|
||||||
|
|
Loading…
Reference in New Issue