Optimistically skip some timers (#232)
This commit is contained in:
parent
e9827d0d5e
commit
ed3c0d7766
|
@ -2,7 +2,10 @@
|
|||
import base64
|
||||
import hashlib
|
||||
import time
|
||||
from datetime import datetime
|
||||
from datetime import timezone
|
||||
from typing import Any
|
||||
from typing import Dict
|
||||
from typing import Generator
|
||||
from typing import List
|
||||
from typing import Optional
|
||||
|
@ -12,6 +15,7 @@ from urllib.parse import unquote
|
|||
import sentry_sdk
|
||||
from flask import current_app
|
||||
from flask import g
|
||||
from SpiffWorkflow.bpmn.specs.events.event_definitions import TimerEventDefinition # type: ignore
|
||||
from SpiffWorkflow.bpmn.specs.events.IntermediateEvent import _BoundaryEventParent # type: ignore
|
||||
from SpiffWorkflow.task import Task as SpiffTask # type: ignore
|
||||
|
||||
|
@ -86,6 +90,29 @@ class ProcessInstanceService:
|
|||
process_model = ProcessModelService.get_process_model(process_model_identifier)
|
||||
return cls.create_process_instance(process_model, user)
|
||||
|
||||
@classmethod
|
||||
def waiting_event_can_be_skipped(cls, waiting_event: Dict[str, Any], now_in_utc: datetime) -> bool:
|
||||
#
|
||||
# over time this function can gain more knowledge of different event types,
|
||||
# for now we are just handling Duration Timer events.
|
||||
#
|
||||
# example: {'event_type': 'Duration Timer', 'name': None, 'value': '2023-04-27T20:15:10.626656+00:00'}
|
||||
#
|
||||
event_type = waiting_event.get("event_type")
|
||||
if event_type == "Duration Timer":
|
||||
event_value = waiting_event.get("value")
|
||||
if event_value is not None:
|
||||
event_datetime = TimerEventDefinition.get_datetime(event_value)
|
||||
return event_datetime > now_in_utc # type: ignore
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def all_waiting_events_can_be_skipped(cls, waiting_events: List[Dict[str, Any]]) -> bool:
|
||||
for waiting_event in waiting_events:
|
||||
if not cls.waiting_event_can_be_skipped(waiting_event, datetime.now(timezone.utc)):
|
||||
return False
|
||||
return True
|
||||
|
||||
@classmethod
|
||||
def ready_user_task_has_associated_timer(cls, processor: ProcessInstanceProcessor) -> bool:
|
||||
for ready_user_task in processor.bpmn_process_instance.get_ready_user_tasks():
|
||||
|
@ -101,7 +128,10 @@ class ProcessInstanceService:
|
|||
if processor.process_instance_model.status != status_value:
|
||||
return True
|
||||
|
||||
return status_value == "user_input_required" and not cls.ready_user_task_has_associated_timer(processor)
|
||||
if status_value == "user_input_required" and cls.ready_user_task_has_associated_timer(processor):
|
||||
return cls.all_waiting_events_can_be_skipped(processor.bpmn_process_instance.waiting_events())
|
||||
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def do_waiting(cls, status_value: str = ProcessInstanceStatus.waiting.value) -> None:
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
"""Test_process_instance_processor."""
|
||||
from datetime import datetime
|
||||
from datetime import timezone
|
||||
from typing import Optional
|
||||
|
||||
from flask.app import Flask
|
||||
|
@ -213,3 +215,33 @@ class TestProcessInstanceService(BaseTest):
|
|||
assert len(models) == 2
|
||||
self._check_sample_file_data_model("File", 0, models[0])
|
||||
self._check_sample_file_data_model("File", 1, models[1])
|
||||
|
||||
def test_does_not_skip_events_it_does_not_know_about(self) -> None:
|
||||
assert not (
|
||||
ProcessInstanceService.waiting_event_can_be_skipped(
|
||||
{"event_type": "Unknown", "name": None, "value": "2023-04-27T20:15:10.626656+00:00"},
|
||||
datetime.now(timezone.utc),
|
||||
)
|
||||
)
|
||||
|
||||
def test_does_skip_duration_timer_events_for_the_future(self) -> None:
|
||||
assert ProcessInstanceService.waiting_event_can_be_skipped(
|
||||
{"event_type": "Duration Timer", "name": None, "value": "2023-04-27T20:15:10.626656+00:00"},
|
||||
datetime.fromisoformat("2023-04-26T20:15:10.626656+00:00"),
|
||||
)
|
||||
|
||||
def test_does_not_skip_duration_timer_events_for_the_past(self) -> None:
|
||||
assert not (
|
||||
ProcessInstanceService.waiting_event_can_be_skipped(
|
||||
{"event_type": "Duration Timer", "name": None, "value": "2023-04-27T20:15:10.626656+00:00"},
|
||||
datetime.fromisoformat("2023-04-28T20:15:10.626656+00:00"),
|
||||
)
|
||||
)
|
||||
|
||||
def test_does_not_skip_duration_timer_events_for_now(self) -> None:
|
||||
assert not (
|
||||
ProcessInstanceService.waiting_event_can_be_skipped(
|
||||
{"event_type": "Duration Timer", "name": None, "value": "2023-04-27T20:15:10.626656+00:00"},
|
||||
datetime.fromisoformat("2023-04-27T20:15:10.626656+00:00"),
|
||||
)
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue