diff --git a/spiffworkflow-backend/migrations/versions/b91143f4e414_.py b/spiffworkflow-backend/migrations/versions/b91143f4e414_.py new file mode 100644 index 00000000..041359dc --- /dev/null +++ b/spiffworkflow-backend/migrations/versions/b91143f4e414_.py @@ -0,0 +1,28 @@ +"""empty message + +Revision ID: b91143f4e414 +Revises: 63fc8d693b9f +Create Date: 2023-02-25 22:46:03.533624 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'b91143f4e414' +down_revision = '63fc8d693b9f' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('human_task', sa.Column('process_model_identifier', sa.String(length=255), nullable=True)) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('human_task', 'process_model_identifier') + # ### end Alembic commands ### diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/models/human_task.py b/spiffworkflow-backend/src/spiffworkflow_backend/models/human_task.py index 3317f773..e27b4cc1 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/models/human_task.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/models/human_task.py @@ -49,6 +49,7 @@ class HumanTaskModel(SpiffworkflowBaseDBModel): task_type: str = db.Column(db.String(50)) task_status: str = db.Column(db.String(50)) process_model_display_name: str = db.Column(db.String(255)) + process_model_identifier: str = db.Column(db.String(255)) completed: bool = db.Column(db.Boolean, default=False, nullable=False, index=True) human_task_users = relationship("HumanTaskUserModel", cascade="delete") diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/scripts/get_last_user_completing_task.py b/spiffworkflow-backend/src/spiffworkflow_backend/scripts/get_last_user_completing_task.py new file mode 100644 index 00000000..905e1298 --- /dev/null +++ b/spiffworkflow-backend/src/spiffworkflow_backend/scripts/get_last_user_completing_task.py @@ -0,0 +1,46 @@ +"""Get current user.""" +from typing import Any +from spiffworkflow_backend.models.user import UserModel +from spiffworkflow_backend.models.human_task import HumanTaskModel + +from flask import current_app +from flask import g + +from spiffworkflow_backend.models.script_attributes_context import ( + ScriptAttributesContext, +) +from spiffworkflow_backend.scripts.script import Script + + +class GetLastUserCompletingTask(Script): + @staticmethod + def requires_privileged_permissions() -> bool: + """We have deemed this function safe to run without elevated permissions.""" + return False + + def get_description(self) -> str: + return """Return the last user who completed the given task.""" + + def run( + self, + script_attributes_context: ScriptAttributesContext, + *_args: Any, + **kwargs: Any + ) -> Any: + """Run.""" + # dump the user using our json encoder and then load it back up as a dict + # to remove unwanted field types + if len(_args) == 2: + process_model_identifier = _args[0] + task_bpmn_identifier = _args[1] + else: + process_model_identifier = kwargs["process_model_identifier"] + task_bpmn_identifier = kwargs["task_bpmn_identifier"] + process_model_identifier = _args[0] or kwargs["process_model_identifier"] + print(f"process_model_identifier: {process_model_identifier}") + import pdb; pdb.set_trace() + # human_task = HumanTaskModel.query.filter_by(process_model_identifier=process_model_identifier).order_by(HumanTaskModel.id.desc()).first() + human_task = HumanTaskModel.query.filter_by(process_model_identifier=process_model_identifier).order_by(HumanTaskModel.id.desc()).join(UserModel, UserModel.id == HumanTaskModel.completed_by_user_id).first() + return human_task.completed_by_user + # user_as_json_string = current_app.json.dumps(g.user) + # return current_app.json.loads(user_as_json_string) 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 19b6f4da..b3aa7000 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py @@ -875,11 +875,13 @@ class ProcessInstanceProcessor: ).all() ready_or_waiting_tasks = self.get_all_ready_or_waiting_tasks() process_model_display_name = "" + process_model_identifier = "" process_model_info = self.process_model_service.get_process_model( self.process_instance_model.process_model_identifier ) if process_model_info is not None: process_model_display_name = process_model_info.display_name + process_model_identifier = process_model_info.id self.extract_metadata(process_model_info) @@ -911,6 +913,7 @@ class ProcessInstanceProcessor: human_task = HumanTaskModel( process_instance_id=self.process_instance_model.id, process_model_display_name=process_model_display_name, + process_model_identifier=process_model_identifier, form_file_name=form_file_name, ui_form_file_name=ui_form_file_name, task_id=str(ready_or_waiting_task.id), diff --git a/spiffworkflow-backend/tests/data/model_with_lanes/lanes.bpmn b/spiffworkflow-backend/tests/data/model_with_lanes/lanes.bpmn index 3ee43501..99c9c63c 100644 --- a/spiffworkflow-backend/tests/data/model_with_lanes/lanes.bpmn +++ b/spiffworkflow-backend/tests/data/model_with_lanes/lanes.bpmn @@ -7,7 +7,7 @@ StartEvent_1 - initator_one + initiator_one Event_06f4e68 initiator_two @@ -18,18 +18,19 @@ Flow_1tbyols - - - + + + - This is initiator user? + This is for the initiator user + user_completing_task = get_last_user_completing_task("misc/category_number_one/lanes", "initiator_one") Flow_1tbyols Flow_16ppta1 - This is finance user? + This is for a Finance Team user Flow_16ppta1 Flow_1cfcauf @@ -41,7 +42,8 @@ - This is initiator again? + This is initiator again + Flow_1cfcauf Flow_0x92f7d @@ -63,7 +65,7 @@ - + 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 b46ba1e1..ea6e08cf 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 @@ -65,6 +65,54 @@ class TestProcessInstanceProcessor(BaseTest): app.config["THREAD_LOCAL_DATA"].process_model_identifier = None app.config["THREAD_LOCAL_DATA"].process_instance_id = None + def test_get_last_user_completing_task_script_works( + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, + ) -> None: + """Test_sets_permission_correctly_on_human_task.""" + self.create_process_group( + client, with_super_admin_user, "test_group", "test_group" + ) + initiator_user = self.find_or_create_user("initiator_user") + finance_user = self.find_or_create_user("testuser2") + assert initiator_user.principal is not None + assert finance_user.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="misc/category_number_one/lanes", + bpmn_file_name="lanes.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_human_tasks) == 1 + human_task = process_instance.active_human_tasks[0] + assert human_task.lane_assignment_id is None + assert len(human_task.potential_owners) == 1 + assert human_task.potential_owners[0] == initiator_user + + spiff_task = processor.__class__.get_task_by_bpmn_identifier( + human_task.task_name, processor.bpmn_process_instance + ) + ProcessInstanceService.complete_form_task( + processor, spiff_task, {}, initiator_user, human_task + ) + print(f"initiator_user.username: {initiator_user.username}") + print(f"data: {processor.get_data()}") + print(f"task_data: {spiff_task.data}") + assert initiator_user.username == spiff_task.get_data("user_completing_task")["username"] + def test_sets_permission_correctly_on_human_task( self, app: Flask,