Merge pull request #202 from sartography/feature/fix_process_instance_rewind_two

Feature/fix process instance rewind two
This commit is contained in:
jasquat 2023-04-03 13:47:39 -04:00 committed by GitHub
commit 90c1242032
6 changed files with 293 additions and 295 deletions

View File

@ -1875,7 +1875,7 @@ test = ["pytest"]
[[package]] [[package]]
name = "SpiffWorkflow" name = "SpiffWorkflow"
version = "1.2.1" version = "1.2.1"
description = "A workflow framework and BPMN/DMN Processor" description = ""
category = "main" category = "main"
optional = false optional = false
python-versions = "*" python-versions = "*"
@ -1890,7 +1890,7 @@ lxml = "*"
type = "git" type = "git"
url = "https://github.com/sartography/SpiffWorkflow" url = "https://github.com/sartography/SpiffWorkflow"
reference = "main" reference = "main"
resolved_reference = "62454c99c3a711c38f4249a3b5e7215d42037d72" resolved_reference = "e1add839ddf2512f27cd0afe681ff3e0460d6f7a"
[[package]] [[package]]
name = "sqlalchemy" name = "sqlalchemy"

View File

@ -669,6 +669,15 @@ def process_instance_task_list(
task_model_query = task_model_query.filter(bpmn_process_alias.id.in_(bpmn_process_ids)) task_model_query = task_model_query.filter(bpmn_process_alias.id.in_(bpmn_process_ids))
task_models = task_model_query.all() task_models = task_model_query.all()
task_model_list = {}
if most_recent_tasks_only:
for task_model in task_models:
bpmn_process_guid = task_model.bpmn_process_direct_parent_guid or "TOP"
row_key = f"{bpmn_process_guid}:::{task_model.bpmn_identifier}"
if row_key not in task_model_list:
task_model_list[row_key] = task_model
task_models = list(task_model_list.values())
if to_task_model is not None: if to_task_model is not None:
task_models_dict = json.loads(current_app.json.dumps(task_models)) task_models_dict = json.loads(current_app.json.dumps(task_models))
for task_model in task_models_dict: for task_model in task_models_dict:
@ -693,7 +702,7 @@ def process_instance_reset(
) -> flask.wrappers.Response: ) -> flask.wrappers.Response:
"""Reset a process instance to a particular step.""" """Reset a process instance to a particular step."""
process_instance = _find_process_instance_by_id_or_raise(process_instance_id) process_instance = _find_process_instance_by_id_or_raise(process_instance_id)
ProcessInstanceProcessor.reset_process(process_instance, to_task_guid, commit=True) ProcessInstanceProcessor.reset_process(process_instance, to_task_guid)
return Response(json.dumps({"ok": True}), status=200, mimetype="application/json") return Response(json.dumps({"ok": True}), status=200, mimetype="application/json")

View File

@ -1,5 +1,6 @@
"""Process_instance_processor.""" """Process_instance_processor."""
import _strptime # type: ignore import _strptime # type: ignore
import copy
import decimal import decimal
import json import json
import logging import logging
@ -50,6 +51,8 @@ from SpiffWorkflow.spiff.serializer.config import SPIFF_SPEC_CONFIG # type: ign
from SpiffWorkflow.task import Task as SpiffTask # type: ignore from SpiffWorkflow.task import Task as SpiffTask # type: ignore
from SpiffWorkflow.task import TaskState from SpiffWorkflow.task import TaskState
from SpiffWorkflow.util.deep_merge import DeepMerge # type: ignore from SpiffWorkflow.util.deep_merge import DeepMerge # type: ignore
from sqlalchemy import and_
from sqlalchemy import or_
from spiffworkflow_backend.exceptions.api_error import ApiError from spiffworkflow_backend.exceptions.api_error import ApiError
from spiffworkflow_backend.models.bpmn_process import BpmnProcessModel from spiffworkflow_backend.models.bpmn_process import BpmnProcessModel
@ -741,6 +744,9 @@ class ProcessInstanceProcessor:
spec, subprocesses spec, subprocesses
) )
bpmn_process_instance.data[ProcessInstanceProcessor.VALIDATION_PROCESS_KEY] = validate_only bpmn_process_instance.data[ProcessInstanceProcessor.VALIDATION_PROCESS_KEY] = validate_only
# run _predict to ensure tasks are predicted to add back in LIKELY and MAYBE tasks
bpmn_process_instance._predict()
return ( return (
bpmn_process_instance, bpmn_process_instance,
full_bpmn_process_dict, full_bpmn_process_dict,
@ -1259,127 +1265,124 @@ class ProcessInstanceProcessor:
# they never get picked up by spiff and processed. The process instance just stops after the to_task_guid # they never get picked up by spiff and processed. The process instance just stops after the to_task_guid
# and marks itself complete without processing any of the children. # and marks itself complete without processing any of the children.
@classmethod @classmethod
def reset_process( def reset_process(cls, process_instance: ProcessInstanceModel, to_task_guid: str) -> None:
cls, process_instance: ProcessInstanceModel, to_task_guid: str, commit: Optional[bool] = False
) -> None:
"""Reset a process to an earlier state.""" """Reset a process to an earlier state."""
raise Exception("This feature to reset a process instance to a given task is currently unavaiable") # raise Exception("This feature to reset a process instance to a given task is currently unavaiable")
# cls.add_event_to_process_instance( cls.add_event_to_process_instance(
# process_instance, ProcessInstanceEventType.process_instance_rewound_to_task.value, task_guid=to_task_guid process_instance, ProcessInstanceEventType.process_instance_rewound_to_task.value, task_guid=to_task_guid
# ) )
#
# to_task_model = TaskModel.query.filter_by(guid=to_task_guid, process_instance_id=process_instance.id).first() to_task_model = TaskModel.query.filter_by(guid=to_task_guid, process_instance_id=process_instance.id).first()
# if to_task_model is None: if to_task_model is None:
# raise TaskNotFoundError( raise TaskNotFoundError(
# f"Cannot find a task with guid '{to_task_guid}' for process instance '{process_instance.id}'" f"Cannot find a task with guid '{to_task_guid}' for process instance '{process_instance.id}'"
# ) )
#
# parent_bpmn_processes, task_models_of_parent_bpmn_processes = TaskService.task_models_of_parent_bpmn_processes( # NOTE: run ALL queries before making changes to ensure we get everything before anything changes
# to_task_model parent_bpmn_processes, task_models_of_parent_bpmn_processes = TaskService.task_models_of_parent_bpmn_processes(
# ) to_task_model
# [p.guid for p in task_models_of_parent_bpmn_processes if p.guid] )
# [p.id for p in parent_bpmn_processes] task_models_of_parent_bpmn_processes_guids = [p.guid for p in task_models_of_parent_bpmn_processes if p.guid]
# tasks_to_update_query = db.session.query(TaskModel).filter( parent_bpmn_processes_ids = [p.id for p in parent_bpmn_processes]
# and_(
# or_( tasks_to_update_query = db.session.query(TaskModel).filter(
# TaskModel.end_in_seconds > to_task_model.end_in_seconds, and_(
# TaskModel.end_in_seconds.is_(None), # type: ignore or_(
# ), TaskModel.end_in_seconds > to_task_model.end_in_seconds,
# TaskModel.process_instance_id == process_instance.id, TaskModel.end_in_seconds.is_(None), # type: ignore
# # TaskModel.bpmn_process_id.in_(parent_bpmn_processes_ids), # type: ignore ),
# ) TaskModel.process_instance_id == process_instance.id,
# ) TaskModel.bpmn_process_id.in_(parent_bpmn_processes_ids), # type: ignore
# tasks_to_update = tasks_to_update_query.all() )
# )
# # run all queries before making changes to task_model tasks_to_update = tasks_to_update_query.all()
# if commit: tasks_to_update_guids = [t.guid for t in tasks_to_update]
# # tasks_to_delete_query = db.session.query(TaskModel).filter(
# # and_( tasks_to_delete_query = db.session.query(TaskModel).filter(
# # or_( and_(
# # TaskModel.end_in_seconds > to_task_model.end_in_seconds, or_(
# # TaskModel.end_in_seconds.is_not(None), # type: ignore TaskModel.end_in_seconds > to_task_model.end_in_seconds,
# # ), TaskModel.end_in_seconds.is_not(None), # type: ignore
# # TaskModel.process_instance_id == process_instance.id, ),
# # TaskModel.guid.not_in(task_models_of_parent_bpmn_processes_guids), # type: ignore TaskModel.process_instance_id == process_instance.id,
# # TaskModel.bpmn_process_id.not_in(parent_bpmn_processes_ids), # type: ignore TaskModel.guid.not_in(task_models_of_parent_bpmn_processes_guids), # type: ignore
# # ) TaskModel.bpmn_process_id.not_in(parent_bpmn_processes_ids), # type: ignore
# # ) )
# # )
# # tasks_to_delete = tasks_to_delete_query.all() tasks_to_delete = tasks_to_delete_query.all()
# # tasks_to_delete_guids = [t.guid for t in tasks_to_delete]
# # # delete any later tasks from to_task_model and delete bpmn processes that may be tasks_to_delete_ids = [t.id for t in tasks_to_delete]
# # # link directly to one of those tasks.
# # tasks_to_delete_guids = [t.guid for t in tasks_to_delete] # delete bpmn processes that are also tasks that we either deleted or will update.
# # tasks_to_delete_ids = [t.id for t in tasks_to_delete] # this is to force spiff to recreate those bpmn processes with the correct associated task guids.
# # bpmn_processes_to_delete = BpmnProcessModel.query.filter( bpmn_processes_to_delete_query = db.session.query(BpmnProcessModel).filter(
# # BpmnProcessModel.guid.in_(tasks_to_delete_guids) # type: ignore or_(
# # ).order_by(BpmnProcessModel.id.desc()).all() BpmnProcessModel.guid.in_(tasks_to_delete_guids), # type: ignore
# # human_tasks_to_delete = HumanTaskModel.query.filter( and_(
# # HumanTaskModel.task_model_id.in_(tasks_to_delete_ids) # type: ignore BpmnProcessModel.guid.in_(tasks_to_update_guids), # type: ignore
# # ).all() BpmnProcessModel.id.not_in(parent_bpmn_processes_ids), # type: ignore
# # ),
# # )
# # import pdb; pdb.set_trace() )
# # # ensure the correct order for foreign keys bpmn_processes_to_delete = bpmn_processes_to_delete_query.order_by(
# # for human_task_to_delete in human_tasks_to_delete: BpmnProcessModel.id.desc() # type: ignore
# # db.session.delete(human_task_to_delete) ).all()
# # db.session.commit()
# # for task_to_delete in tasks_to_delete: # delete any human task that was for a task that we deleted since they will get recreated later.
# # db.session.delete(task_to_delete) human_tasks_to_delete = HumanTaskModel.query.filter(
# # db.session.commit() HumanTaskModel.task_model_id.in_(tasks_to_delete_ids) # type: ignore
# # for bpmn_process_to_delete in bpmn_processes_to_delete: ).all()
# # db.session.delete(bpmn_process_to_delete)
# # db.session.commit() # ensure the correct order for foreign keys
# for human_task_to_delete in human_tasks_to_delete:
# related_human_task = HumanTaskModel.query.filter_by(task_model_id=to_task_model.id).first() db.session.delete(human_task_to_delete)
# if related_human_task is not None: for task_to_delete in tasks_to_delete:
# db.session.delete(related_human_task) db.session.delete(task_to_delete)
# for bpmn_process_to_delete in bpmn_processes_to_delete:
# tasks_to_update_ids = [t.id for t in tasks_to_update] db.session.delete(bpmn_process_to_delete)
# human_tasks_to_delete = HumanTaskModel.query.filter(
# HumanTaskModel.task_model_id.in_(tasks_to_update_ids) # type: ignore related_human_task = HumanTaskModel.query.filter_by(task_model_id=to_task_model.id).first()
# ).all() if related_human_task is not None:
# for human_task_to_delete in human_tasks_to_delete: db.session.delete(related_human_task)
# db.session.delete(human_task_to_delete)
# db.session.commit() tasks_to_update_ids = [t.id for t in tasks_to_update]
# human_tasks_to_delete = HumanTaskModel.query.filter(
# for task_to_update in tasks_to_update: HumanTaskModel.task_model_id.in_(tasks_to_update_ids) # type: ignore
# # print(f"task_to_update: {task_to_update}") ).all()
# print(f"task_to_update.state: {task_to_update.state}") for human_task_to_delete in human_tasks_to_delete:
# TaskService.reset_task_model(task_to_update, state="FUTURE", commit=commit) db.session.delete(human_task_to_delete)
# # TaskService.reset_task_model(task_to_update, state=task_to_update.state, commit=commit)
# # if task_to_update.task_definition.bpmn_identifier != 'top_level_process_script_after_gate': for task_to_update in tasks_to_update:
# # TaskService.reset_task_model(task_to_update, state='FUTURE', commit=commit) TaskService.reset_task_model(task_to_update, state="FUTURE")
# # else: db.session.bulk_save_objects(tasks_to_update)
# # TaskService.reset_task_model(task_to_update, state=task_to_update.state, commit=commit)
# parent_task_model = TaskModel.query.filter_by(guid=to_task_model.properties_json["parent"]).first()
# parent_task_model = TaskModel.query.filter_by(guid=to_task_model.properties_json["parent"]).first() if parent_task_model is None:
# if parent_task_model is None: raise TaskNotFoundError(
# raise TaskNotFoundError( f"Cannot find a task with guid '{to_task_guid}' for process instance '{process_instance.id}'"
# f"Cannot find a task with guid '{to_task_guid}' for process instance '{process_instance.id}'" )
# )
# TaskService.reset_task_model(
# TaskService.reset_task_model( to_task_model,
# to_task_model, state="READY",
# state="READY", json_data_hash=parent_task_model.json_data_hash,
# json_data_hash=parent_task_model.json_data_hash, python_env_data_hash=parent_task_model.python_env_data_hash,
# python_env_data_hash=parent_task_model.python_env_data_hash, )
# commit=commit, db.session.add(to_task_model)
# ) for task_model in task_models_of_parent_bpmn_processes:
# for task_model in task_models_of_parent_bpmn_processes: TaskService.reset_task_model(task_model, state="WAITING")
# TaskService.reset_task_model(task_model, state="WAITING", commit=commit) db.session.bulk_save_objects(task_models_of_parent_bpmn_processes)
#
# bpmn_process = to_task_model.bpmn_process bpmn_process = to_task_model.bpmn_process
# properties_json = copy.copy(bpmn_process.properties_json) properties_json = copy.copy(bpmn_process.properties_json)
# properties_json["last_task"] = parent_task_model.guid properties_json["last_task"] = parent_task_model.guid
# bpmn_process.properties_json = properties_json bpmn_process.properties_json = properties_json
# db.session.add(bpmn_process) db.session.add(bpmn_process)
# db.session.commit() db.session.commit()
#
# if commit: processor = ProcessInstanceProcessor(process_instance)
# processor = ProcessInstanceProcessor(process_instance) processor.save()
# processor.save() processor.suspend()
# processor.suspend()
@staticmethod @staticmethod
def get_parser() -> MyCustomParser: def get_parser() -> MyCustomParser:

View File

@ -68,9 +68,9 @@ class TaskService:
spiff_task: SpiffTask, spiff_task: SpiffTask,
) -> None: ) -> None:
for child_spiff_task in spiff_task.children: for child_spiff_task in spiff_task.children:
# if child_spiff_task._has_state(TaskState.PREDICTED_MASK): if child_spiff_task._has_state(TaskState.PREDICTED_MASK):
# self.__class__.remove_spiff_task_from_parent(child_spiff_task, self.task_models) self.__class__.remove_spiff_task_from_parent(child_spiff_task, self.task_models)
# continue continue
self.update_task_model_with_spiff_task( self.update_task_model_with_spiff_task(
spiff_task=child_spiff_task, spiff_task=child_spiff_task,
) )
@ -157,7 +157,7 @@ class TaskService:
bpmn_process: BpmnProcessModel, bpmn_process: BpmnProcessModel,
) -> None: ) -> None:
new_properties_json = copy.copy(bpmn_process.properties_json) new_properties_json = copy.copy(bpmn_process.properties_json)
new_properties_json["last_task"] = str(spiff_workflow.last_task) if spiff_workflow.last_task else None new_properties_json["last_task"] = str(spiff_workflow.last_task.id) if spiff_workflow.last_task else None
new_properties_json["success"] = spiff_workflow.success new_properties_json["success"] = spiff_workflow.success
bpmn_process.properties_json = new_properties_json bpmn_process.properties_json = new_properties_json
@ -403,9 +403,9 @@ class TaskService:
# we are going to avoid saving likely and maybe tasks to the db. # we are going to avoid saving likely and maybe tasks to the db.
# that means we need to remove them from their parents' lists of children as well. # that means we need to remove them from their parents' lists of children as well.
spiff_task = spiff_workflow.get_task_from_id(UUID(task_id)) spiff_task = spiff_workflow.get_task_from_id(UUID(task_id))
# if spiff_task._has_state(TaskState.PREDICTED_MASK): if spiff_task._has_state(TaskState.PREDICTED_MASK):
# cls.remove_spiff_task_from_parent(spiff_task, new_task_models) cls.remove_spiff_task_from_parent(spiff_task, new_task_models)
# continue continue
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:
@ -504,7 +504,6 @@ class TaskService:
cls, cls,
task_model: TaskModel, task_model: TaskModel,
state: str, state: str,
commit: Optional[bool] = True,
json_data_hash: Optional[str] = None, json_data_hash: Optional[str] = None,
python_env_data_hash: Optional[str] = None, python_env_data_hash: Optional[str] = None,
) -> None: ) -> None:
@ -517,24 +516,14 @@ class TaskService:
else: else:
task_model.python_env_data_hash = python_env_data_hash task_model.python_env_data_hash = python_env_data_hash
new_properties_json = copy.copy(task_model.properties_json)
task_model.state = state task_model.state = state
task_model.start_in_seconds = None task_model.start_in_seconds = None
task_model.end_in_seconds = None task_model.end_in_seconds = None
if commit: new_properties_json = copy.copy(task_model.properties_json)
db.session.add(task_model)
db.session.commit()
new_properties_json["state"] = getattr(TaskState, state) new_properties_json["state"] = getattr(TaskState, state)
task_model.properties_json = new_properties_json task_model.properties_json = new_properties_json
if commit:
# if we commit the properties json at the same time as the other items
# the json gets reset for some reason.
db.session.add(task_model)
db.session.commit()
@classmethod @classmethod
def _create_task( def _create_task(
cls, cls,

View File

@ -12,10 +12,12 @@ from tests.spiffworkflow_backend.helpers.test_data import load_test_spec
from spiffworkflow_backend.exceptions.api_error import ApiError from spiffworkflow_backend.exceptions.api_error import ApiError
from spiffworkflow_backend.models.bpmn_process import BpmnProcessModel from spiffworkflow_backend.models.bpmn_process import BpmnProcessModel
from spiffworkflow_backend.models.db import db
from spiffworkflow_backend.models.group import GroupModel from spiffworkflow_backend.models.group import GroupModel
from spiffworkflow_backend.models.process_instance import ProcessInstanceModel from spiffworkflow_backend.models.process_instance import ProcessInstanceModel
from spiffworkflow_backend.models.process_instance import ProcessInstanceStatus from spiffworkflow_backend.models.process_instance import ProcessInstanceStatus
from spiffworkflow_backend.models.task import TaskModel # noqa: F401 from spiffworkflow_backend.models.task import TaskModel # noqa: F401
from spiffworkflow_backend.models.task_definition import TaskDefinitionModel
from spiffworkflow_backend.models.user import UserModel from spiffworkflow_backend.models.user import UserModel
from spiffworkflow_backend.services.authorization_service import AuthorizationService from spiffworkflow_backend.services.authorization_service import AuthorizationService
from spiffworkflow_backend.services.authorization_service import ( from spiffworkflow_backend.services.authorization_service import (
@ -254,128 +256,128 @@ class TestProcessInstanceProcessor(BaseTest):
assert spiff_task is not None assert spiff_task is not None
assert spiff_task.state == TaskState.COMPLETED assert spiff_task.state == TaskState.COMPLETED
# def test_properly_resets_process_to_given_task( def test_properly_resets_process_to_given_task(
# self, self,
# app: Flask, app: Flask,
# client: FlaskClient, client: FlaskClient,
# with_db_and_bpmn_file_cleanup: None, with_db_and_bpmn_file_cleanup: None,
# with_super_admin_user: UserModel, with_super_admin_user: UserModel,
# ) -> None: ) -> None:
# self.create_process_group_with_api(client, with_super_admin_user, "test_group", "test_group") self.create_process_group_with_api(client, with_super_admin_user, "test_group", "test_group")
# initiator_user = self.find_or_create_user("initiator_user") initiator_user = self.find_or_create_user("initiator_user")
# finance_user_three = self.find_or_create_user("testuser3") finance_user_three = self.find_or_create_user("testuser3")
# assert initiator_user.principal is not None assert initiator_user.principal is not None
# assert finance_user_three.principal is not None assert finance_user_three.principal is not None
# AuthorizationService.import_permissions_from_yaml_file() AuthorizationService.import_permissions_from_yaml_file()
#
# finance_group = GroupModel.query.filter_by(identifier="Finance Team").first() finance_group = GroupModel.query.filter_by(identifier="Finance Team").first()
# 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",
# process_model_source_directory="manual_task", 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
# ) )
# processor = ProcessInstanceProcessor(process_instance) processor = ProcessInstanceProcessor(process_instance)
# processor.do_engine_steps(save=True) processor.do_engine_steps(save=True)
# assert len(process_instance.active_human_tasks) == 1 assert len(process_instance.active_human_tasks) == 1
# initial_human_task_id = process_instance.active_human_tasks[0].id initial_human_task_id = process_instance.active_human_tasks[0].id
#
# # save again to ensure we go attempt to process the human tasks again # save again to ensure we go attempt to process the human tasks again
# processor.save() processor.save()
#
# assert len(process_instance.active_human_tasks) == 1 assert len(process_instance.active_human_tasks) == 1
# assert initial_human_task_id == process_instance.active_human_tasks[0].id assert initial_human_task_id == process_instance.active_human_tasks[0].id
#
# processor = ProcessInstanceProcessor(process_instance) processor = ProcessInstanceProcessor(process_instance)
# human_task_one = process_instance.active_human_tasks[0] human_task_one = process_instance.active_human_tasks[0]
# spiff_manual_task = processor.__class__.get_task_by_bpmn_identifier( spiff_manual_task = processor.__class__.get_task_by_bpmn_identifier(
# human_task_one.task_name, processor.bpmn_process_instance human_task_one.task_name, processor.bpmn_process_instance
# ) )
# assert spiff_manual_task is not None assert spiff_manual_task is not None
#
# processor.suspend() processor.suspend()
# ProcessInstanceProcessor.reset_process(process_instance, str(spiff_manual_task.parent.id), commit=True) ProcessInstanceProcessor.reset_process(process_instance, str(spiff_manual_task.parent.id))
#
# process_instance = ProcessInstanceModel.query.filter_by(id=process_instance.id).first() process_instance = ProcessInstanceModel.query.filter_by(id=process_instance.id).first()
# processor = ProcessInstanceProcessor(process_instance) processor = ProcessInstanceProcessor(process_instance)
# processor.resume() processor.resume()
# processor.do_engine_steps(save=True) processor.do_engine_steps(save=True)
# human_task_one = process_instance.active_human_tasks[0] human_task_one = process_instance.active_human_tasks[0]
# spiff_manual_task = processor.bpmn_process_instance.get_task_from_id(UUID(human_task_one.task_id)) spiff_manual_task = processor.bpmn_process_instance.get_task_from_id(UUID(human_task_one.task_id))
# ProcessInstanceService.complete_form_task(processor, spiff_manual_task, {}, initiator_user, human_task_one) ProcessInstanceService.complete_form_task(processor, spiff_manual_task, {}, initiator_user, human_task_one)
# assert process_instance.status == "complete" assert process_instance.status == "complete"
#
# def test_properly_resets_process_to_given_task_with_call_activity( def test_properly_resets_process_to_given_task_with_call_activity(
# self, self,
# app: Flask, app: Flask,
# client: FlaskClient, client: FlaskClient,
# with_db_and_bpmn_file_cleanup: None, with_db_and_bpmn_file_cleanup: None,
# with_super_admin_user: UserModel, with_super_admin_user: UserModel,
# ) -> None: ) -> None:
# self.create_process_group_with_api(client, with_super_admin_user, "test_group", "test_group") self.create_process_group_with_api(client, with_super_admin_user, "test_group", "test_group")
# initiator_user = self.find_or_create_user("initiator_user") initiator_user = self.find_or_create_user("initiator_user")
# finance_user_three = self.find_or_create_user("testuser3") finance_user_three = self.find_or_create_user("testuser3")
# assert initiator_user.principal is not None assert initiator_user.principal is not None
# assert finance_user_three.principal is not None assert finance_user_three.principal is not None
# AuthorizationService.import_permissions_from_yaml_file() AuthorizationService.import_permissions_from_yaml_file()
#
# finance_group = GroupModel.query.filter_by(identifier="Finance Team").first() finance_group = GroupModel.query.filter_by(identifier="Finance Team").first()
# 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_with_subprocesses", process_model_id="test_group/manual_task_with_subprocesses",
# process_model_source_directory="manual_task_with_subprocesses", process_model_source_directory="manual_task_with_subprocesses",
# ) )
# 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
# ) )
# processor = ProcessInstanceProcessor(process_instance) processor = ProcessInstanceProcessor(process_instance)
# processor.do_engine_steps(save=True) processor.do_engine_steps(save=True)
# # with open("before_reset.json", 'w') as f: f.write(json.dumps(processor.serialize(), indent=2)) assert len(process_instance.active_human_tasks) == 1
# assert len(process_instance.active_human_tasks) == 1 initial_human_task_id = process_instance.active_human_tasks[0].id
# initial_human_task_id = process_instance.active_human_tasks[0].id assert len(process_instance.active_human_tasks) == 1
# assert len(process_instance.active_human_tasks) == 1 assert initial_human_task_id == process_instance.active_human_tasks[0].id
# assert initial_human_task_id == process_instance.active_human_tasks[0].id
# human_task_one = process_instance.active_human_tasks[0]
# human_task_one = process_instance.active_human_tasks[0] spiff_manual_task = processor.bpmn_process_instance.get_task_from_id(UUID(human_task_one.task_id))
# spiff_manual_task = processor.bpmn_process_instance.get_task_from_id(UUID(human_task_one.task_id)) ProcessInstanceService.complete_form_task(processor, spiff_manual_task, {}, initiator_user, human_task_one)
# ProcessInstanceService.complete_form_task(processor, spiff_manual_task, {}, initiator_user, human_task_one) human_task_one = process_instance.active_human_tasks[0]
# human_task_one = process_instance.active_human_tasks[0] spiff_manual_task = processor.bpmn_process_instance.get_task_from_id(UUID(human_task_one.task_id))
# spiff_manual_task = processor.bpmn_process_instance.get_task_from_id(UUID(human_task_one.task_id)) ProcessInstanceService.complete_form_task(processor, spiff_manual_task, {}, initiator_user, human_task_one)
# ProcessInstanceService.complete_form_task(processor, spiff_manual_task, {}, initiator_user, human_task_one)
# processor.suspend()
# # NOTES: task_model_to_reset_to = (
# # somehow we are hosing the task state so that when completing tasks of a subprocess, the task AFTER the subprocess task TaskModel.query.join(TaskDefinitionModel)
# # is not marked READY but instead stays as FUTURE. Running things like: .filter(TaskDefinitionModel.bpmn_identifier == "top_level_subprocess_script")
# # self.last_completed_spiff_task.task_spec._update(self.last_completed_spiff_task) .order_by(TaskModel.id.desc()) # type: ignore
# # and .first()
# # self.last_completed_spiff_task.task_spec._predict(self.last_completed_spiff_task, mask=TaskState.NOT_FINISHED_MASK) )
# # did not help. assert task_model_to_reset_to is not None
# ProcessInstanceProcessor.reset_process(process_instance, task_model_to_reset_to.guid)
# processor.suspend()
# task_model_to_reset_to = ( # make sure sqlalchemy session matches current db state
# TaskModel.query.join(TaskDefinitionModel) db.session.expire_all()
# .filter(TaskDefinitionModel.bpmn_identifier == "top_level_subprocess_script") process_instance = ProcessInstanceModel.query.filter_by(id=process_instance.id).first()
# .order_by(TaskModel.id.desc()) # type: ignore processor = ProcessInstanceProcessor(process_instance)
# .first()
# ) # make sure we reset to the task we expected
# assert task_model_to_reset_to is not None ready_or_waiting_tasks = processor.get_all_ready_or_waiting_tasks()
# ProcessInstanceProcessor.reset_process(process_instance, task_model_to_reset_to.guid, commit=True) top_level_subprocess_script_spiff_task = next(
# task for task in ready_or_waiting_tasks if task.task_spec.name == "top_level_subprocess_script"
# process_instance = ProcessInstanceModel.query.filter_by(id=process_instance.id).first() )
# processor = ProcessInstanceProcessor(process_instance) assert top_level_subprocess_script_spiff_task is not None
# processor.resume() processor.resume()
# processor.do_engine_steps(save=True) processor.do_engine_steps(save=True)
#
# assert len(process_instance.active_human_tasks) == 1 assert len(process_instance.active_human_tasks) == 1
# human_task_one = process_instance.active_human_tasks[0] human_task_one = process_instance.active_human_tasks[0]
# spiff_manual_task = processor.bpmn_process_instance.get_task_from_id(UUID(human_task_one.task_id)) spiff_manual_task = processor.bpmn_process_instance.get_task_from_id(UUID(human_task_one.task_id))
# ProcessInstanceService.complete_form_task(processor, spiff_manual_task, {}, initiator_user, human_task_one) ProcessInstanceService.complete_form_task(processor, spiff_manual_task, {}, initiator_user, human_task_one)
#
# assert process_instance.status == "complete" assert process_instance.status == "complete"
def test_properly_saves_tasks_when_running( def test_properly_saves_tasks_when_running(
self, self,
@ -510,18 +512,17 @@ class TestProcessInstanceProcessor(BaseTest):
f" {expected_task_data_key}." f" {expected_task_data_key}."
) )
# TODO: add back in when removing MAYBE and LIKELY tasks count_failure_message = (
# count_failure_message = ( f"{base_failure_message} There are more than 2 entries of this task in the db."
# f"{base_failure_message} There are more than 2 entries of this task in the db." " There should only ever be max 2."
# " There should only ever be max 2." )
# ) task_models_with_bpmn_identifier_count = (
# task_models_with_bpmn_identifier_count = ( TaskModel.query.join(TaskDefinitionModel)
# TaskModel.query.join(TaskDefinitionModel) .filter(TaskModel.process_instance_id == process_instance_relookup.id)
# .filter(TaskModel.process_instance_id == process_instance_relookup.id) .filter(TaskDefinitionModel.bpmn_identifier == spiff_task.task_spec.name)
# .filter(TaskDefinitionModel.bpmn_identifier == spiff_task.task_spec.name) .count()
# .count() )
# ) assert task_models_with_bpmn_identifier_count < 3, count_failure_message
# assert task_models_with_bpmn_identifier_count < 3, count_failure_message
task_model = TaskModel.query.filter_by(guid=str(spiff_task.id)).first() task_model = TaskModel.query.filter_by(guid=str(spiff_task.id)).first()
assert task_model.start_in_seconds is not None assert task_model.start_in_seconds is not None
@ -582,13 +583,12 @@ class TestProcessInstanceProcessor(BaseTest):
) )
assert task_bpmn_identifier in spiff_tasks_checked, message assert task_bpmn_identifier in spiff_tasks_checked, message
# TODO: add back in when removing MAYBE and LIKELY tasks task_models_that_are_predicted_count = (
# task_models_that_are_predicted_count = ( TaskModel.query.filter(TaskModel.process_instance_id == process_instance_relookup.id)
# TaskModel.query.filter(TaskModel.process_instance_id == process_instance_relookup.id) .filter(TaskModel.state.in_(["LIKELY", "MAYBE"])) # type: ignore
# .filter(TaskModel.state.in_(["LIKELY", "MAYBE"])) # type: ignore .count()
# .count() )
# ) assert task_models_that_are_predicted_count == 0
# assert task_models_that_are_predicted_count == 0
assert processor.get_data() == data_set_7 assert processor.get_data() == data_set_7

View File

@ -236,8 +236,7 @@ export default function ProcessInstanceShow({ variant }: OwnProps) {
tasks.forEach(function getUserTasksElement(task: Task) { tasks.forEach(function getUserTasksElement(task: Task) {
if (task.state === 'COMPLETED') { if (task.state === 'COMPLETED') {
taskIds.completed.push(task); taskIds.completed.push(task);
} } else if (task.state === 'READY' || task.state === 'WAITING') {
if (task.state === 'READY' || task.state === 'WAITING') {
taskIds.readyOrWaiting.push(task); taskIds.readyOrWaiting.push(task);
} }
return null; return null;
@ -674,16 +673,14 @@ export default function ProcessInstanceShow({ variant }: OwnProps) {
); );
}; };
const canResetProcess = (_task: Task) => { const canResetProcess = (task: Task) => {
// disabling this feature for now return (
return false; ability.can('POST', targetUris.processInstanceResetPath) &&
// return ( processInstance &&
// ability.can('POST', targetUris.processInstanceResetPath) && processInstance.status === 'suspended' &&
// processInstance && task.state === 'READY' &&
// processInstance.status === 'suspended' && !showingActiveTask()
// task.state === 'READY' && );
// !showingActiveTask()
// );
}; };
const getEvents = (task: Task) => { const getEvents = (task: Task) => {