use task table for process instance show page. spiff steps are not working yet and neither is data w/ burnettk

This commit is contained in:
jasquat 2023-03-20 16:51:29 -04:00
parent eca3caf8f5
commit d6684124fd
No known key found for this signature in database
14 changed files with 240 additions and 103 deletions

View File

@ -47,7 +47,8 @@ def app() -> Flask:
def with_db_and_bpmn_file_cleanup() -> None:
"""Do it cleanly!"""
meta = db.metadata
db.session.execute(db.update(BpmnProcessModel, values={"parent_process_id": None}))
db.session.execute(db.update(BpmnProcessModel, values={"top_level_process_id": None}))
db.session.execute(db.update(BpmnProcessModel, values={"direct_parent_process_id": None}))
for table in reversed(meta.sorted_tables):
db.session.execute(table.delete())

View File

@ -1,3 +1,5 @@
from __future__ import with_statement
import logging
from logging.config import fileConfig

View File

@ -1,8 +1,8 @@
"""empty message
Revision ID: b652c232839f
Revision ID: 4255f548bfb4
Revises:
Create Date: 2023-03-17 16:50:32.774216
Create Date: 2023-03-20 13:00:28.655387
"""
from alembic import op
@ -10,7 +10,7 @@ import sqlalchemy as sa
from sqlalchemy.dialects import mysql
# revision identifiers, used by Alembic.
revision = 'b652c232839f'
revision = '4255f548bfb4'
down_revision = None
branch_labels = None
depends_on = None
@ -115,19 +115,22 @@ def upgrade():
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('guid', sa.String(length=36), nullable=True),
sa.Column('bpmn_process_definition_id', sa.Integer(), nullable=False),
sa.Column('parent_process_id', sa.Integer(), nullable=True),
sa.Column('top_level_process_id', sa.Integer(), nullable=True),
sa.Column('direct_parent_process_id', sa.Integer(), nullable=True),
sa.Column('properties_json', sa.JSON(), nullable=False),
sa.Column('json_data_hash', sa.String(length=255), nullable=False),
sa.Column('start_in_seconds', sa.DECIMAL(precision=17, scale=6), nullable=True),
sa.Column('end_in_seconds', sa.DECIMAL(precision=17, scale=6), nullable=True),
sa.ForeignKeyConstraint(['bpmn_process_definition_id'], ['bpmn_process_definition.id'], ),
sa.ForeignKeyConstraint(['parent_process_id'], ['bpmn_process.id'], ),
sa.ForeignKeyConstraint(['direct_parent_process_id'], ['bpmn_process.id'], ),
sa.ForeignKeyConstraint(['top_level_process_id'], ['bpmn_process.id'], ),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('guid')
)
op.create_index(op.f('ix_bpmn_process_bpmn_process_definition_id'), 'bpmn_process', ['bpmn_process_definition_id'], unique=False)
op.create_index(op.f('ix_bpmn_process_direct_parent_process_id'), 'bpmn_process', ['direct_parent_process_id'], unique=False)
op.create_index(op.f('ix_bpmn_process_json_data_hash'), 'bpmn_process', ['json_data_hash'], unique=False)
op.create_index(op.f('ix_bpmn_process_parent_process_id'), 'bpmn_process', ['parent_process_id'], unique=False)
op.create_index(op.f('ix_bpmn_process_top_level_process_id'), 'bpmn_process', ['top_level_process_id'], unique=False)
op.create_table('bpmn_process_definition_relationship',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('bpmn_process_definition_parent_id', sa.Integer(), nullable=False),
@ -519,8 +522,9 @@ def downgrade():
op.drop_index(op.f('ix_bpmn_process_definition_relationship_bpmn_process_definition_child_id'), table_name='bpmn_process_definition_relationship')
op.drop_index(op.f('ix_bpmn_process_definition_relationship_bpmn_process_definition_parent_id'), table_name='bpmn_process_definition_relationship')
op.drop_table('bpmn_process_definition_relationship')
op.drop_index(op.f('ix_bpmn_process_parent_process_id'), table_name='bpmn_process')
op.drop_index(op.f('ix_bpmn_process_top_level_process_id'), table_name='bpmn_process')
op.drop_index(op.f('ix_bpmn_process_json_data_hash'), table_name='bpmn_process')
op.drop_index(op.f('ix_bpmn_process_direct_parent_process_id'), table_name='bpmn_process')
op.drop_index(op.f('ix_bpmn_process_bpmn_process_definition_id'), table_name='bpmn_process')
op.drop_table('bpmn_process')
op.drop_index(op.f('ix_user_service_id'), table_name='user')

View File

@ -919,6 +919,12 @@ paths:
description: If true, this wil return only the most recent tasks.
schema:
type: boolean
- name: bpmn_process_guid
in: query
required: false
description: The guid of the bpmn process to get the tasks for.
schema:
type: string
get:
tags:
- Process Instances
@ -972,6 +978,12 @@ paths:
description: If true, this wil return only the most recent tasks.
schema:
type: boolean
- name: bpmn_process_guid
in: query
required: false
description: The guid of the bpmn process to get the tasks for.
schema:
type: string
get:
tags:
- Process Instances

View File

@ -8,6 +8,10 @@ from spiffworkflow_backend.models.db import db
from spiffworkflow_backend.models.db import SpiffworkflowBaseDBModel
class BpmnProcessNotFoundError(Exception):
pass
# properties_json attributes:
# "last_task", # guid generated by spiff
# "root", # guid generated by spiff
@ -24,7 +28,8 @@ class BpmnProcessModel(SpiffworkflowBaseDBModel):
)
bpmn_process_definition = relationship(BpmnProcessDefinitionModel)
parent_process_id: int | None = db.Column(ForeignKey("bpmn_process.id"), nullable=True, index=True)
top_level_process_id: int | None = db.Column(ForeignKey("bpmn_process.id"), nullable=True, index=True)
direct_parent_process_id: int | None = db.Column(ForeignKey("bpmn_process.id"), nullable=True, index=True)
properties_json: dict = db.Column(db.JSON, nullable=False)
json_data_hash: str = db.Column(db.String(255), nullable=False, index=True)

View File

@ -1,5 +1,7 @@
"""APIs for dealing with process groups, process models, and process instances."""
import base64
from spiffworkflow_backend.services.task_service import TaskService
from sqlalchemy.orm import aliased
from spiffworkflow_backend.models.bpmn_process import BpmnProcessModel
import json
from typing import Any
@ -560,6 +562,7 @@ def process_instance_task_list_without_task_data_for_me(
all_tasks: bool = False,
spiff_step: int = 0,
most_recent_tasks_only: bool = False,
bpmn_process_guid: Optional[str] = None,
) -> flask.wrappers.Response:
"""Process_instance_task_list_without_task_data_for_me."""
process_instance = _find_process_instance_for_me_or_raise(process_instance_id)
@ -569,6 +572,7 @@ def process_instance_task_list_without_task_data_for_me(
all_tasks=all_tasks,
spiff_step=spiff_step,
most_recent_tasks_only=most_recent_tasks_only,
bpmn_process_guid=bpmn_process_guid
)
@ -578,6 +582,7 @@ def process_instance_task_list_without_task_data(
all_tasks: bool = False,
spiff_step: int = 0,
most_recent_tasks_only: bool = False,
bpmn_process_guid: Optional[str] = None,
) -> flask.wrappers.Response:
"""Process_instance_task_list_without_task_data."""
process_instance = _find_process_instance_by_id_or_raise(process_instance_id)
@ -587,12 +592,14 @@ def process_instance_task_list_without_task_data(
all_tasks=all_tasks,
spiff_step=spiff_step,
most_recent_tasks_only=most_recent_tasks_only,
bpmn_process_guid=bpmn_process_guid
)
def process_instance_task_list(
_modified_process_model_identifier: str,
process_instance: ProcessInstanceModel,
bpmn_process_guid: Optional[str] = None,
all_tasks: bool = False,
spiff_step: int = 0,
to_task_guid: Optional[str] = None,
@ -644,9 +651,14 @@ def process_instance_task_list(
# state: string;
# typename: string;
# calling_subprocess_task_guid: string;
# call_activity_process_bpmn_identifier?: string;
# calling_subprocess_task_guid: string; -> bpmn_process_direct_parent_guid
# call_activity_process_bpmn_identifier?: string; -> bpmn_process_direct_parent_bpmn_identifier
bpmn_process_ids = []
if bpmn_process_guid:
bpmn_process = BpmnProcessModel.query.filter_by(guid=bpmn_process_guid).first()
bpmn_processes = TaskService.bpmn_process_and_descendants([bpmn_process])
bpmn_process_ids = [p.id for p in bpmn_processes]
task_model_query = db.session.query(TaskModel).filter(
TaskModel.process_instance_id == process_instance.id,
@ -664,23 +676,39 @@ def process_instance_task_list(
)
task_model_query = task_model_query.filter(TaskModel.end_in_seconds <= to_task_model.end_in_seconds)
bpmn_process_alias = aliased(BpmnProcessModel)
direct_parent_bpmn_process_alias = aliased(BpmnProcessModel)
direct_parent_bpmn_process_definition_alias = aliased(BpmnProcessDefinitionModel)
task_model_query = (
task_model_query.order_by(
ProcessInstanceEventModel.timestamp.desc(), ProcessInstanceEventModel.id.desc() # type: ignore
TaskModel.id.desc() # type: ignore
)
.join(TaskDefinitionModel, TaskDefinitionModel.id == TaskModel.task_definition_id)
.join(BpmnProcessModel, BpmnProcessModel.id == TaskModel.bpmn_process_id)
.join(bpmn_process_alias, bpmn_process_alias.id == TaskModel.bpmn_process_id)
.outerjoin(direct_parent_bpmn_process_alias, direct_parent_bpmn_process_alias.id == bpmn_process_alias.direct_parent_process_id)
.outerjoin(direct_parent_bpmn_process_definition_alias, direct_parent_bpmn_process_definition_alias.id == direct_parent_bpmn_process_alias.bpmn_process_definition_id)
.join(
BpmnProcessDefinitionModel, BpmnProcessDefinitionModel.id == TaskDefinitionModel.bpmn_process_definition_id
)
.add_columns(
BpmnProcessDefinitionModel.bpmn_identifier.label("bpmn_process_definition_identifier"), # type: ignore
BpmnProcessDefinitionModel.bpmn_name.label("bpmn_process_definition_name"), # type: ignore
TaskDefinitionModel.bpmn_identifier.label("task_definition_identifier"), # type: ignore
TaskDefinitionModel.bpmn_name.label("task_definition_name"), # type: ignore
TaskDefinitionModel.typename.label("bpmn_task_type"), # type: ignore
direct_parent_bpmn_process_alias.guid.label("bpmn_process_direct_parent_guid"),
direct_parent_bpmn_process_definition_alias.bpmn_identifier.label("bpmn_process_direct_parent_bpmn_identifier"),
TaskDefinitionModel.bpmn_identifier,
TaskDefinitionModel.bpmn_name,
TaskDefinitionModel.typename,
TaskDefinitionModel.properties_json.label('task_definition_properties_json'), # type: ignore
)
)
if len(bpmn_process_ids) > 0:
print(f"bpmn_process_ids: {bpmn_process_ids}")
task_model_query = (
task_model_query.filter(bpmn_process_alias.id.in_(bpmn_process_ids))
)
task_models = task_model_query.all()
# processor = ProcessInstanceProcessor(process_instance)

View File

@ -687,7 +687,7 @@ class ProcessInstanceProcessor:
single_bpmn_process_dict = cls._get_bpmn_process_dict(bpmn_process, get_tasks=True)
spiff_bpmn_process_dict.update(single_bpmn_process_dict)
bpmn_subprocesses = BpmnProcessModel.query.filter_by(parent_process_id=bpmn_process.id).all()
bpmn_subprocesses = BpmnProcessModel.query.filter_by(top_level_process_id=bpmn_process.id).all()
bpmn_subprocess_id_to_guid_mappings = {}
for bpmn_subprocess in bpmn_subprocesses:
bpmn_subprocess_id_to_guid_mappings[bpmn_subprocess.id] = bpmn_subprocess.guid

View File

@ -13,7 +13,7 @@ from SpiffWorkflow.task import TaskStateNames
from sqlalchemy.dialects.mysql import insert as mysql_insert
from sqlalchemy.dialects.postgresql import insert as postgres_insert
from spiffworkflow_backend.models.bpmn_process import BpmnProcessModel
from spiffworkflow_backend.models.bpmn_process import BpmnProcessModel, BpmnProcessNotFoundError
from spiffworkflow_backend.models.db import db
from spiffworkflow_backend.models.json_data import JsonDataModel # noqa: F401
from spiffworkflow_backend.models.process_instance import ProcessInstanceModel
@ -144,7 +144,7 @@ class TaskService:
bpmn_process, new_task_models, new_json_data_dicts = cls.add_bpmn_process(
bpmn_process_dict=serializer.workflow_to_dict(subprocess),
process_instance=process_instance,
bpmn_process_parent=process_instance.bpmn_process,
top_level_process=process_instance.bpmn_process,
bpmn_process_guid=subprocess_guid,
bpmn_definition_to_task_definitions_mappings=bpmn_definition_to_task_definitions_mappings,
spiff_workflow=spiff_workflow,
@ -160,7 +160,7 @@ class TaskService:
bpmn_definition_to_task_definitions_mappings: dict,
spiff_workflow: BpmnWorkflow,
serializer: BpmnWorkflowSerializer,
bpmn_process_parent: Optional[BpmnProcessModel] = None,
top_level_process: Optional[BpmnProcessModel] = None,
bpmn_process_guid: Optional[str] = None,
) -> Tuple[BpmnProcessModel, dict[str, TaskModel], dict[str, JsonDataDict]]:
"""This creates and adds a bpmn_process to the Db session.
@ -182,9 +182,9 @@ class TaskService:
new_json_data_dicts: dict[str, JsonDataDict] = {}
bpmn_process = None
if bpmn_process_parent is not None:
if top_level_process is not None:
bpmn_process = BpmnProcessModel.query.filter_by(
parent_process_id=bpmn_process_parent.id, guid=bpmn_process_guid
top_level_process_id=top_level_process.id, guid=bpmn_process_guid
).first()
elif process_instance.bpmn_process_id is not None:
bpmn_process = process_instance.bpmn_process
@ -194,6 +194,28 @@ class TaskService:
bpmn_process_is_new = True
bpmn_process = BpmnProcessModel(guid=bpmn_process_guid)
bpmn_process_definition = bpmn_definition_to_task_definitions_mappings[spiff_workflow.spec.name][
"bpmn_process_definition"
]
bpmn_process.bpmn_process_definition = bpmn_process_definition
if top_level_process is not None:
subprocesses = spiff_workflow._get_outermost_workflow().subprocesses
direct_bpmn_process_parent = top_level_process
for subprocess_guid, subprocess in subprocesses.items():
if subprocess == spiff_workflow.outer_workflow:
direct_bpmn_process_parent = BpmnProcessModel.query.filter_by(guid=str(subprocess_guid)).first()
if direct_bpmn_process_parent is None:
raise BpmnProcessNotFoundError(
f"Could not find bpmn process with guid: {str(subprocess_guid)} "
f"while searching for direct parent process of {bpmn_process_guid}."
)
if direct_bpmn_process_parent is None:
raise BpmnProcessNotFoundError(f"Could not find a direct bpmn process parent for guid: {bpmn_process_guid}")
bpmn_process.direct_parent_process_id = direct_bpmn_process_parent.id
# Point the root id to the Start task instead of the Root task
# since we are ignoring the Root task.
for task_id, task_properties in tasks.items():
@ -206,15 +228,10 @@ class TaskService:
if bpmn_process_json_data is not None:
new_json_data_dicts[bpmn_process_json_data["hash"]] = bpmn_process_json_data
if bpmn_process_parent is None:
if top_level_process is None:
process_instance.bpmn_process = bpmn_process
elif bpmn_process.parent_process_id is None:
bpmn_process.parent_process_id = bpmn_process_parent.id
bpmn_process_definition = bpmn_definition_to_task_definitions_mappings[spiff_workflow.spec.name][
"bpmn_process_definition"
]
bpmn_process.bpmn_process_definition = bpmn_process_definition
elif bpmn_process.top_level_process_id is None:
bpmn_process.top_level_process_id = top_level_process.id
# Since we bulk insert tasks later we need to add the bpmn_process to the session
# to ensure we have an id.
@ -285,6 +302,14 @@ class TaskService:
setattr(task_model, task_model_data_column, task_data_hash)
return json_data_dict
@classmethod
def bpmn_process_and_descendants(cls, bpmn_processes: list[BpmnProcessModel]) -> list[BpmnProcessModel]:
bpmn_process_ids = [p.id for p in bpmn_processes]
direct_children = BpmnProcessModel.query.filter(BpmnProcessModel.direct_parent_process_id.in_(bpmn_process_ids)).all() # type: ignore
if len(direct_children) > 0:
return bpmn_processes + cls.bpmn_process_and_descendants(direct_children)
return bpmn_processes
@classmethod
def _create_task(
cls,

View File

@ -1,38 +1,71 @@
<?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" name="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 id="Event_03zsjvn">
<bpmn:incoming>Flow_095sred</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" name="Test Process To Call Script">
<bpmn:incoming>Flow_06g687y</bpmn:incoming>
<bpmn:outgoing>Flow_01e21r0</bpmn:outgoing>
<bpmn:startEvent id="Event_0pp84tn">
<bpmn:outgoing>Flow_1qsx5et</bpmn:outgoing>
</bpmn:startEvent>
<bpmn:subProcess id="test_process_to_call_subprocess">
<bpmn:incoming>Flow_1qsx5et</bpmn:incoming>
<bpmn:outgoing>Flow_095sred</bpmn:outgoing>
<bpmn:startEvent id="StartEvent_1">
<bpmn:outgoing>Flow_12zb3j0</bpmn:outgoing>
</bpmn:startEvent>
<bpmn:scriptTask id="test_process_to_call_subprocess_script" name="Test Process To Call Subprocess Script">
<bpmn:incoming>Flow_12zb3j0</bpmn:incoming>
<bpmn:outgoing>Flow_0iu4d71</bpmn:outgoing>
<bpmn:script>set_in_test_process_to_call_script = 1</bpmn:script>
</bpmn:scriptTask>
<bpmn:endEvent id="Event_1nn875f">
<bpmn:incoming>Flow_0iu4d71</bpmn:incoming>
</bpmn:endEvent>
<bpmn:sequenceFlow id="Flow_12zb3j0" sourceRef="StartEvent_1" targetRef="test_process_to_call_subprocess_script" />
<bpmn:sequenceFlow id="Flow_0iu4d71" sourceRef="test_process_to_call_subprocess_script" targetRef="Event_1nn875f" />
</bpmn:subProcess>
<bpmn:sequenceFlow id="Flow_1qsx5et" sourceRef="Event_0pp84tn" targetRef="test_process_to_call_subprocess" />
<bpmn:sequenceFlow id="Flow_095sred" sourceRef="test_process_to_call_subprocess" targetRef="Event_03zsjvn" />
</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 id="Event_0pp84tn_di" bpmnElement="Event_0pp84tn">
<dc:Bounds x="162.33333333333334" y="132" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Event_03zsjvn_di" bpmnElement="Event_03zsjvn">
<dc:Bounds x="432" y="132" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_14ywg4w_di" bpmnElement="test_process_to_call_subprocess" isExpanded="false">
<dc:Bounds x="270" y="110" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="Flow_1qsx5et_di" bpmnElement="Flow_1qsx5et">
<di:waypoint x="198" y="150" />
<di:waypoint x="270" y="150" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_095sred_di" bpmnElement="Flow_095sred">
<di:waypoint x="370" y="150" />
<di:waypoint x="432" y="150" />
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
<bpmndi:BPMNDiagram id="BPMNDiagram_1ikntvh">
<bpmndi:BPMNPlane id="BPMNPlane_18euprj" bpmnElement="test_process_to_call_subprocess">
<bpmndi:BPMNShape id="Activity_059upl6_di" bpmnElement="test_process_to_call_subprocess_script">
<dc:Bounds x="238" y="160" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Event_1nn875f_di" bpmnElement="Event_1nn875f">
<dc:Bounds x="432" y="159" width="36" height="36" />
<dc:Bounds x="360" y="182" 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 id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
<dc:Bounds x="180" y="182" width="36" height="36" />
</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 id="Flow_12zb3j0_di" bpmnElement="Flow_12zb3j0">
<di:waypoint x="216" y="200" />
<di:waypoint x="238" y="200" />
</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 id="Flow_0iu4d71_di" bpmnElement="Flow_0iu4d71">
<di:waypoint x="338" y="200" />
<di:waypoint x="360" y="200" />
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>

View File

@ -326,11 +326,11 @@ class TestProcessInstanceProcessor(BaseTest):
"manual_task": first_data_set,
"top_level_subprocess_script": second_data_set,
"top_level_subprocess": second_data_set,
"test_process_to_call_script": third_data_set,
"test_process_to_call_subprocess_script": third_data_set,
"top_level_call_activity": third_data_set,
"end_event_of_manual_task_model": third_data_set,
"top_level_subprocess_script_second": fourth_data_set,
"test_process_to_call_script_second": fourth_data_set,
"test_process_to_call_subprocess_script_second": fourth_data_set,
}
spiff_tasks_checked_once: list = []
@ -365,7 +365,7 @@ class TestProcessInstanceProcessor(BaseTest):
assert len(all_spiff_tasks) > 1
for spiff_task in all_spiff_tasks:
assert spiff_task.state == TaskState.COMPLETED
assert_spiff_task_is_in_process("test_process_to_call_script", "test_process_to_call")
assert_spiff_task_is_in_process("test_process_to_call_subprocess_script", "test_process_to_call_subprocess")
assert_spiff_task_is_in_process("top_level_subprocess_script", "top_level_subprocess")
assert_spiff_task_is_in_process("top_level_script", "top_level_process")
@ -378,6 +378,21 @@ class TestProcessInstanceProcessor(BaseTest):
assert bpmn_process_definition.bpmn_identifier == "test_process_to_call"
assert bpmn_process_definition.bpmn_name == "Test Process To Call"
# Check that the direct parent of the called activity subprocess task is the
# name of the process that was called from the activity.
if spiff_task.task_spec.name == "test_process_to_call_subprocess_script":
task_model = TaskModel.query.filter_by(guid=str(spiff_task.id)).first()
assert task_model is not None
bpmn_process = task_model.bpmn_process
assert bpmn_process is not None
bpmn_process_definition = bpmn_process.bpmn_process_definition
assert bpmn_process_definition is not None
assert bpmn_process_definition.bpmn_identifier == "test_process_to_call_subprocess"
assert bpmn_process.direct_parent_process_id is not None
direct_parent_process = BpmnProcessModel.query.filter_by(id=bpmn_process.direct_parent_process_id).first()
assert direct_parent_process is not None
assert direct_parent_process.bpmn_process_definition.bpmn_identifier == "test_process_to_call"
assert processor.get_data() == fifth_data_set
def test_does_not_recreate_human_tasks_on_multiple_saves(

View File

@ -60,14 +60,14 @@ import HttpService from '../services/HttpService';
import ButtonWithConfirmation from './ButtonWithConfirmation';
import { getBpmnProcessIdentifiers, makeid } from '../helpers';
import { useUriListForPermissions } from '../hooks/UriListForPermissions';
import { PermissionsToCheck, ProcessInstanceTask } from '../interfaces';
import { PermissionsToCheck, Task } from '../interfaces';
import { usePermissionFetcher } from '../hooks/PermissionService';
type OwnProps = {
processModelId: string;
diagramType: string;
readyOrWaitingProcessInstanceTasks?: ProcessInstanceTask[] | null;
completedProcessInstanceTasks?: ProcessInstanceTask[] | null;
readyOrWaitingProcessInstanceTasks?: Task[] | null;
completedProcessInstanceTasks?: Task[] | null;
saveDiagram?: (..._args: any[]) => any;
onDeleteFile?: (..._args: any[]) => any;
isPrimaryFile?: boolean;
@ -364,18 +364,18 @@ export default function ReactDiagramEditor({
function highlightBpmnIoElement(
canvas: any,
processInstanceTask: ProcessInstanceTask,
task: Task,
bpmnIoClassName: string,
bpmnProcessIdentifiers: string[]
) {
if (checkTaskCanBeHighlighted(processInstanceTask.name)) {
if (checkTaskCanBeHighlighted(task.bpmn_identifier)) {
try {
if (
bpmnProcessIdentifiers.includes(
processInstanceTask.process_identifier
task.bpmn_process_definition_identifier
)
) {
canvas.addMarker(processInstanceTask.name, bpmnIoClassName);
canvas.addMarker(task.bpmn_identifier, bpmnIoClassName);
}
} catch (bpmnIoError: any) {
// the task list also contains task for processes called from call activities which will

View File

@ -21,17 +21,26 @@ export interface RecentProcessModel {
processModelDisplayName: string;
}
export interface TaskDefinitionPropertiesJson {
spec: string;
}
export interface Task {
id: number;
guid: string;
bpmn_identifier: string;
bpmn_name?: string;
calling_subprocess_task_guid: string;
bpmn_process_direct_parent_guid: string;
bpmn_process_definition_identifier: string;
data: any;
state: string;
typename: string;
call_activity_process_bpmn_identifier?: string;
task_definition_properties_json: TaskDefinitionPropertiesJson;
// TOOD: DELETE THIS!
task_spiff_step?: number;
}
export interface ProcessInstanceTask {

View File

@ -46,7 +46,8 @@ import {
ProcessData,
ProcessInstance,
ProcessInstanceMetadata,
ProcessInstanceTask,
Task,
TaskDefinitionPropertiesJson,
} from '../interfaces';
import { usePermissionFetcher } from '../hooks/PermissionService';
import ProcessInstanceClass from '../classes/ProcessInstanceClass';
@ -64,10 +65,9 @@ export default function ProcessInstanceShow({ variant }: OwnProps) {
const [processInstance, setProcessInstance] =
useState<ProcessInstance | null>(null);
const [tasks, setTasks] = useState<ProcessInstanceTask[] | null>(null);
const [tasks, setTasks] = useState<Task[] | null>(null);
const [tasksCallHadError, setTasksCallHadError] = useState<boolean>(false);
const [taskToDisplay, setTaskToDisplay] =
useState<ProcessInstanceTask | null>(null);
const [taskToDisplay, setTaskToDisplay] = useState<Task | null>(null);
const [taskDataToDisplay, setTaskDataToDisplay] = useState<string>('');
const [showTaskDataLoading, setShowTaskDataLoading] =
useState<boolean>(false);
@ -148,6 +148,10 @@ export default function ProcessInstanceShow({ variant }: OwnProps) {
if (typeof params.spiff_step !== 'undefined') {
taskParams = `${taskParams}&spiff_step=${params.spiff_step}`;
}
const bpmnProcessGuid = searchParams.get('bpmn_process_guid');
if (bpmnProcessGuid) {
taskParams = `${taskParams}&bpmn_process_guid=${bpmnProcessGuid}`;
}
let taskPath = '';
if (ability.can('GET', taskListPath)) {
taskPath = `${taskListPath}${taskParams}`;
@ -213,14 +217,7 @@ export default function ProcessInstanceShow({ variant }: OwnProps) {
const getTaskIds = () => {
const taskIds = { completed: [], readyOrWaiting: [] };
if (tasks) {
const callingSubprocessId = searchParams.get('call_activity_task_id');
tasks.forEach(function getUserTasksElement(task: ProcessInstanceTask) {
if (
callingSubprocessId &&
callingSubprocessId !== task.calling_subprocess_task_id
) {
return null;
}
tasks.forEach(function getUserTasksElement(task: Task) {
if (task.state === 'COMPLETED') {
(taskIds.completed as any).push(task);
}
@ -251,13 +248,13 @@ export default function ProcessInstanceShow({ variant }: OwnProps) {
const spiffStepLink = (label: any, spiffStep: number) => {
const processIdentifier = searchParams.get('process_identifier');
const callActivityTaskId = searchParams.get('call_activity_task_id');
const callActivityTaskId = searchParams.get('bpmn_process_guid');
const queryParamArray = [];
if (processIdentifier) {
queryParamArray.push(`process_identifier=${processIdentifier}`);
}
if (callActivityTaskId) {
queryParamArray.push(`call_activity_task_id=${callActivityTaskId}`);
queryParamArray.push(`bpmn_process_guid=${callActivityTaskId}`);
}
let queryParams = '';
if (queryParamArray.length > 0) {
@ -509,7 +506,7 @@ export default function ProcessInstanceShow({ variant }: OwnProps) {
return <div />;
};
const processTaskResult = (result: ProcessInstanceTask) => {
const processTaskResult = (result: Task) => {
if (result == null) {
setTaskDataToDisplay('');
} else {
@ -518,7 +515,7 @@ export default function ProcessInstanceShow({ variant }: OwnProps) {
setShowTaskDataLoading(false);
};
const initializeTaskDataToDisplay = (task: ProcessInstanceTask | null) => {
const initializeTaskDataToDisplay = (task: Task | null) => {
if (
task &&
task.state === 'COMPLETED' &&
@ -526,7 +523,7 @@ export default function ProcessInstanceShow({ variant }: OwnProps) {
) {
setShowTaskDataLoading(true);
HttpService.makeCallToBackend({
path: `${targetUris.processInstanceTaskDataPath}/${task.task_spiff_step}`,
path: `${targetUris.processInstanceTaskDataPath}/${task.id}`,
httpMethod: 'GET',
successCallback: processTaskResult,
failureCallback: (error: any) => {
@ -577,13 +574,12 @@ export default function ProcessInstanceShow({ variant }: OwnProps) {
successCallback: handleProcessDataShowResponse,
});
} else if (tasks) {
const matchingTask: any = tasks.find((task: any) => {
const callingSubprocessId = searchParams.get('call_activity_task_id');
const matchingTask: Task | undefined = tasks.find((task: Task) => {
return (
(!callingSubprocessId ||
callingSubprocessId === task.calling_subprocess_task_id) &&
task.name === shapeElement.id &&
bpmnProcessIdentifiers.includes(task.process_identifier)
task.bpmn_identifier === shapeElement.id &&
bpmnProcessIdentifiers.includes(
task.bpmn_process_definition_identifier
)
);
});
if (matchingTask) {
@ -618,7 +614,7 @@ export default function ProcessInstanceShow({ variant }: OwnProps) {
httpMethod: 'POST',
successCallback: processScriptUnitTestCreateResult,
postBody: {
bpmn_task_identifier: taskToUse.name,
bpmn_task_identifier: taskToUse.bpmn_identifier,
input_json: previousTask.data,
expected_output_json: taskToUse.data,
},
@ -634,7 +630,7 @@ export default function ProcessInstanceShow({ variant }: OwnProps) {
];
return (
(task.state === 'WAITING' &&
subprocessTypes.filter((t) => t === task.type).length > 0) ||
subprocessTypes.filter((t) => t === task.typename).length > 0) ||
task.state === 'READY'
);
};
@ -656,7 +652,7 @@ export default function ProcessInstanceShow({ variant }: OwnProps) {
processInstance &&
processInstance.status === 'waiting' &&
ability.can('POST', targetUris.processInstanceSendEventPath) &&
taskTypes.filter((t) => t === task.type).length > 0 &&
taskTypes.filter((t) => t === task.typename).length > 0 &&
task.state === 'WAITING' &&
showingLastSpiffStep()
);
@ -717,7 +713,7 @@ export default function ProcessInstanceShow({ variant }: OwnProps) {
setEditingTaskData(false);
const dataObject = taskDataStringToObject(taskDataToDisplay);
if (taskToDisplay) {
const taskToDisplayCopy: ProcessInstanceTask = {
const taskToDisplayCopy: Task = {
...taskToDisplay,
data: dataObject,
}; // spread operator
@ -768,11 +764,11 @@ export default function ProcessInstanceShow({ variant }: OwnProps) {
});
};
const taskDisplayButtons = (task: any) => {
const taskDisplayButtons = (task: Task) => {
const buttons = [];
if (
task.type === 'Script Task' &&
task.typename === 'Script Task' &&
ability.can('PUT', targetUris.processModelShowPath)
) {
buttons.push(
@ -785,11 +781,15 @@ export default function ProcessInstanceShow({ variant }: OwnProps) {
);
}
if (task.type === 'Call Activity') {
if (task.typename === 'CallActivity') {
console.log('task', task)
const taskDefinitionPropertiesJson: TaskDefinitionPropertiesJson =
task.task_definition_properties_json;
console.log('taskDefinitionPropertiesJson', taskDefinitionPropertiesJson)
buttons.push(
<Link
data-qa="go-to-call-activity-result"
to={`${window.location.pathname}?process_identifier=${task.call_activity_process_identifier}&call_activity_task_id=${task.id}`}
to={`${window.location.pathname}?process_identifier=${taskDefinitionPropertiesJson.spec}&bpmn_process_guid=${task.guid}`}
target="_blank"
>
View Call Activity Diagram
@ -971,12 +971,15 @@ export default function ProcessInstanceShow({ variant }: OwnProps) {
};
const taskUpdateDisplayArea = () => {
const taskToUse: any = { ...taskToDisplay, data: taskDataToDisplay };
if (!taskToDisplay) {
return null;
}
const taskToUse: Task = { ...taskToDisplay, data: taskDataToDisplay };
const candidateEvents: any = getEvents(taskToUse);
if (taskToDisplay) {
let taskTitleText = taskToUse.id;
if (taskToUse.title) {
taskTitleText += ` (${taskToUse.title})`;
let taskTitleText = taskToUse.guid;
if (taskToUse.bpmn_name) {
taskTitleText += ` (${taskToUse.bpmn_name})`;
}
return (
<Modal
@ -985,8 +988,8 @@ export default function ProcessInstanceShow({ variant }: OwnProps) {
onRequestClose={handleTaskDataDisplayClose}
>
<Stack orientation="horizontal" gap={2}>
<span title={taskTitleText}>{taskToUse.name}</span> (
{taskToUse.type}
<span title={taskTitleText}>{taskToUse.bpmn_identifier}</span> (
{taskToUse.typename}
): {taskToUse.state}
{taskDisplayButtons(taskToUse)}
</Stack>