@@ -7,7 +7,27 @@ starts with a timer, the timer waits until the event occurs; this might be days
7
7
Of course, we can always check that it's waiting and serialize the workflow until that time. However, we might decide that
8
8
we don't want SpiffWorkflow to manage this at all. We could do this with a custom task spec.
9
9
10
- First we'll create a new class
10
+ The code for this example can be found in :app: `misc/custom_start_event.py `.
11
+
12
+ There is a very simple diagram :bpmn: `timer_start.bpmn ` with the process ID `timer_start ` with a Start Event
13
+ with a Duration Timer of one day that can be used to illustrate how the custom task works. If you run this workflow
14
+ with any of the configurations provided, you'll see a `WAITING ` Start Event; if you use the parser and serializer we
15
+ just created, you'll be propmted to complete the User Task immediately.
16
+
17
+ To run this model with the custom spec:
18
+
19
+ .. code :: python
20
+
21
+ ./ runner.py - e spiff_example.misc.custom_start_event add - p timer_start - b bpmn/ tutorial/ timer_start.bpmn
22
+ ./ runner.py - e spiff_example.misc.custom_start_event
23
+
24
+ First we'll create a new class.
25
+
26
+ .. note ::
27
+
28
+ It might be better have the class's init method take both the event definition to use *and * the timer event
29
+ definition. Unfortunately, our parser is not terribly intuitive or easily extendable, so I've done it this
30
+ way to make this a little easier to follow.
11
31
12
32
.. code :: python
13
33
@@ -27,7 +47,7 @@ First we'll create a new class
27
47
super ().__init__ (wf_spec, bpmn_id, event_definition, ** kwargs)
28
48
self .timer_event = None
29
49
30
- When we create our custom event , we'll check to see if we're creating a Start Event with a :code: `TimerEventDefinition `, and
50
+ When we create our custom spec , we'll check to see if we're creating a Start Event with a :code: `TimerEventDefinition `, and
31
51
if so, we'll replace it with a :code: `NoneEventDefinition `. There are three different types of Timer Events, so we'll use
32
52
the base class for all three to make sure we account for TimeDate, Duration, and Cycle.
33
53
@@ -47,57 +67,44 @@ Whenever we create a custom task spec, we'll need to create a converter for it s
47
67
.. code :: python
48
68
49
69
from SpiffWorkflow.bpmn.serializer import BpmnWorkflowSerializer
50
- from SpiffWorkflow.bpmn.serializer.default import EventConverter
51
70
from SpiffWorkflow.spiff.serializer.task_spec import SpiffBpmnTaskConverter
52
71
from SpiffWorkflow.spiff.serializer import DEFAULT_CONFIG
53
72
54
73
class CustomStartEventConverter (SpiffBpmnTaskConverter ):
55
74
56
- def __init__ (self , registry ):
57
- super ().__init__ (CustomStartEvent, registry)
58
-
59
75
def to_dict (self , spec ):
60
76
dct = super ().to_dict(spec)
61
- if spec.timer_event is not None :
62
- dct[' event_definition' ] = self .registry.convert(spec.timer_event)
63
- else :
64
- dct[' event_definition' ] = self .registry.convert(spec.event_definition)
77
+ dct[' event_definition' ] = self .registry.convert(spec.event_definition)
78
+ dct[' timer_event' ] = self .registry.convert(spec.timer_event)
65
79
return dct
66
80
67
-
68
- DEFAULT_CONFIG [ ' task_specs ' ].remove(StartEventConverter )
69
- DEFAULT_CONFIG [ ' task_specs ' ].append(CustomStartEventConverter )
70
- registry = BpmnWorkflowSerializer.configure( DEFAULT_CONFIG )
71
- serializer = BpmnWorkflowSerializer(registry)
81
+ def from_dict ( self , dct ):
82
+ spec = super ().from_dict(dct )
83
+ spec.event_definition = self .registry.restore(dct[ ' event_definition ' ] )
84
+ spec.timer_event = self .registry.restore(dct[ ' timer_event ' ] )
85
+ return spec
72
86
73
87
Our converter will inherit from the :code: `SpiffBpmnTaskConverter `, since that's our base generic BPMN mixin class.
88
+ The parent converter will handle serializing the standard BPMN attributes, as well as attributes added in the
89
+ :code: `spiff ` package. There is a similar base converter in the :code: `bpmn.serializer.helpers ` package.
74
90
75
- The :code: `SpiffBpmnTaskConverter ` itself inherits from
76
- :code: `SpiffWorkflow.bpmn.serializer.helpers.task_spec.BpmnTaskSpecConverter `. which provides some helper methods for
77
- extracting standard attributes from tasks; the :code: `SpiffBpmnTaskConverter ` does the same for extensions from the
78
- :code: `spiff ` package.
79
-
80
- We don't have to do much -- all we do is replace the event definition with the original. The timer event will be
81
- moved when the task is restored, and this saves us from having to write a custom parser.
82
-
83
- .. note ::
84
-
85
- It might be better have the class's init method take both the event definition to use *and * the timer event
86
- definition. Unfortunately, our parser is not terribly intuitive or easily extendable, so I've done it this
87
- way to make this a little easier to follow.
91
+ A converter needs to implement two methods: :code: `to_dict ` (which takes a task spec and returns a JSON-serializable
92
+ dictionary of its attributes) and :code: `from_dict ` (which takes the dictionary and returns a task spec of the
93
+ appropriate type. We call the base method to do most of the work, and then update the result to reflect the changes
94
+ we made, in this case ensuring that both event definitions are handled. The parent converter also provides :code: `convert `
95
+ and :code: `restore ` methods to serialize any object that Spiff's serializer knows how to handle. For more details about
96
+ the serializer, see :doc: `serialization `.
88
97
89
- When we create our serializer, we need to tell it about this task. We'll remove the converter for the standard Start
90
- Event and add the one we created to the configuration. We then get a registry of classes that the serializer knows
91
- about that includes our custom spec, as well as all the other specs and initialize the serializer with it.
98
+ When we create our serializer, we need to tell it about this task. The serializer is initialized with a mapping
99
+ of object class to converter class, so we just need to add an entry for this mapping.
92
100
93
- .. note ::
101
+ .. code :: python
94
102
95
- The reason there are two steps involved (regurning a registry and *then * passing it to the serializer) rather
96
- that using the configuration directly is to allow further customization of the :code: `registry `. Workflows
97
- can contain arbtrary data, we want to provide developers the ability to serialization code for any object. See
98
- :ref: `serializing_custom_objects ` for more information about how this works.
103
+ SPIFF_CONFIG [CustomStartEvent] = CustomStartEventConverter
104
+ registry = FileSerializer.configure(SPIFF_CONFIG )
105
+ serializer = FileSerializer(dirname, registry = registry)
99
106
100
- Finally, we have to update our parser:
107
+ We also have to tell the parser to use our class instead of the standard class.
101
108
102
109
.. code :: python
103
110
@@ -114,10 +121,3 @@ will use. This is a bit unintuitive, but that's how it works.
114
121
115
122
Fortunately, we were able to reuse an existing Task Spec parser, which simplifies the process quite a bit.
116
123
117
- Having created a parser and serializer, we could create a configuration module and instantiate an engine with these
118
- components.
119
-
120
- There is a very simple diagram :bpmn: `timer_start.bpmn ` with the process ID `timer_start ` with a Start Event
121
- with a Duration Timer of one day that can be used to illustrate how the custom task works. If you run this workflow
122
- with any of the configurations provided, you'll see a `WAITING ` Start Event; if you use the parser and serializer we
123
- just created, you'll be propmted to complete the User Task immediately.
0 commit comments