From 59aacf05a8aa685c6882f444198f305bd6157c3d Mon Sep 17 00:00:00 2001 From: jasquat Date: Mon, 28 Nov 2022 13:41:30 -0500 Subject: [PATCH 1/3] only delete active tasks if needed w/ burnettk cullerton --- .../services/process_instance_processor.py | 50 +++++++++++-------- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py index 111d01ec1..f6ac3be3d 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py @@ -590,16 +590,12 @@ class ProcessInstanceProcessor: if self.bpmn_process_instance.is_completed(): self.process_instance_model.end_in_seconds = round(time.time()) - active_tasks = ActiveTaskModel.query.filter_by( - process_instance_id=self.process_instance_model.id - ).all() - if len(active_tasks) > 0: - for at in active_tasks: - db.session.delete(at) - db.session.add(self.process_instance_model) db.session.commit() + active_tasks = ActiveTaskModel.query.filter_by( + process_instance_id=self.process_instance_model.id + ).all() ready_or_waiting_tasks = self.get_all_ready_or_waiting_tasks() for ready_or_waiting_task in ready_or_waiting_tasks: # filter out non-usertasks @@ -626,20 +622,27 @@ class ProcessInstanceProcessor: if process_model_info is not None: process_model_display_name = process_model_info.display_name - active_task = ActiveTaskModel( - process_instance_id=self.process_instance_model.id, - process_model_display_name=process_model_display_name, - form_file_name=form_file_name, - ui_form_file_name=ui_form_file_name, - task_id=str(ready_or_waiting_task.id), - task_name=ready_or_waiting_task.task_spec.name, - task_title=ready_or_waiting_task.task_spec.description, - task_type=ready_or_waiting_task.task_spec.__class__.__name__, - task_status=ready_or_waiting_task.get_state_name(), - lane_assignment_id=potential_owner_hash["lane_assignment_id"], - ) - db.session.add(active_task) - db.session.commit() + active_task = None + for at in active_tasks: + if at.task_id == str(ready_or_waiting_task.id): + active_task = at + active_tasks.pop(at) + + if active_task is None: + active_task = ActiveTaskModel( + process_instance_id=self.process_instance_model.id, + process_model_display_name=process_model_display_name, + form_file_name=form_file_name, + ui_form_file_name=ui_form_file_name, + task_id=str(ready_or_waiting_task.id), + task_name=ready_or_waiting_task.task_spec.name, + task_title=ready_or_waiting_task.task_spec.description, + task_type=ready_or_waiting_task.task_spec.__class__.__name__, + task_status=ready_or_waiting_task.get_state_name(), + lane_assignment_id=potential_owner_hash["lane_assignment_id"], + ) + db.session.add(active_task) + db.session.commit() for potential_owner_id in potential_owner_hash["potential_owner_ids"]: active_task_user = ActiveTaskUserModel( @@ -648,6 +651,11 @@ class ProcessInstanceProcessor: db.session.add(active_task_user) db.session.commit() + if len(active_tasks) > 0: + for at in active_tasks: + db.session.delete(at) + db.session.commit() + @staticmethod def get_parser() -> MyCustomParser: """Get_parser.""" From 0eeb096d433fca4273893d0836537ac17f0d6409 Mon Sep 17 00:00:00 2001 From: jasquat Date: Mon, 28 Nov 2022 15:26:50 -0500 Subject: [PATCH 2/3] added script to save process instance metadata and fixed permissions issue w/ burnettk cullerton --- bin/run_pyl | 7 +++ .../{568f5fe2c9f8_.py => ff1c1628337c_.py} | 43 +++++++++------ .../config/permissions/development.yml | 51 ++++++++---------- .../terraform_deployed_environment.yml | 51 ++++++++---------- .../load_database_models.py | 3 ++ .../models/process_instance.py | 4 ++ .../models/process_instance_metadata.py | 30 +++++++++++ .../models/spiff_step_details.py | 5 +- .../scripts/save_process_instance_metadata.py | 35 +++++++++++++ .../save_process_instance_metadata.bpmn | 52 +++++++++++++++++++ .../test_save_process_instance_metadata.py | 42 +++++++++++++++ .../src/components/ProcessModelListTiles.tsx | 1 + 12 files changed, 248 insertions(+), 76 deletions(-) rename spiffworkflow-backend/migrations/versions/{568f5fe2c9f8_.py => ff1c1628337c_.py} (95%) create mode 100644 spiffworkflow-backend/src/spiffworkflow_backend/models/process_instance_metadata.py create mode 100644 spiffworkflow-backend/src/spiffworkflow_backend/scripts/save_process_instance_metadata.py create mode 100644 spiffworkflow-backend/tests/data/save_process_instance_metadata/save_process_instance_metadata.bpmn create mode 100644 spiffworkflow-backend/tests/spiffworkflow_backend/scripts/test_save_process_instance_metadata.py diff --git a/bin/run_pyl b/bin/run_pyl index af82c6a6a..d3abd7fc6 100755 --- a/bin/run_pyl +++ b/bin/run_pyl @@ -20,6 +20,12 @@ function get_python_dirs() { (git ls-tree -r HEAD --name-only | grep -E '\.py$' | awk -F '/' '{print $1}' | sort | uniq | grep -v '\.' | grep -Ev '^(bin|migrations)$') || echo '' } +function run_fix_docstrings() { + if command -v fix_python_docstrings >/dev/null ; then + fix_python_docstrings $(get_top_level_directories_containing_python_files) + fi +} + function run_autoflake() { if ! command -v autoflake8 >/dev/null ; then pip install autoflake8 @@ -51,6 +57,7 @@ done for python_project in "${python_projects[@]}" ; do pushd "$python_project" + run_fix_docstrings || run_fix_docstrings run_autoflake || run_autoflake popd done diff --git a/spiffworkflow-backend/migrations/versions/568f5fe2c9f8_.py b/spiffworkflow-backend/migrations/versions/ff1c1628337c_.py similarity index 95% rename from spiffworkflow-backend/migrations/versions/568f5fe2c9f8_.py rename to spiffworkflow-backend/migrations/versions/ff1c1628337c_.py index bfd7fc2b1..d8da6d3c4 100644 --- a/spiffworkflow-backend/migrations/versions/568f5fe2c9f8_.py +++ b/spiffworkflow-backend/migrations/versions/ff1c1628337c_.py @@ -1,8 +1,8 @@ """empty message -Revision ID: 568f5fe2c9f8 +Revision ID: ff1c1628337c Revises: -Create Date: 2022-11-24 12:11:46.669020 +Create Date: 2022-11-28 15:08:52.014254 """ from alembic import op @@ -10,7 +10,7 @@ import sqlalchemy as sa # revision identifiers, used by Alembic. -revision = '568f5fe2c9f8' +revision = 'ff1c1628337c' down_revision = None branch_labels = None depends_on = None @@ -166,17 +166,6 @@ def upgrade(): sa.PrimaryKeyConstraint('id'), sa.UniqueConstraint('key') ) - op.create_table('spiff_step_details', - sa.Column('id', sa.Integer(), nullable=False), - sa.Column('process_instance_id', sa.Integer(), nullable=False), - sa.Column('spiff_step', sa.Integer(), nullable=False), - sa.Column('task_json', sa.JSON(), nullable=False), - sa.Column('timestamp', sa.DECIMAL(precision=17, scale=6), nullable=False), - sa.Column('completed_by_user_id', sa.Integer(), nullable=True), - sa.Column('lane_assignment_id', sa.Integer(), nullable=True), - sa.ForeignKeyConstraint(['lane_assignment_id'], ['group.id'], ), - sa.PrimaryKeyConstraint('id') - ) op.create_table('user_group_assignment', sa.Column('id', sa.Integer(), nullable=False), sa.Column('user_id', sa.Integer(), nullable=False), @@ -249,6 +238,29 @@ def upgrade(): sa.PrimaryKeyConstraint('id'), sa.UniqueConstraint('principal_id', 'permission_target_id', 'permission', name='permission_assignment_uniq') ) + op.create_table('process_instance_metadata', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('process_instance_id', sa.Integer(), nullable=False), + sa.Column('key', sa.String(length=255), nullable=False), + sa.Column('value', sa.String(length=255), nullable=False), + sa.Column('updated_at_in_seconds', sa.Integer(), nullable=False), + sa.Column('created_at_in_seconds', sa.Integer(), nullable=False), + sa.ForeignKeyConstraint(['process_instance_id'], ['process_instance.id'], ), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('process_instance_id', 'key', name='process_instance_metadata_unique') + ) + op.create_table('spiff_step_details', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('process_instance_id', sa.Integer(), nullable=False), + sa.Column('spiff_step', sa.Integer(), nullable=False), + sa.Column('task_json', sa.JSON(), nullable=False), + sa.Column('timestamp', sa.DECIMAL(precision=17, scale=6), nullable=False), + sa.Column('completed_by_user_id', sa.Integer(), nullable=True), + sa.Column('lane_assignment_id', sa.Integer(), nullable=True), + sa.ForeignKeyConstraint(['lane_assignment_id'], ['group.id'], ), + sa.ForeignKeyConstraint(['process_instance_id'], ['process_instance.id'], ), + sa.PrimaryKeyConstraint('id') + ) op.create_table('active_task_user', sa.Column('id', sa.Integer(), nullable=False), sa.Column('active_task_id', sa.Integer(), nullable=False), @@ -282,6 +294,8 @@ def downgrade(): op.drop_index(op.f('ix_active_task_user_user_id'), table_name='active_task_user') op.drop_index(op.f('ix_active_task_user_active_task_id'), table_name='active_task_user') op.drop_table('active_task_user') + op.drop_table('spiff_step_details') + op.drop_table('process_instance_metadata') op.drop_table('permission_assignment') op.drop_table('message_instance') op.drop_index(op.f('ix_message_correlation_value'), table_name='message_correlation') @@ -291,7 +305,6 @@ def downgrade(): op.drop_table('message_correlation') op.drop_table('active_task') op.drop_table('user_group_assignment') - op.drop_table('spiff_step_details') op.drop_table('secret') op.drop_table('refresh_token') op.drop_index(op.f('ix_process_instance_report_identifier'), table_name='process_instance_report') diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/development.yml b/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/development.yml index 91207f86f..e17e3f110 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/development.yml +++ b/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/development.yml @@ -136,47 +136,38 @@ permissions: allowed_permissions: [create, read, update, delete] uri: /v1.0/process-groups/manage-procurement:procurement:* - demo-models-instantiate-vendor-block: - groups: ["demo"] + manage-revenue-streams-instantiate: + groups: ["core-contributor", "demo"] users: [] allowed_permissions: [create] - uri: /v1.0/process-models/manage-procurement:vendor-lifecycle-management:vendor-md-maintenance:vendor-md-block/process-instances - demo-models-instantiate-vendor-change: - groups: ["demo"] + uri: /v1.0/process-models/manage-revenue-streams:product-revenue-streams:customer-contracts-trade-terms/* + manage-revenue-streams-instances: + groups: ["core-contributor", "demo"] users: [] - allowed_permissions: [create] - uri: /v1.0/process-models/manage-procurement:vendor-lifecycle-management:vendor-md-maintenance:vendor-md-change/process-instances - demo-models-instantiate-vendor-creation: - groups: ["demo"] - users: [] - allowed_permissions: [create] - uri: /v1.0/process-models/manage-procurement:vendor-lifecycle-management:vendor-md-maintenance:vendor-md-creation/process-instances - demo-models-instantiate-vendor-core-invoice_appoval: - groups: ["demo"] - users: [] - allowed_permissions: [create] - uri: /v1.0/process-models/manage-procurement:procurement:core-contributor-invoice-management:cc-invoice-approval/process-instances - demo-models-customer-contracts: - groups: ["demo"] - users: [] - allowed_permissions: [create] - uri: /v1.0/process-models/manage-revenue-streams:product-revenue-streams:customer-contracts-trade-terms/process-instances + allowed_permissions: [create, read] + uri: /v1.0/process-instances/manage-revenue-streams:product-revenue-streams:customer-contracts-trade-terms/* - core-admin-models-instantiate: - groups: ["core-contributor"] + manage-procurement-invoice-instantiate: + groups: ["core-contributor", "demo"] users: [] allowed_permissions: [create] - uri: /v1.0/process-models/manage-procurement:procurement:core-contributor-invoice-management:cc-invoice-approval/process-instances - core-admin-instances: - groups: ["core-contributor"] + uri: /v1.0/process-models/manage-procurement:procurement:core-contributor-invoice-management:* + manage-procurement-invoice-instances: + groups: ["core-contributor", "demo"] users: [] allowed_permissions: [create, read] uri: /v1.0/process-instances/manage-procurement:procurement:core-contributor-invoice-management:* - core-admin-instances-slash: - groups: ["core-contributor"] + + manage-procurement-instantiate: + groups: ["core-contributor", "demo"] + users: [] + allowed_permissions: [create] + uri: /v1.0/process-models/manage-procurement:vendor-lifecycle-management:* + manage-procurement-instances: + groups: ["core-contributor", "demo"] users: [] allowed_permissions: [create, read] - uri: /v1.0/process-instances/manage-procurement:procurement:core-contributor-invoice-management/* + uri: /v1.0/process-instances/manage-procurement:vendor-lifecycle-management:* core1-admin-models-instantiate: groups: ["core-contributor"] diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/terraform_deployed_environment.yml b/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/terraform_deployed_environment.yml index c1a0579cf..e60946b3c 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/terraform_deployed_environment.yml +++ b/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/terraform_deployed_environment.yml @@ -136,44 +136,35 @@ permissions: allowed_permissions: [create, read, update, delete] uri: /v1.0/process-groups/manage-procurement:procurement:* - demo-models-instantiate-vendor-block: - groups: ["demo"] + manage-revenue-streams-instantiate: + groups: ["core-contributor", "demo"] users: [] allowed_permissions: [create] - uri: /v1.0/process-models/manage-procurement:vendor-lifecycle-management:vendor-md-maintenance:vendor-md-block/process-instances - demo-models-instantiate-vendor-change: - groups: ["demo"] + uri: /v1.0/process-models/manage-revenue-streams:product-revenue-streams:customer-contracts-trade-terms/* + manage-revenue-streams-instances: + groups: ["core-contributor", "demo"] users: [] - allowed_permissions: [create] - uri: /v1.0/process-models/manage-procurement:vendor-lifecycle-management:vendor-md-maintenance:vendor-md-change/process-instances - demo-models-instantiate-vendor-creation: - groups: ["demo"] - users: [] - allowed_permissions: [create] - uri: /v1.0/process-models/manage-procurement:vendor-lifecycle-management:vendor-md-maintenance:vendor-md-creation/process-instances - demo-models-instantiate-vendor-core-invoice_appoval: - groups: ["demo"] - users: [] - allowed_permissions: [create] - uri: /v1.0/process-models/manage-procurement:procurement:core-contributor-invoice-management:cc-invoice-approval/process-instances - demo-models-customer-contracts: - groups: ["demo"] - users: [] - allowed_permissions: [create] - uri: /v1.0/process-models/manage-revenue-streams:product-revenue-streams:customer-contracts-trade-terms/process-instances + allowed_permissions: [create, read] + uri: /v1.0/process-instances/manage-revenue-streams:product-revenue-streams:customer-contracts-trade-terms/* - core-admin-models-instantiate: - groups: ["core-contributor"] + manage-procurement-invoice-instantiate: + groups: ["core-contributor", "demo"] users: [] allowed_permissions: [create] - uri: /v1.0/process-models/manage-procurement:procurement:core-contributor-invoice-management:cc-invoice-approval/process-instances - core-admin-instances: - groups: ["core-contributor"] + uri: /v1.0/process-models/manage-procurement:procurement:core-contributor-invoice-management:* + manage-procurement-invoice-instances: + groups: ["core-contributor", "demo"] users: [] allowed_permissions: [create, read] uri: /v1.0/process-instances/manage-procurement:procurement:core-contributor-invoice-management:* - core-admin-instances-slash: - groups: ["core-contributor"] + + manage-procurement-instantiate: + groups: ["core-contributor", "demo"] + users: [] + allowed_permissions: [create] + uri: /v1.0/process-models/manage-procurement:vendor-lifecycle-management:* + manage-procurement-instances: + groups: ["core-contributor", "demo"] users: [] allowed_permissions: [create, read] - uri: /v1.0/process-instances/manage-procurement:procurement:core-contributor-invoice-management/* + uri: /v1.0/process-instances/manage-procurement:vendor-lifecycle-management:* diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/load_database_models.py b/spiffworkflow-backend/src/spiffworkflow_backend/load_database_models.py index 97c990004..71adb57c6 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/load_database_models.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/load_database_models.py @@ -51,5 +51,8 @@ from spiffworkflow_backend.models.spiff_step_details import ( ) # noqa: F401 from spiffworkflow_backend.models.user import UserModel # noqa: F401 from spiffworkflow_backend.models.group import GroupModel # noqa: F401 +from spiffworkflow_backend.models.process_instance_metadata import ( + ProcessInstanceMetadataModel, +) # noqa: F401 add_listeners() diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/models/process_instance.py b/spiffworkflow-backend/src/spiffworkflow_backend/models/process_instance.py index f06eb953c..e6a5f6849 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/models/process_instance.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/models/process_instance.py @@ -22,6 +22,10 @@ from spiffworkflow_backend.models.task import TaskSchema from spiffworkflow_backend.models.user import UserModel +class ProcessInstanceNotFoundError(Exception): + """ProcessInstanceNotFoundError.""" + + class NavigationItemSchema(Schema): """NavigationItemSchema.""" diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/models/process_instance_metadata.py b/spiffworkflow-backend/src/spiffworkflow_backend/models/process_instance_metadata.py new file mode 100644 index 000000000..5a4d4ca5b --- /dev/null +++ b/spiffworkflow-backend/src/spiffworkflow_backend/models/process_instance_metadata.py @@ -0,0 +1,30 @@ +"""Spiff_step_details.""" +from dataclasses import dataclass + +from flask_bpmn.models.db import db +from flask_bpmn.models.db import SpiffworkflowBaseDBModel +from sqlalchemy import ForeignKey + +from spiffworkflow_backend.models.process_instance import ProcessInstanceModel + + +@dataclass +class ProcessInstanceMetadataModel(SpiffworkflowBaseDBModel): + """ProcessInstanceMetadataModel.""" + + __tablename__ = "process_instance_metadata" + __table_args__ = ( + db.UniqueConstraint( + "process_instance_id", "key", name="process_instance_metadata_unique" + ), + ) + + id: int = db.Column(db.Integer, primary_key=True) + process_instance_id: int = db.Column( + ForeignKey(ProcessInstanceModel.id), nullable=False # type: ignore + ) + key: str = db.Column(db.String(255), nullable=False) + value: str = db.Column(db.String(255), nullable=False) + + updated_at_in_seconds: int = db.Column(db.Integer, nullable=False) + created_at_in_seconds: int = db.Column(db.Integer, nullable=False) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/models/spiff_step_details.py b/spiffworkflow-backend/src/spiffworkflow_backend/models/spiff_step_details.py index e00e7cacf..91d70116a 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/models/spiff_step_details.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/models/spiff_step_details.py @@ -8,6 +8,7 @@ from sqlalchemy import ForeignKey from sqlalchemy.orm import deferred from spiffworkflow_backend.models.group import GroupModel +from spiffworkflow_backend.models.process_instance import ProcessInstanceModel @dataclass @@ -16,7 +17,9 @@ class SpiffStepDetailsModel(SpiffworkflowBaseDBModel): __tablename__ = "spiff_step_details" id: int = db.Column(db.Integer, primary_key=True) - process_instance_id: int = db.Column(db.Integer, nullable=False) + process_instance_id: int = db.Column( + ForeignKey(ProcessInstanceModel.id), nullable=False # type: ignore + ) spiff_step: int = db.Column(db.Integer, nullable=False) task_json: str = deferred(db.Column(db.JSON, nullable=False)) # type: ignore timestamp: float = db.Column(db.DECIMAL(17, 6), nullable=False) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/scripts/save_process_instance_metadata.py b/spiffworkflow-backend/src/spiffworkflow_backend/scripts/save_process_instance_metadata.py new file mode 100644 index 000000000..7176f6a5a --- /dev/null +++ b/spiffworkflow-backend/src/spiffworkflow_backend/scripts/save_process_instance_metadata.py @@ -0,0 +1,35 @@ +"""Get_env.""" +from typing import Any +from flask_bpmn.models.db import db + +from spiffworkflow_backend.models.process_instance_metadata import ProcessInstanceMetadataModel +from spiffworkflow_backend.models.script_attributes_context import ( + ScriptAttributesContext, +) +from spiffworkflow_backend.scripts.script import Script + + +class SaveProcessInstanceMetadata(Script): + """SaveProcessInstanceMetadata.""" + + def get_description(self) -> str: + """Get_description.""" + return """Save a given dict as process instance metadata (useful for creating reports).""" + + def run( + self, + script_attributes_context: ScriptAttributesContext, + *args: Any, + **kwargs: Any, + ) -> Any: + """Run.""" + metadata_dict = args[0] + for key, value in metadata_dict.items(): + pim = ProcessInstanceMetadataModel.query.filter_by( + process_instance_id=script_attributes_context.process_instance_id, key=key).first() + if pim is None: + pim = ProcessInstanceMetadataModel( + process_instance_id=script_attributes_context.process_instance_id, key=key) + pim.value = value + db.session.add(pim) + db.session.commit() diff --git a/spiffworkflow-backend/tests/data/save_process_instance_metadata/save_process_instance_metadata.bpmn b/spiffworkflow-backend/tests/data/save_process_instance_metadata/save_process_instance_metadata.bpmn new file mode 100644 index 000000000..2c72b08d1 --- /dev/null +++ b/spiffworkflow-backend/tests/data/save_process_instance_metadata/save_process_instance_metadata.bpmn @@ -0,0 +1,52 @@ + + + + + Flow_1j4jzft + + + + Flow_01xr2ac + + + Flow_1j4jzft + Flow_10xyk22 + save_process_instance_metadata({"key1": "value1"}) + + + + Flow_10xyk22 + Flow_01xr2ac + save_process_instance_metadata({"key2": "value2", "key3": "value3"}) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/scripts/test_save_process_instance_metadata.py b/spiffworkflow-backend/tests/spiffworkflow_backend/scripts/test_save_process_instance_metadata.py new file mode 100644 index 000000000..62823f15f --- /dev/null +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/scripts/test_save_process_instance_metadata.py @@ -0,0 +1,42 @@ +"""Test_get_localtime.""" +from flask.app import Flask +from flask.testing import FlaskClient +from tests.spiffworkflow_backend.helpers.base_test import BaseTest +from tests.spiffworkflow_backend.helpers.test_data import load_test_spec + +from spiffworkflow_backend.models.process_instance_metadata import ProcessInstanceMetadataModel +from spiffworkflow_backend.models.user import UserModel +from spiffworkflow_backend.services.process_instance_processor import ( + ProcessInstanceProcessor, +) + + +class TestSaveProcessInstanceMetadata(BaseTest): + """TestSaveProcessInstanceMetadata.""" + + def test_can_save_process_instance_metadata( + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, + ) -> None: + """Test_can_save_process_instance_metadata.""" + initiator_user = self.find_or_create_user("initiator_user") + self.create_process_group( + client, with_super_admin_user, "test_group", "test_group" + ) + process_model = load_test_spec( + process_model_id="save_process_instance_metadata/save_process_instance_metadata", + bpmn_file_name="save_process_instance_metadata.bpmn", + process_model_source_directory="save_process_instance_metadata", + ) + process_instance = self.create_process_instance_from_process_model( + process_model=process_model, user=initiator_user + ) + processor = ProcessInstanceProcessor(process_instance) + processor.do_engine_steps(save=True) + + process_instance_metadata = ProcessInstanceMetadataModel.query.filter_by( + process_instance_id=process_instance.id).all() + assert len(process_instance_metadata) == 3 diff --git a/spiffworkflow-frontend/src/components/ProcessModelListTiles.tsx b/spiffworkflow-frontend/src/components/ProcessModelListTiles.tsx index 3022fadc1..4787fe94e 100644 --- a/spiffworkflow-frontend/src/components/ProcessModelListTiles.tsx +++ b/spiffworkflow-frontend/src/components/ProcessModelListTiles.tsx @@ -81,6 +81,7 @@ export default function ProcessModelListTiles({
Date: Mon, 28 Nov 2022 15:47:56 -0500 Subject: [PATCH 3/3] fixed issue ensuring active tasks are up to date w/ burnettk cullerton --- .../scripts/save_process_instance_metadata.py | 13 ++++-- .../services/process_instance_processor.py | 16 ++++---- .../test_save_process_instance_metadata.py | 7 +++- .../unit/test_process_instance_processor.py | 40 +++++++++++++++++++ 4 files changed, 64 insertions(+), 12 deletions(-) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/scripts/save_process_instance_metadata.py b/spiffworkflow-backend/src/spiffworkflow_backend/scripts/save_process_instance_metadata.py index 7176f6a5a..ae5fe00ef 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/scripts/save_process_instance_metadata.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/scripts/save_process_instance_metadata.py @@ -1,8 +1,11 @@ """Get_env.""" from typing import Any + from flask_bpmn.models.db import db -from spiffworkflow_backend.models.process_instance_metadata import ProcessInstanceMetadataModel +from spiffworkflow_backend.models.process_instance_metadata import ( + ProcessInstanceMetadataModel, +) from spiffworkflow_backend.models.script_attributes_context import ( ScriptAttributesContext, ) @@ -26,10 +29,14 @@ class SaveProcessInstanceMetadata(Script): metadata_dict = args[0] for key, value in metadata_dict.items(): pim = ProcessInstanceMetadataModel.query.filter_by( - process_instance_id=script_attributes_context.process_instance_id, key=key).first() + process_instance_id=script_attributes_context.process_instance_id, + key=key, + ).first() if pim is None: pim = ProcessInstanceMetadataModel( - process_instance_id=script_attributes_context.process_instance_id, key=key) + process_instance_id=script_attributes_context.process_instance_id, + key=key, + ) pim.value = value db.session.add(pim) db.session.commit() diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py index f6ac3be3d..9b6ed01b9 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py @@ -626,7 +626,7 @@ class ProcessInstanceProcessor: for at in active_tasks: if at.task_id == str(ready_or_waiting_task.id): active_task = at - active_tasks.pop(at) + active_tasks.remove(at) if active_task is None: active_task = ActiveTaskModel( @@ -644,12 +644,14 @@ class ProcessInstanceProcessor: db.session.add(active_task) db.session.commit() - for potential_owner_id in potential_owner_hash["potential_owner_ids"]: - active_task_user = ActiveTaskUserModel( - user_id=potential_owner_id, active_task_id=active_task.id - ) - db.session.add(active_task_user) - db.session.commit() + for potential_owner_id in potential_owner_hash[ + "potential_owner_ids" + ]: + active_task_user = ActiveTaskUserModel( + user_id=potential_owner_id, active_task_id=active_task.id + ) + db.session.add(active_task_user) + db.session.commit() if len(active_tasks) > 0: for at in active_tasks: diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/scripts/test_save_process_instance_metadata.py b/spiffworkflow-backend/tests/spiffworkflow_backend/scripts/test_save_process_instance_metadata.py index 62823f15f..96eb62970 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/scripts/test_save_process_instance_metadata.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/scripts/test_save_process_instance_metadata.py @@ -4,7 +4,9 @@ from flask.testing import FlaskClient from tests.spiffworkflow_backend.helpers.base_test import BaseTest from tests.spiffworkflow_backend.helpers.test_data import load_test_spec -from spiffworkflow_backend.models.process_instance_metadata import ProcessInstanceMetadataModel +from spiffworkflow_backend.models.process_instance_metadata import ( + ProcessInstanceMetadataModel, +) from spiffworkflow_backend.models.user import UserModel from spiffworkflow_backend.services.process_instance_processor import ( ProcessInstanceProcessor, @@ -38,5 +40,6 @@ class TestSaveProcessInstanceMetadata(BaseTest): processor.do_engine_steps(save=True) process_instance_metadata = ProcessInstanceMetadataModel.query.filter_by( - process_instance_id=process_instance.id).all() + process_instance_id=process_instance.id + ).all() assert len(process_instance_metadata) == 3 diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_instance_processor.py b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_instance_processor.py index f0de77aa7..3e0107957 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_instance_processor.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_instance_processor.py @@ -161,6 +161,7 @@ class TestProcessInstanceProcessor(BaseTest): ) processor = ProcessInstanceProcessor(process_instance) processor.do_engine_steps(save=True) + processor.save() assert len(process_instance.active_tasks) == 1 active_task = process_instance.active_tasks[0] @@ -241,3 +242,42 @@ class TestProcessInstanceProcessor(BaseTest): ) assert process_instance.status == ProcessInstanceStatus.complete.value + + def test_does_not_recreate_active_tasks_on_multiple_saves( + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, + ) -> None: + """Test_sets_permission_correctly_on_active_task_when_using_dict.""" + self.create_process_group( + client, with_super_admin_user, "test_group", "test_group" + ) + initiator_user = self.find_or_create_user("initiator_user") + finance_user_three = self.find_or_create_user("testuser3") + assert initiator_user.principal is not None + assert finance_user_three.principal is not None + AuthorizationService.import_permissions_from_yaml_file() + + finance_group = GroupModel.query.filter_by(identifier="Finance Team").first() + assert finance_group is not None + + process_model = load_test_spec( + process_model_id="test_group/model_with_lanes", + bpmn_file_name="lanes_with_owner_dict.bpmn", + process_model_source_directory="model_with_lanes", + ) + process_instance = self.create_process_instance_from_process_model( + process_model=process_model, user=initiator_user + ) + processor = ProcessInstanceProcessor(process_instance) + processor.do_engine_steps(save=True) + assert len(process_instance.active_tasks) == 1 + initial_active_task_id = process_instance.active_tasks[0].id + + # save again to ensure we go attempt to process the active tasks again + processor.save() + + assert len(process_instance.active_tasks) == 1 + assert initial_active_task_id == process_instance.active_tasks[0].id