added subprocess and call activity to task data bpmn file and the test is passing now

This commit is contained in:
jasquat 2023-03-10 10:46:40 -05:00
parent 513871ad21
commit 66a6c0449a
6 changed files with 223 additions and 48 deletions

View File

@ -1047,6 +1047,7 @@ class ProcessInstanceProcessor:
bpmn_process_dict: dict, bpmn_process_dict: dict,
bpmn_process_parent: Optional[BpmnProcessModel] = None, bpmn_process_parent: Optional[BpmnProcessModel] = None,
bpmn_process_guid: Optional[str] = None, bpmn_process_guid: Optional[str] = None,
add_tasks_if_new_bpmn_process: bool = True,
) -> BpmnProcessModel: ) -> BpmnProcessModel:
tasks = bpmn_process_dict.pop("tasks") tasks = bpmn_process_dict.pop("tasks")
bpmn_process_data = bpmn_process_dict.pop("data") bpmn_process_data = bpmn_process_dict.pop("data")
@ -1059,7 +1060,9 @@ class ProcessInstanceProcessor:
elif self.process_instance_model.bpmn_process_id is not None: elif self.process_instance_model.bpmn_process_id is not None:
bpmn_process = self.process_instance_model.bpmn_process bpmn_process = self.process_instance_model.bpmn_process
bpmn_process_is_new = False
if bpmn_process is None: if bpmn_process is None:
bpmn_process_is_new = True
bpmn_process = BpmnProcessModel(guid=bpmn_process_guid) bpmn_process = BpmnProcessModel(guid=bpmn_process_guid)
bpmn_process.properties_json = bpmn_process_dict bpmn_process.properties_json = bpmn_process_dict
@ -1087,25 +1090,27 @@ class ProcessInstanceProcessor:
bpmn_process.parent_process_id = bpmn_process_parent.id bpmn_process.parent_process_id = bpmn_process_parent.id
db.session.add(bpmn_process) db.session.add(bpmn_process)
for task_id, task_properties in tasks.items(): if bpmn_process_is_new and add_tasks_if_new_bpmn_process:
task_data_dict = task_properties.pop("data") # if True:
state_int = task_properties["state"] for task_id, task_properties in tasks.items():
task_data_dict = task_properties.pop("data")
state_int = task_properties["state"]
task_model = TaskModel.query.filter_by(guid=task_id).first() task_model = TaskModel.query.filter_by(guid=task_id).first()
if task_model is None: if task_model is None:
# bpmn_process_identifier = task_properties['workflow_name'] # bpmn_process_identifier = task_properties['workflow_name']
# bpmn_identifier = task_properties['task_spec'] # bpmn_identifier = task_properties['task_spec']
# #
# task_definition = TaskDefinitionModel.query.filter_by(bpmn_identifier=bpmn_identifier) # task_definition = TaskDefinitionModel.query.filter_by(bpmn_identifier=bpmn_identifier)
# .join(BpmnProcessDefinitionModel).filter(BpmnProcessDefinitionModel.bpmn_identifier==bpmn_process_identifier).first() # .join(BpmnProcessDefinitionModel).filter(BpmnProcessDefinitionModel.bpmn_identifier==bpmn_process_identifier).first()
# if task_definition is None: # if task_definition is None:
# subprocess_task = TaskModel.query.filter_by(guid=bpmn_process.guid) # subprocess_task = TaskModel.query.filter_by(guid=bpmn_process.guid)
task_model = TaskModel(guid=task_id, bpmn_process_id=bpmn_process.id) task_model = TaskModel(guid=task_id, bpmn_process_id=bpmn_process.id)
task_model.state = TaskStateNames[state_int] task_model.state = TaskStateNames[state_int]
task_model.properties_json = task_properties task_model.properties_json = task_properties
TaskService.update_task_data_on_task_model(task_model, task_data_dict) TaskService.update_task_data_on_task_model(task_model, task_data_dict)
db.session.add(task_model) db.session.add(task_model)
return bpmn_process return bpmn_process
@ -1115,7 +1120,6 @@ class ProcessInstanceProcessor:
Expects the save method to commit it. Expects the save method to commit it.
""" """
bpmn_dict = json.loads(self.serialize()) bpmn_dict = json.loads(self.serialize())
# with open('tmp2.json', 'w') as f: f.write(json.dumps(bpmn_dict)
bpmn_dict_keys = ("spec", "subprocess_specs", "serializer_version") bpmn_dict_keys = ("spec", "subprocess_specs", "serializer_version")
process_instance_data_dict = {} process_instance_data_dict = {}
bpmn_spec_dict = {} bpmn_spec_dict = {}
@ -1132,15 +1136,18 @@ class ProcessInstanceProcessor:
# FIXME: Update tasks in the did_complete_task instead to set the final info. # FIXME: Update tasks in the did_complete_task instead to set the final info.
# We will need to somehow cache all tasks initially though before each task is run. # We will need to somehow cache all tasks initially though before each task is run.
# Maybe always do this for first run - just need to know it's the first run. # Maybe always do this for first run - just need to know it's the first run.
if self.process_instance_model.bpmn_process_id is None: # import pdb; pdb.set_trace()
subprocesses = process_instance_data_dict.pop("subprocesses") # if self.process_instance_model.bpmn_process_id is None:
bpmn_process_parent = self._add_bpmn_process(process_instance_data_dict) subprocesses = process_instance_data_dict.pop("subprocesses")
for subprocess_task_id, subprocess_properties in subprocesses.items(): bpmn_process_parent = self._add_bpmn_process(process_instance_data_dict)
self._add_bpmn_process( for subprocess_task_id, subprocess_properties in subprocesses.items():
subprocess_properties, # import pdb; pdb.set_trace()
bpmn_process_parent, print(f"subprocess_task_id: {subprocess_task_id}")
bpmn_process_guid=subprocess_task_id, self._add_bpmn_process(
) subprocess_properties,
bpmn_process_parent,
bpmn_process_guid=subprocess_task_id,
)
def save(self) -> None: def save(self) -> None:
"""Saves the current state of this processor to the database.""" """Saves the current state of this processor to the database."""
@ -1693,6 +1700,7 @@ class ProcessInstanceProcessor:
secondary_engine_step_delegate=step_delegate, secondary_engine_step_delegate=step_delegate,
serializer=self._serializer, serializer=self._serializer,
process_instance=self.process_instance_model, process_instance=self.process_instance_model,
add_bpmn_process=self._add_bpmn_process,
) )
execution_strategy = execution_strategy_named( execution_strategy = execution_strategy_named(
execution_strategy_name, task_model_delegate execution_strategy_name, task_model_delegate

View File

@ -1,8 +1,10 @@
import json import json
from hashlib import sha256 from hashlib import sha256
from typing import Tuple
from typing import Any
from typing import Optional from typing import Optional
from SpiffWorkflow.bpmn.serializer.workflow import BpmnWorkflowSerializer # type: ignore from SpiffWorkflow.bpmn.serializer.workflow import BpmnWorkflowSerializer, BpmnWorkflow # type: ignore
from SpiffWorkflow.task import Task as SpiffTask # type: ignore from SpiffWorkflow.task import Task as SpiffTask # type: ignore
from SpiffWorkflow.task import TaskStateNames from SpiffWorkflow.task import TaskStateNames
@ -50,49 +52,60 @@ class TaskService:
@classmethod @classmethod
def find_or_create_task_model_from_spiff_task( def find_or_create_task_model_from_spiff_task(
cls, spiff_task: SpiffTask, process_instance: ProcessInstanceModel cls, spiff_task: SpiffTask, process_instance: ProcessInstanceModel,
serializer: BpmnWorkflowSerializer, add_bpmn_process: Any
) -> TaskModel: ) -> TaskModel:
spiff_task_guid = str(spiff_task.id) spiff_task_guid = str(spiff_task.id)
task_model: Optional[TaskModel] = TaskModel.query.filter_by( task_model: Optional[TaskModel] = TaskModel.query.filter_by(
guid=spiff_task_guid guid=spiff_task_guid
).first() ).first()
if task_model is None: if task_model is None:
bpmn_process = cls.task_bpmn_process(spiff_task, process_instance) bpmn_process = cls.task_bpmn_process(spiff_task, process_instance, serializer, add_bpmn_process)
task_model = TaskModel( task_model = TaskModel.query.filter_by(
guid=spiff_task_guid, bpmn_process_id=bpmn_process.id guid=spiff_task_guid
) ).first()
db.session.add(task_model) if task_model is None:
db.session.commit() task_model = TaskModel(
guid=spiff_task_guid, bpmn_process_id=bpmn_process.id
)
db.session.commit()
return task_model return task_model
@classmethod @classmethod
def task_subprocess_guid(cls, spiff_task: SpiffTask) -> Optional[str]: def task_subprocess(cls, spiff_task: SpiffTask) -> Optional[Tuple[str, BpmnWorkflow]]:
top_level_workflow = spiff_task.workflow._get_outermost_workflow() top_level_workflow = spiff_task.workflow._get_outermost_workflow()
my_wf = spiff_task.workflow # This is the workflow the spiff_task is part of my_wf = spiff_task.workflow # This is the workflow the spiff_task is part of
my_sp = None
my_sp_id = None my_sp_id = None
if my_wf != top_level_workflow: if my_wf != top_level_workflow:
# All the subprocesses are at the top level, so you can just compare them # All the subprocesses are at the top level, so you can just compare them
for sp_id, sp in top_level_workflow.subprocesses.items(): for sp_id, sp in top_level_workflow.subprocesses.items():
if sp == my_wf: if sp == my_wf:
my_sp = sp
my_sp_id = sp_id my_sp_id = sp_id
break break
return my_sp_id return (str(my_sp_id), my_sp)
@classmethod @classmethod
def task_bpmn_process( def task_bpmn_process(
cls, spiff_task: SpiffTask, process_instance: ProcessInstanceModel cls, spiff_task: SpiffTask, process_instance: ProcessInstanceModel,
serializer: BpmnWorkflowSerializer, add_bpmn_process: Any
) -> BpmnProcessModel: ) -> BpmnProcessModel:
subprocess_guid = cls.task_subprocess_guid(spiff_task) subprocess_guid, subprocess = cls.task_subprocess(spiff_task)
if subprocess_guid is None: if subprocess is None:
# This is the top level workflow, which has no guid # This is the top level workflow, which has no guid
return process_instance.bpmn_process return process_instance.bpmn_process
else: else:
# import pdb; pdb.set_trace()
bpmn_process: Optional[BpmnProcessModel] = BpmnProcessModel.query.filter_by( bpmn_process: Optional[BpmnProcessModel] = BpmnProcessModel.query.filter_by(
guid=subprocess_guid guid=subprocess_guid
).first() ).first()
# import pdb; pdb.set_trace()
if bpmn_process is None: if bpmn_process is None:
spiff_task_guid = str(spiff_task.id) bpmn_process = add_bpmn_process(serializer.workflow_to_dict(subprocess), process_instance.bpmn_process, subprocess_guid, add_tasks_if_new_bpmn_process=True)
raise Exception( db.session.commit()
f"Could not find bpmn_process for task {spiff_task_guid}" # spiff_task_guid = str(spiff_task.id)
) # raise Exception(
# f"Could not find bpmn_process for task {spiff_task_guid}"
# )
return bpmn_process return bpmn_process

View File

@ -1,5 +1,6 @@
import logging import logging
import time import time
from typing import Any
from typing import Callable from typing import Callable
from typing import List from typing import List
from typing import Optional from typing import Optional
@ -50,12 +51,14 @@ class TaskModelSavingDelegate(EngineStepDelegate):
serializer: BpmnWorkflowSerializer, serializer: BpmnWorkflowSerializer,
process_instance: ProcessInstanceModel, process_instance: ProcessInstanceModel,
secondary_engine_step_delegate: Optional[EngineStepDelegate] = None, secondary_engine_step_delegate: Optional[EngineStepDelegate] = None,
add_bpmn_process: Any = None,
) -> None: ) -> None:
self.secondary_engine_step_delegate = secondary_engine_step_delegate self.secondary_engine_step_delegate = secondary_engine_step_delegate
self.process_instance = process_instance self.process_instance = process_instance
self.current_task_model: Optional[TaskModel] = None self.current_task_model: Optional[TaskModel] = None
self.serializer = serializer self.serializer = serializer
self.add_bpmn_process = add_bpmn_process
def should_update_task_model(self) -> bool: def should_update_task_model(self) -> bool:
"""We need to figure out if we have previously save task info on this process intance. """We need to figure out if we have previously save task info on this process intance.
@ -63,12 +66,13 @@ class TaskModelSavingDelegate(EngineStepDelegate):
Use the bpmn_process_id to do this. Use the bpmn_process_id to do this.
""" """
return self.process_instance.bpmn_process_id is not None return self.process_instance.bpmn_process_id is not None
# return False
def will_complete_task(self, spiff_task: SpiffTask) -> None: def will_complete_task(self, spiff_task: SpiffTask) -> None:
if self.should_update_task_model(): if self.should_update_task_model():
self.current_task_model = ( self.current_task_model = (
TaskService.find_or_create_task_model_from_spiff_task( TaskService.find_or_create_task_model_from_spiff_task(
spiff_task, self.process_instance spiff_task, self.process_instance, self.serializer, self.add_bpmn_process
) )
) )
self.current_task_model.start_in_seconds = time.time() self.current_task_model.start_in_seconds = time.time()

View File

@ -0,0 +1,112 @@
<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:spiffworkflow="http://spiffworkflow.org/bpmn/schema/1.0/core" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" id="Definitions_96f6665" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="3.0.0-dev">
<bpmn:process id="top_level_process" name="Manual Task" isExecutable="true">
<bpmn:startEvent id="StartEvent_1">
<bpmn:outgoing>Flow_0stlaxe</bpmn:outgoing>
</bpmn:startEvent>
<bpmn:endEvent id="end_event_of_manual_task_model">
<bpmn:incoming>Flow_1and8ze</bpmn:incoming>
</bpmn:endEvent>
<bpmn:manualTask id="manual_task" name="Hello">
<bpmn:extensionElements>
<spiffworkflow:instructionsForEndUser>## Hello</spiffworkflow:instructionsForEndUser>
</bpmn:extensionElements>
<bpmn:incoming>Flow_1fktmf7</bpmn:incoming>
<bpmn:outgoing>Flow_09gjylo</bpmn:outgoing>
</bpmn:manualTask>
<bpmn:sequenceFlow id="Flow_0stlaxe" sourceRef="StartEvent_1" targetRef="top_level_script" />
<bpmn:scriptTask id="top_level_script">
<bpmn:incoming>Flow_0stlaxe</bpmn:incoming>
<bpmn:outgoing>Flow_1fktmf7</bpmn:outgoing>
<bpmn:script>set_in_top_level_script = 1</bpmn:script>
</bpmn:scriptTask>
<bpmn:sequenceFlow id="Flow_1fktmf7" sourceRef="top_level_script" targetRef="manual_task" />
<bpmn:sequenceFlow id="Flow_1i7syph" sourceRef="top_level_subprocess" targetRef="top_level_call_activity" />
<bpmn:sequenceFlow id="Flow_1and8ze" sourceRef="top_level_call_activity" targetRef="end_event_of_manual_task_model" />
<bpmn:sequenceFlow id="Flow_09gjylo" sourceRef="manual_task" targetRef="top_level_subprocess" />
<bpmn:subProcess id="top_level_subprocess">
<bpmn:incoming>Flow_09gjylo</bpmn:incoming>
<bpmn:outgoing>Flow_1i7syph</bpmn:outgoing>
<bpmn:startEvent id="Event_0g7txdo">
<bpmn:outgoing>Flow_00k1tii</bpmn:outgoing>
</bpmn:startEvent>
<bpmn:sequenceFlow id="Flow_00k1tii" sourceRef="Event_0g7txdo" targetRef="top_level_subprocess_script" />
<bpmn:endEvent id="Event_0zi0szr">
<bpmn:incoming>Flow_1b4o55k</bpmn:incoming>
</bpmn:endEvent>
<bpmn:sequenceFlow id="Flow_1b4o55k" sourceRef="top_level_subprocess_script" targetRef="Event_0zi0szr" />
<bpmn:scriptTask id="top_level_subprocess_script">
<bpmn:incoming>Flow_00k1tii</bpmn:incoming>
<bpmn:outgoing>Flow_1b4o55k</bpmn:outgoing>
<bpmn:script>set_in_top_level_subprocess = 1</bpmn:script>
</bpmn:scriptTask>
</bpmn:subProcess>
<bpmn:callActivity id="top_level_call_activity" calledElement="test_process_to_call">
<bpmn:incoming>Flow_1i7syph</bpmn:incoming>
<bpmn:outgoing>Flow_1and8ze</bpmn:outgoing>
</bpmn:callActivity>
</bpmn:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="top_level_process">
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
<dc:Bounds x="179" y="159" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_1vokg57_di" bpmnElement="top_level_script">
<dc:Bounds x="270" y="137" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_1rcj16n_di" bpmnElement="manual_task">
<dc:Bounds x="400" y="137" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Event_0ia26nb_di" bpmnElement="end_event_of_manual_task_model">
<dc:Bounds x="812" y="159" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_04hrmow_di" bpmnElement="top_level_call_activity">
<dc:Bounds x="680" y="137" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_19a46sv_di" bpmnElement="top_level_subprocess">
<dc:Bounds x="530" y="137" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="Flow_0stlaxe_di" bpmnElement="Flow_0stlaxe">
<di:waypoint x="215" y="177" />
<di:waypoint x="270" y="177" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_1fktmf7_di" bpmnElement="Flow_1fktmf7">
<di:waypoint x="370" y="177" />
<di:waypoint x="400" y="177" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_1i7syph_di" bpmnElement="Flow_1i7syph">
<di:waypoint x="630" y="177" />
<di:waypoint x="680" y="177" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_1and8ze_di" bpmnElement="Flow_1and8ze">
<di:waypoint x="780" y="177" />
<di:waypoint x="812" y="177" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_09gjylo_di" bpmnElement="Flow_09gjylo">
<di:waypoint x="500" y="177" />
<di:waypoint x="530" y="177" />
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
<bpmndi:BPMNDiagram id="BPMNDiagram_01cbxj3">
<bpmndi:BPMNPlane id="BPMNPlane_07qyo6y" bpmnElement="top_level_subprocess">
<bpmndi:BPMNShape id="Event_0g7txdo_di" bpmnElement="Event_0g7txdo">
<dc:Bounds x="362" y="132" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Event_0zi0szr_di" bpmnElement="Event_0zi0szr">
<dc:Bounds x="562" y="132" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_0g000aa_di" bpmnElement="top_level_subprocess_script">
<dc:Bounds x="430" y="110" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="Flow_00k1tii_di" bpmnElement="Flow_00k1tii">
<di:waypoint x="398" y="150" />
<di:waypoint x="430" y="150" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_1b4o55k_di" bpmnElement="Flow_1b4o55k">
<di:waypoint x="530" y="150" />
<di:waypoint x="562" y="150" />
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn:definitions>

View File

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" id="Definitions_96f6665" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="3.0.0-dev">
<bpmn:process id="test_process_to_call" isExecutable="true">
<bpmn:startEvent id="StartEvent_1">
<bpmn:outgoing>Flow_06g687y</bpmn:outgoing>
</bpmn:startEvent>
<bpmn:sequenceFlow id="Flow_06g687y" sourceRef="StartEvent_1" targetRef="test_process_to_call_script" />
<bpmn:endEvent id="Event_1nn875f">
<bpmn:incoming>Flow_01e21r0</bpmn:incoming>
</bpmn:endEvent>
<bpmn:sequenceFlow id="Flow_01e21r0" sourceRef="test_process_to_call_script" targetRef="Event_1nn875f" />
<bpmn:scriptTask id="test_process_to_call_script">
<bpmn:incoming>Flow_06g687y</bpmn:incoming>
<bpmn:outgoing>Flow_01e21r0</bpmn:outgoing>
<bpmn:script>set_in_test_process_to_call_script = 1</bpmn:script>
</bpmn:scriptTask>
</bpmn:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="test_process_to_call">
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
<dc:Bounds x="179" y="159" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Event_1nn875f_di" bpmnElement="Event_1nn875f">
<dc:Bounds x="432" y="159" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_059upl6_di" bpmnElement="test_process_to_call_script">
<dc:Bounds x="270" y="137" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="Flow_06g687y_di" bpmnElement="Flow_06g687y">
<di:waypoint x="215" y="177" />
<di:waypoint x="270" y="177" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_01e21r0_di" bpmnElement="Flow_01e21r0">
<di:waypoint x="370" y="177" />
<di:waypoint x="432" y="177" />
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn:definitions>

View File

@ -314,9 +314,8 @@ class TestProcessInstanceProcessor(BaseTest):
assert finance_group is not None assert finance_group is not None
process_model = load_test_spec( process_model = load_test_spec(
process_model_id="test_group/manual_task", process_model_id="test_group/manual_task_with_subprocesses",
bpmn_file_name="manual_task.bpmn", process_model_source_directory="manual_task_with_subprocesses",
process_model_source_directory="manual_task",
) )
process_instance = self.create_process_instance_from_process_model( process_instance = self.create_process_instance_from_process_model(
process_model=process_model, user=initiator_user process_model=process_model, user=initiator_user