From 65aeca98bcf514f565aa5d6762f697c36cebd356 Mon Sep 17 00:00:00 2001 From: jasquat Date: Wed, 8 Mar 2023 13:06:25 -0500 Subject: [PATCH 1/4] initial changes to remove loop reset with spiff w/ burnettk --- spiffworkflow-backend/poetry.lock | 6 +-- spiffworkflow-backend/pyproject.toml | 2 +- .../src/spiffworkflow_backend/api.yml | 12 ++++++ .../routes/process_instances_controller.py | 38 ++++++++++++++++--- .../src/routes/ProcessInstanceShow.tsx | 2 +- 5 files changed, 50 insertions(+), 10 deletions(-) diff --git a/spiffworkflow-backend/poetry.lock b/spiffworkflow-backend/poetry.lock index c95a3a958..338f96ddb 100644 --- a/spiffworkflow-backend/poetry.lock +++ b/spiffworkflow-backend/poetry.lock @@ -1854,8 +1854,8 @@ lxml = "*" [package.source] type = "git" url = "https://github.com/sartography/SpiffWorkflow" -reference = "main" -resolved_reference = "bee868d38b2c3da680c7a96b6a634d16b90d5861" +reference = "feature/remove-loop-reset" +resolved_reference = "13034aaf12f62aa3914744ca05bc9a3e3b3c3452" [[package]] name = "SQLAlchemy" @@ -2234,7 +2234,7 @@ testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools" [metadata] lock-version = "1.1" python-versions = ">=3.9,<3.12" -content-hash = "2fd5138221eabec441b601bb3769be478bed42099e72e20f7b8aaa1c1a888909" +content-hash = "eac3b5aa78efea376a9e23e32f9e6853cc22c17a2a21b41e30800cb7c807d017" [metadata.files] alabaster = [ diff --git a/spiffworkflow-backend/pyproject.toml b/spiffworkflow-backend/pyproject.toml index 4f47921bb..fbaf11277 100644 --- a/spiffworkflow-backend/pyproject.toml +++ b/spiffworkflow-backend/pyproject.toml @@ -27,7 +27,7 @@ flask-marshmallow = "*" flask-migrate = "*" flask-restful = "*" werkzeug = "*" -SpiffWorkflow = {git = "https://github.com/sartography/SpiffWorkflow", rev = "main"} +SpiffWorkflow = {git = "https://github.com/sartography/SpiffWorkflow", rev = "feature/remove-loop-reset"} # SpiffWorkflow = {develop = true, path = "../SpiffWorkflow" } sentry-sdk = "^1.10" sphinx-autoapi = "^2.0" diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/api.yml b/spiffworkflow-backend/src/spiffworkflow_backend/api.yml index bc5e4f0be..963ef12b4 100755 --- a/spiffworkflow-backend/src/spiffworkflow_backend/api.yml +++ b/spiffworkflow-backend/src/spiffworkflow_backend/api.yml @@ -889,6 +889,12 @@ paths: description: If set will return the tasks as they were during a specific step of execution. schema: type: integer + - name: most_recent_tasks_only + in: query + required: false + description: If true, this wil return only the most recent tasks. + schema: + type: boolean get: tags: - Process Instances @@ -936,6 +942,12 @@ paths: description: If set will return the tasks as they were during a specific step of execution. schema: type: integer + - name: most_recent_tasks_only + in: query + required: false + description: If true, this wil return only the most recent tasks. + schema: + type: boolean get: tags: - Process Instances diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_instances_controller.py b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_instances_controller.py index 97d7f6323..634bf0aeb 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_instances_controller.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_instances_controller.py @@ -1,5 +1,6 @@ """APIs for dealing with process groups, process models, and process instances.""" import json +from uuid import UUID from typing import Any from typing import Dict from typing import Optional @@ -505,6 +506,7 @@ def process_instance_task_list_without_task_data_for_me( process_instance_id: int, all_tasks: bool = False, spiff_step: int = 0, + most_recent_tasks_only: bool = False, ) -> flask.wrappers.Response: """Process_instance_task_list_without_task_data_for_me.""" process_instance = _find_process_instance_for_me_or_raise(process_instance_id) @@ -513,6 +515,7 @@ def process_instance_task_list_without_task_data_for_me( process_instance, all_tasks, spiff_step, + most_recent_tasks_only, ) @@ -521,6 +524,7 @@ def process_instance_task_list_without_task_data( process_instance_id: int, all_tasks: bool = False, spiff_step: int = 0, + most_recent_tasks_only: bool = False, ) -> flask.wrappers.Response: """Process_instance_task_list_without_task_data.""" process_instance = _find_process_instance_by_id_or_raise(process_instance_id) @@ -529,6 +533,7 @@ def process_instance_task_list_without_task_data( process_instance, all_tasks, spiff_step, + most_recent_tasks_only, ) @@ -561,6 +566,8 @@ def process_instance_task_list( subprocess_state_overrides = {} for step_detail in step_details: + # if step_detail.bpmn_task_identifier == 'Activity_0iajzy6': + # print(f"step_detail: {step_detail}") if step_detail.task_id in tasks: tasks[step_detail.task_id]["state"] = Task.task_state_name_to_int( step_detail.task_state @@ -583,20 +590,39 @@ def process_instance_task_list( for spiff_task_id in tasks: if spiff_task_id not in steps_by_id: + # if tasks[spiff_task_id]['task_spec'] == 'Activity_0iajzy6': + # print(f"tasks[spiff_task_id]: {tasks[spiff_task_id]}") tasks[spiff_task_id]["data"] = {} + state_to_set = TaskState.FUTURE + if tasks[spiff_task_id]["state"] == TaskState.LIKELY: + # print("WE HERE") + previous_completed_steps_for_bpmn_task_identifier = [s for s in step_details if s.bpmn_task_identifier == tasks[spiff_task_id]['task_spec'] and s.task_state == "COMPLETED"] + # previous_completed_steps_for_bpmn_task_identifier = [s for s in step_details if s.task_state == "COMPLETED"] + # print(f"previous_completed_steps_for_bpmn_task_identifier: {previous_completed_steps_for_bpmn_task_identifier}") + if len(previous_completed_steps_for_bpmn_task_identifier) > 0: + state_to_set = TaskState.COMPLETED tasks[spiff_task_id]["state"] = subprocess_state_overrides.get( - spiff_task_id, TaskState.FUTURE + spiff_task_id, state_to_set ) bpmn_process_instance = ProcessInstanceProcessor._serializer.workflow_from_dict( full_bpmn_process_dict ) - spiff_task = processor.__class__.get_task_by_bpmn_identifier( - step_details[-1].bpmn_task_identifier, bpmn_process_instance - ) - if spiff_task is not None and spiff_task.state != TaskState.READY: + last_step_detail_bpmn_task_identifier = step_details[-1].task_id + print(f"last_step_detail_bpmn_task_identifier: {last_step_detail_bpmn_task_identifier}") + uuid = UUID(last_step_detail_bpmn_task_identifier) + spiff_task = processor.bpmn_process_instance.get_task(uuid) + print(f"spiff_task: {spiff_task}") + # # workflow.complete_task_from_id(uuid) + # # spiff_task = processor.__class__.get_task_by_bpmn_identifier( + # # last_step_detail_bpmn_task_identifier, bpmn_process_instance + # # ) + if spiff_task is not None: #and spiff_task.state != TaskState.READY: + print("HEY WE HERE") + print(f"spiff_task: {spiff_task}") spiff_task.complete() + print(f"spiff_task2: {spiff_task}") spiff_tasks = None if all_tasks: @@ -619,6 +645,8 @@ def process_instance_task_list( spiff_tasks_by_process_id_and_task_name: dict[str, SpiffTask] = {} for spiff_task in spiff_tasks: row_id = f"{spiff_task.task_spec._wf_spec.name}:{spiff_task.task_spec.name}" + # if spiff_task.task_spec.name == 'Activity_0iajzy6' or spiff_task.task_spec.name == 'Activity_0pv92j7': + # print(f"spiff_task: {spiff_task} - {spiff_task.id}") if ( row_id not in spiff_tasks_by_process_id_and_task_name or spiff_task.last_state_change diff --git a/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx b/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx index bfe543d9f..e9da2273a 100644 --- a/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx @@ -145,7 +145,7 @@ export default function ProcessInstanceShow({ variant }: OwnProps) { path: `${apiPath}/${modifiedProcessModelId}/${params.process_instance_id}${queryParams}`, successCallback: setProcessInstance, }); - let taskParams = '?all_tasks=true'; + let taskParams = '?all_tasks=true&most_recent_tasks_only=true'; if (typeof params.spiff_step !== 'undefined') { taskParams = `${taskParams}&spiff_step=${params.spiff_step}`; } From 83702b658882607eef3d9c473ec7debf56862f16 Mon Sep 17 00:00:00 2001 From: Elizabeth Esswein Date: Thu, 9 Mar 2023 18:10:05 -0500 Subject: [PATCH 2/4] update process instance task list --- .../routes/process_instances_controller.py | 100 ++++++------------ 1 file changed, 32 insertions(+), 68 deletions(-) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_instances_controller.py b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_instances_controller.py index 9608a705e..77ceff5a9 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_instances_controller.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_instances_controller.py @@ -1,10 +1,10 @@ """APIs for dealing with process groups, process models, and process instances.""" import base64 import json -from uuid import UUID from typing import Any from typing import Dict from typing import Optional +from uuid import UUID import flask.wrappers from flask import current_app @@ -15,9 +15,6 @@ from flask import request from flask.wrappers import Response from SpiffWorkflow.task import Task as SpiffTask # type: ignore from SpiffWorkflow.task import TaskState -from sqlalchemy import and_ -from sqlalchemy import or_ - from spiffworkflow_backend.exceptions.api_error import ApiError from spiffworkflow_backend.models.db import db from spiffworkflow_backend.models.human_task import HumanTaskModel @@ -67,6 +64,8 @@ from spiffworkflow_backend.services.process_instance_service import ( ) from spiffworkflow_backend.services.process_model_service import ProcessModelService from spiffworkflow_backend.services.spec_file_service import SpecFileService +from sqlalchemy import and_ +from sqlalchemy import or_ def process_instance_create( @@ -576,71 +575,33 @@ def process_instance_task_list( processor = ProcessInstanceProcessor(process_instance) full_bpmn_process_dict = processor.full_bpmn_process_dict - tasks = full_bpmn_process_dict["tasks"] subprocesses = full_bpmn_process_dict["subprocesses"] steps_by_id = {step_detail.task_id: step_detail for step_detail in step_details} - subprocess_state_overrides = {} - for step_detail in step_details: - # if step_detail.bpmn_task_identifier == 'Activity_0iajzy6': - # print(f"step_detail: {step_detail}") - if step_detail.task_id in tasks: - tasks[step_detail.task_id]["state"] = Task.task_state_name_to_int( - step_detail.task_state - ) - else: - for subprocess_id, subprocess_info in subprocesses.items(): - if step_detail.task_id in subprocess_info["tasks"]: - subprocess_info["tasks"][step_detail.task_id]["state"] = ( - Task.task_state_name_to_int(step_detail.task_state) - ) - subprocess_state_overrides[subprocess_id] = TaskState.WAITING + def restore_task(spiff_task: dict[str, Any], step_ended: float) -> None: + if spiff_task["last_state_change"] > step_ended: + spiff_task["state"] = Task.task_state_name_to_int("FUTURE") + spiff_task["data"] = {} - for subprocess_info in subprocesses.values(): - for spiff_task_id in subprocess_info["tasks"]: - if spiff_task_id not in steps_by_id: - subprocess_info["tasks"][spiff_task_id]["data"] = {} - subprocess_info["tasks"][spiff_task_id]["state"] = ( - subprocess_state_overrides.get(spiff_task_id, TaskState.FUTURE) - ) - - for spiff_task_id in tasks: - if spiff_task_id not in steps_by_id: - # if tasks[spiff_task_id]['task_spec'] == 'Activity_0iajzy6': - # print(f"tasks[spiff_task_id]: {tasks[spiff_task_id]}") - tasks[spiff_task_id]["data"] = {} - state_to_set = TaskState.FUTURE - if tasks[spiff_task_id]["state"] == TaskState.LIKELY: - # print("WE HERE") - previous_completed_steps_for_bpmn_task_identifier = [s for s in step_details if s.bpmn_task_identifier == tasks[spiff_task_id]['task_spec'] and s.task_state == "COMPLETED"] - # previous_completed_steps_for_bpmn_task_identifier = [s for s in step_details if s.task_state == "COMPLETED"] - # print(f"previous_completed_steps_for_bpmn_task_identifier: {previous_completed_steps_for_bpmn_task_identifier}") - if len(previous_completed_steps_for_bpmn_task_identifier) > 0: - state_to_set = TaskState.COMPLETED - tasks[spiff_task_id]["state"] = subprocess_state_overrides.get( - spiff_task_id, state_to_set - ) + if spiff_step > 0: + last_change = step_details[-1].end_in_seconds or 0 + for spiff_task in tasks.values(): + restore_task(spiff_task, last_change) + for spiff_task_id, subprocess in subprocesses.items(): + for spiff_task in subprocess["tasks"].values(): + restore_task(spiff_task, last_change) bpmn_process_instance = ProcessInstanceProcessor._serializer.workflow_from_dict( full_bpmn_process_dict ) - - last_step_detail_bpmn_task_identifier = step_details[-1].task_id - print(f"last_step_detail_bpmn_task_identifier: {last_step_detail_bpmn_task_identifier}") - uuid = UUID(last_step_detail_bpmn_task_identifier) - spiff_task = processor.bpmn_process_instance.get_task(uuid) - print(f"spiff_task: {spiff_task}") - # # workflow.complete_task_from_id(uuid) - # # spiff_task = processor.__class__.get_task_by_bpmn_identifier( - # # last_step_detail_bpmn_task_identifier, bpmn_process_instance - # # ) - if spiff_task is not None: #and spiff_task.state != TaskState.READY: - print("HEY WE HERE") - print(f"spiff_task: {spiff_task}") - spiff_task.complete() - print(f"spiff_task2: {spiff_task}") + if spiff_step > 0: + bpmn_process_instance.complete_task_from_id(UUID(step_details[-1].task_id)) + for subprocess_id, subprocess in bpmn_process_instance.subprocesses.items(): + if not subprocess.is_completed(): + task = bpmn_process_instance.get_task(subprocess_id) + task._set_state(TaskState.WAITING) spiff_tasks = None if all_tasks: @@ -656,23 +617,24 @@ def process_instance_task_list( subprocesses_by_child_task_ids, task_typename_by_task_id ) - tasks = [] spiff_tasks_to_process = spiff_tasks - if most_recent_tasks_only: spiff_tasks_by_process_id_and_task_name: dict[str, SpiffTask] = {} - for spiff_task in spiff_tasks: + current_tasks = {} + for spiff_task in spiff_tasks_to_process: row_id = f"{spiff_task.task_spec._wf_spec.name}:{spiff_task.task_spec.name}" - # if spiff_task.task_spec.name == 'Activity_0iajzy6' or spiff_task.task_spec.name == 'Activity_0pv92j7': - # print(f"spiff_task: {spiff_task} - {spiff_task.id}") + if spiff_task.state in [TaskState.READY, TaskState.WAITING]: + current_tasks[row_id] = spiff_task if ( row_id not in spiff_tasks_by_process_id_and_task_name - or spiff_task.last_state_change - > spiff_tasks_by_process_id_and_task_name[row_id].last_state_change + or spiff_task.state + > spiff_tasks_by_process_id_and_task_name[row_id].state ): spiff_tasks_by_process_id_and_task_name[row_id] = spiff_task + spiff_tasks_by_process_id_and_task_name.update(current_tasks) spiff_tasks_to_process = spiff_tasks_by_process_id_and_task_name.values() + response = [] for spiff_task in spiff_tasks_to_process: task_spiff_step: Optional[int] = None if str(spiff_task.id) in steps_by_id: @@ -686,9 +648,11 @@ def process_instance_task_list( calling_subprocess_task_id=calling_subprocess_task_id, task_spiff_step=task_spiff_step, ) - tasks.append(task) + if task.state in ["MAYBE", "LIKELY"]: + task.state = "FUTURE" + response.append(task) - return make_response(jsonify(tasks), 200) + return make_response(jsonify(response), 200) def process_instance_reset( From 7dd1747da7920bfba5f997d70e2ac71b96a1520f Mon Sep 17 00:00:00 2001 From: Elizabeth Esswein Date: Wed, 15 Mar 2023 11:10:02 -0400 Subject: [PATCH 3/4] remove ununsed variable --- .../routes/process_instances_controller.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_instances_controller.py b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_instances_controller.py index 8501960ab..3aaa418fe 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_instances_controller.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_instances_controller.py @@ -600,7 +600,7 @@ def process_instance_task_list( last_change = step_details[-1].end_in_seconds or 0 for spiff_task in tasks.values(): restore_task(spiff_task, last_change) - for spiff_task_id, subprocess in subprocesses.items(): + for subprocess in subprocesses.values(): for spiff_task in subprocess["tasks"].values(): restore_task(spiff_task, last_change) From 3b0135efe911f43072447164a1988f091c0a8ea5 Mon Sep 17 00:00:00 2001 From: burnettk Date: Wed, 15 Mar 2023 14:14:45 -0400 Subject: [PATCH 4/4] lint --- .../routes/process_instances_controller.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_instances_controller.py b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_instances_controller.py index 3aaa418fe..1c9e2758d 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_instances_controller.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_instances_controller.py @@ -15,6 +15,9 @@ from flask import request from flask.wrappers import Response from SpiffWorkflow.task import Task as SpiffTask # type: ignore from SpiffWorkflow.task import TaskState +from sqlalchemy import and_ +from sqlalchemy import or_ + from spiffworkflow_backend.exceptions.api_error import ApiError from spiffworkflow_backend.models.db import db from spiffworkflow_backend.models.human_task import HumanTaskModel @@ -70,8 +73,6 @@ from spiffworkflow_backend.services.process_instance_service import ( ) from spiffworkflow_backend.services.process_model_service import ProcessModelService from spiffworkflow_backend.services.spec_file_service import SpecFileService -from sqlalchemy import and_ -from sqlalchemy import or_ def process_instance_create(