mirror of
https://github.com/status-im/spiff-arena.git
synced 2025-01-14 12:14:46 +00:00
Merge commit 'df6e065606b6b5b930754697f3332c4daebb4c9e'
This commit is contained in:
commit
768a8ebeb6
@ -81,17 +81,18 @@ class EventDefinitionParser(TaskParser):
|
|||||||
"""Parse the timerEventDefinition node and return an instance of TimerEventDefinition."""
|
"""Parse the timerEventDefinition node and return an instance of TimerEventDefinition."""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
label = self.node.get('name', self.node.get('id'))
|
||||||
time_date = first(self.xpath('.//bpmn:timeDate'))
|
time_date = first(self.xpath('.//bpmn:timeDate'))
|
||||||
if time_date is not None:
|
if time_date is not None:
|
||||||
return TimerEventDefinition(self.node.get('name'), time_date.text)
|
return TimerEventDefinition(label, time_date.text)
|
||||||
|
|
||||||
time_duration = first(self.xpath('.//bpmn:timeDuration'))
|
time_duration = first(self.xpath('.//bpmn:timeDuration'))
|
||||||
if time_duration is not None:
|
if time_duration is not None:
|
||||||
return TimerEventDefinition(self.node.get('name'), time_duration.text)
|
return TimerEventDefinition(label, time_duration.text)
|
||||||
|
|
||||||
time_cycle = first(self.xpath('.//bpmn:timeCycle'))
|
time_cycle = first(self.xpath('.//bpmn:timeCycle'))
|
||||||
if time_cycle is not None:
|
if time_cycle is not None:
|
||||||
return CycleTimerEventDefinition(self.node.get('name'), time_cycle.text)
|
return CycleTimerEventDefinition(label, time_cycle.text)
|
||||||
raise ValidationException("Unknown Time Specification", node=self.node, filename=self.filename)
|
raise ValidationException("Unknown Time Specification", node=self.node, filename=self.filename)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise ValidationException("Time Specification Error. " + str(e), node=self.node, filename=self.filename)
|
raise ValidationException("Time Specification Error. " + str(e), node=self.node, filename=self.filename)
|
||||||
|
@ -156,6 +156,9 @@ class EventBasedGateway(CatchingEvent):
|
|||||||
def spec_type(self):
|
def spec_type(self):
|
||||||
return 'Event Based Gateway'
|
return 'Event Based Gateway'
|
||||||
|
|
||||||
|
def _predict_hook(self, my_task):
|
||||||
|
my_task._sync_children(self.outputs, state=TaskState.MAYBE)
|
||||||
|
|
||||||
def _on_complete_hook(self, my_task):
|
def _on_complete_hook(self, my_task):
|
||||||
for child in my_task.children:
|
for child in my_task.children:
|
||||||
if not child.task_spec.event_definition.has_fired(child):
|
if not child.task_spec.event_definition.has_fired(child):
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
import datetime
|
import datetime
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
|
|
||||||
|
from SpiffWorkflow.task import TaskState
|
||||||
|
|
||||||
class EventDefinition(object):
|
class EventDefinition(object):
|
||||||
"""
|
"""
|
||||||
@ -307,6 +308,11 @@ class TimerEventDefinition(EventDefinition):
|
|||||||
The Timer is considered to have fired if the evaluated dateTime
|
The Timer is considered to have fired if the evaluated dateTime
|
||||||
expression is before datetime.datetime.now()
|
expression is before datetime.datetime.now()
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
if my_task.internal_data.get('event_fired'):
|
||||||
|
# If we manually send this event, this will be set
|
||||||
|
return True
|
||||||
|
|
||||||
dt = my_task.workflow.script_engine.evaluate(my_task, self.dateTime)
|
dt = my_task.workflow.script_engine.evaluate(my_task, self.dateTime)
|
||||||
if isinstance(dt,datetime.timedelta):
|
if isinstance(dt,datetime.timedelta):
|
||||||
if my_task._get_internal_data('start_time',None) is not None:
|
if my_task._get_internal_data('start_time',None) is not None:
|
||||||
@ -330,6 +336,9 @@ class TimerEventDefinition(EventDefinition):
|
|||||||
now = datetime.date.today()
|
now = datetime.date.today()
|
||||||
return now > dt
|
return now > dt
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
return self.__class__.__name__ == other.__class__.__name__ and self.label == other.label
|
||||||
|
|
||||||
def serialize(self):
|
def serialize(self):
|
||||||
retdict = super(TimerEventDefinition, self).serialize()
|
retdict = super(TimerEventDefinition, self).serialize()
|
||||||
retdict['label'] = self.label
|
retdict['label'] = self.label
|
||||||
@ -363,6 +372,10 @@ class CycleTimerEventDefinition(EventDefinition):
|
|||||||
# We will fire this timer whenever a cycle completes
|
# We will fire this timer whenever a cycle completes
|
||||||
# The task itself will manage counting how many times it fires
|
# The task itself will manage counting how many times it fires
|
||||||
|
|
||||||
|
if my_task.internal_data.get('event_fired'):
|
||||||
|
# If we manually send this event, this will be set
|
||||||
|
return True
|
||||||
|
|
||||||
repeat, delta = my_task.workflow.script_engine.evaluate(my_task, self.cycle_definition)
|
repeat, delta = my_task.workflow.script_engine.evaluate(my_task, self.cycle_definition)
|
||||||
|
|
||||||
# This is the first time we've entered this event
|
# This is the first time we've entered this event
|
||||||
@ -393,6 +406,9 @@ class CycleTimerEventDefinition(EventDefinition):
|
|||||||
my_task.internal_data['start_time'] = None
|
my_task.internal_data['start_time'] = None
|
||||||
super(CycleTimerEventDefinition, self).reset(my_task)
|
super(CycleTimerEventDefinition, self).reset(my_task)
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
return self.__class__.__name__ == other.__class__.__name__ and self.label == other.label
|
||||||
|
|
||||||
def serialize(self):
|
def serialize(self):
|
||||||
retdict = super(CycleTimerEventDefinition, self).serialize()
|
retdict = super(CycleTimerEventDefinition, self).serialize()
|
||||||
retdict['label'] = self.label
|
retdict['label'] = self.label
|
||||||
@ -411,19 +427,27 @@ class MultipleEventDefinition(EventDefinition):
|
|||||||
def event_type(self):
|
def event_type(self):
|
||||||
return 'Multiple'
|
return 'Multiple'
|
||||||
|
|
||||||
def catch(self, my_task, event_definition=None):
|
def has_fired(self, my_task):
|
||||||
event_definition.catch(my_task, event_definition)
|
|
||||||
|
seen_events = my_task.internal_data.get('seen_events', [])
|
||||||
|
for event in self.event_definitions:
|
||||||
|
if isinstance(event, (TimerEventDefinition, CycleTimerEventDefinition)):
|
||||||
|
child = [c for c in my_task.children if c.task_spec.event_definition == event]
|
||||||
|
child[0].task_spec._update_hook(child[0])
|
||||||
|
child[0]._set_state(TaskState.MAYBE)
|
||||||
|
if event.has_fired(my_task):
|
||||||
|
seen_events.append(event)
|
||||||
|
|
||||||
if self.parallel:
|
if self.parallel:
|
||||||
# Parallel multiple need to match all events
|
# Parallel multiple need to match all events
|
||||||
seen_events = my_task.internal_data.get('seen_events', []) + [event_definition]
|
return all(event in seen_events for event in self.event_definitions)
|
||||||
my_task._set_internal_data(seen_events=seen_events)
|
|
||||||
if all(event in seen_events for event in self.event_definitions):
|
|
||||||
my_task._set_internal_data(event_fired=True)
|
|
||||||
else:
|
|
||||||
my_task._set_internal_data(event_fired=False)
|
|
||||||
else:
|
else:
|
||||||
# Otherwise, matching one is sufficient
|
return len(seen_events) > 0
|
||||||
my_task._set_internal_data(event_fired=True)
|
|
||||||
|
def catch(self, my_task, event_definition=None):
|
||||||
|
event_definition.catch(my_task, event_definition)
|
||||||
|
seen_events = my_task.internal_data.get('seen_events', []) + [event_definition]
|
||||||
|
my_task._set_internal_data(seen_events=seen_events)
|
||||||
|
|
||||||
def reset(self, my_task):
|
def reset(self, my_task):
|
||||||
my_task.internal_data.pop('seen_events', None)
|
my_task.internal_data.pop('seen_events', None)
|
||||||
|
@ -54,7 +54,7 @@ class CatchingEvent(Simple, BpmnSpecMixin):
|
|||||||
my_task._ready()
|
my_task._ready()
|
||||||
super(CatchingEvent, self)._update_hook(my_task)
|
super(CatchingEvent, self)._update_hook(my_task)
|
||||||
|
|
||||||
def _on_ready(self, my_task):
|
def _on_ready_hook(self, my_task):
|
||||||
|
|
||||||
# None events don't propogate, so as soon as we're ready, we fire our event
|
# None events don't propogate, so as soon as we're ready, we fire our event
|
||||||
if isinstance(self.event_definition, NoneEventDefinition):
|
if isinstance(self.event_definition, NoneEventDefinition):
|
||||||
@ -63,7 +63,7 @@ class CatchingEvent(Simple, BpmnSpecMixin):
|
|||||||
# If we have not seen the event we're waiting for, enter the waiting state
|
# If we have not seen the event we're waiting for, enter the waiting state
|
||||||
if not self.event_definition.has_fired(my_task):
|
if not self.event_definition.has_fired(my_task):
|
||||||
my_task._set_state(TaskState.WAITING)
|
my_task._set_state(TaskState.WAITING)
|
||||||
super(CatchingEvent, self)._on_ready(my_task)
|
super(CatchingEvent, self)._on_ready_hook(my_task)
|
||||||
|
|
||||||
def _on_complete_hook(self, my_task):
|
def _on_complete_hook(self, my_task):
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ from functools import partial
|
|||||||
from SpiffWorkflow.bpmn.serializer.bpmn_converters import BpmnTaskSpecConverter
|
from SpiffWorkflow.bpmn.serializer.bpmn_converters import BpmnTaskSpecConverter
|
||||||
from SpiffWorkflow.bpmn.specs.events.StartEvent import StartEvent
|
from SpiffWorkflow.bpmn.specs.events.StartEvent import StartEvent
|
||||||
from SpiffWorkflow.bpmn.specs.events.EndEvent import EndEvent
|
from SpiffWorkflow.bpmn.specs.events.EndEvent import EndEvent
|
||||||
from SpiffWorkflow.bpmn.specs.events.IntermediateEvent import IntermediateThrowEvent, IntermediateCatchEvent, BoundaryEvent
|
from SpiffWorkflow.bpmn.specs.events.IntermediateEvent import IntermediateThrowEvent, IntermediateCatchEvent, BoundaryEvent, EventBasedGateway
|
||||||
from SpiffWorkflow.spiff.specs.none_task import NoneTask
|
from SpiffWorkflow.spiff.specs.none_task import NoneTask
|
||||||
from SpiffWorkflow.spiff.specs.manual_task import ManualTask
|
from SpiffWorkflow.spiff.specs.manual_task import ManualTask
|
||||||
from SpiffWorkflow.spiff.specs.user_task import UserTask
|
from SpiffWorkflow.spiff.specs.user_task import UserTask
|
||||||
@ -164,3 +164,7 @@ class ReceiveTaskConverter(SpiffEventConverter):
|
|||||||
dct['prescript'] = spec.prescript
|
dct['prescript'] = spec.prescript
|
||||||
dct['postscript'] = spec.postscript
|
dct['postscript'] = spec.postscript
|
||||||
return dct
|
return dct
|
||||||
|
|
||||||
|
class EventBasedGatewayConverter(SpiffEventConverter):
|
||||||
|
def __init__(self, data_converter=None, typename=None):
|
||||||
|
super().__init__(EventBasedGateway, data_converter, typename)
|
@ -19,7 +19,7 @@ class EventBsedGatewayTest(BpmnWorkflowTestCase):
|
|||||||
|
|
||||||
def testEventBasedGatewaySaveRestore(self):
|
def testEventBasedGatewaySaveRestore(self):
|
||||||
self.actual_test(True)
|
self.actual_test(True)
|
||||||
|
|
||||||
def actual_test(self, save_restore=False):
|
def actual_test(self, save_restore=False):
|
||||||
|
|
||||||
self.workflow.do_engine_steps()
|
self.workflow.do_engine_steps()
|
||||||
@ -36,6 +36,20 @@ class EventBsedGatewayTest(BpmnWorkflowTestCase):
|
|||||||
self.assertEqual(self.workflow.get_tasks_from_spec_name('message_2_event')[0].state, TaskState.CANCELLED)
|
self.assertEqual(self.workflow.get_tasks_from_spec_name('message_2_event')[0].state, TaskState.CANCELLED)
|
||||||
self.assertEqual(self.workflow.get_tasks_from_spec_name('timer_event')[0].state, TaskState.CANCELLED)
|
self.assertEqual(self.workflow.get_tasks_from_spec_name('timer_event')[0].state, TaskState.CANCELLED)
|
||||||
|
|
||||||
|
def testTimeout(self):
|
||||||
|
|
||||||
|
self.workflow.do_engine_steps()
|
||||||
|
waiting_tasks = self.workflow.get_waiting_tasks()
|
||||||
|
self.assertEqual(len(waiting_tasks), 1)
|
||||||
|
timer_event = waiting_tasks[0].task_spec.event_definition.event_definitions[-1]
|
||||||
|
self.workflow.catch(timer_event)
|
||||||
|
self.workflow.refresh_waiting_tasks()
|
||||||
|
self.workflow.do_engine_steps()
|
||||||
|
self.assertEqual(self.workflow.is_completed(), True)
|
||||||
|
self.assertEqual(self.workflow.get_tasks_from_spec_name('message_1_event')[0].state, TaskState.CANCELLED)
|
||||||
|
self.assertEqual(self.workflow.get_tasks_from_spec_name('message_2_event')[0].state, TaskState.CANCELLED)
|
||||||
|
self.assertEqual(self.workflow.get_tasks_from_spec_name('timer_event')[0].state, TaskState.COMPLETED)
|
||||||
|
|
||||||
def testMultipleStart(self):
|
def testMultipleStart(self):
|
||||||
spec, subprocess = self.load_workflow_spec('multiple-start-parallel.bpmn', 'main')
|
spec, subprocess = self.load_workflow_spec('multiple-start-parallel.bpmn', 'main')
|
||||||
workflow = BpmnWorkflow(spec)
|
workflow = BpmnWorkflow(spec)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user