From 3782864dc9b6a2f520962d8512abbdfbec0b262d Mon Sep 17 00:00:00 2001 From: jasquat Date: Fri, 17 Mar 2023 13:51:56 -0400 Subject: [PATCH] added event logs for skipping and manually editing a task --- spiffworkflow-backend/migrations/env.py | 2 - .../models/process_instance_event.py | 7 +- .../routes/process_api_blueprint.py | 43 +++++------ .../routes/process_instances_controller.py | 5 +- .../services/process_instance_processor.py | 35 +++++---- .../services/task_service.py | 10 +-- .../services/workflow_execution_service.py | 6 +- .../src/routes/ProcessInstanceShow.tsx | 71 ------------------- 8 files changed, 63 insertions(+), 116 deletions(-) diff --git a/spiffworkflow-backend/migrations/env.py b/spiffworkflow-backend/migrations/env.py index 68feded2a..630e381ad 100644 --- a/spiffworkflow-backend/migrations/env.py +++ b/spiffworkflow-backend/migrations/env.py @@ -1,5 +1,3 @@ -from __future__ import with_statement - import logging from logging.config import fileConfig diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/models/process_instance_event.py b/spiffworkflow-backend/src/spiffworkflow_backend/models/process_instance_event.py index de965e9a1..560abffd2 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/models/process_instance_event.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/models/process_instance_event.py @@ -1,13 +1,14 @@ from __future__ import annotations -from spiffworkflow_backend.models.user import UserModel -from spiffworkflow_backend.helpers.spiff_enum import SpiffEnum + from typing import Any -from sqlalchemy.orm import validates from sqlalchemy import ForeignKey +from sqlalchemy.orm import validates +from spiffworkflow_backend.helpers.spiff_enum import SpiffEnum from spiffworkflow_backend.models.db import db from spiffworkflow_backend.models.db import SpiffworkflowBaseDBModel +from spiffworkflow_backend.models.user import UserModel # event types take the form [SUBJECT]_[PAST_TENSE_VERB] since subject is not always the same. diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py index 32becbc63..a07f5f494 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py @@ -17,24 +17,28 @@ from spiffworkflow_backend.exceptions.process_entity_not_found_error import ( ProcessEntityNotFoundError, ) from spiffworkflow_backend.models.db import db +from spiffworkflow_backend.models.json_data import JsonDataModel from spiffworkflow_backend.models.principal import PrincipalModel from spiffworkflow_backend.models.process_instance import ProcessInstanceModel from spiffworkflow_backend.models.process_instance import ProcessInstanceModelSchema from spiffworkflow_backend.models.process_instance import ( ProcessInstanceTaskDataCannotBeUpdatedError, ) +from spiffworkflow_backend.models.process_instance_event import ProcessInstanceEventType from spiffworkflow_backend.models.process_instance_file_data import ( ProcessInstanceFileDataModel, ) from spiffworkflow_backend.models.process_model import ProcessModelInfo from spiffworkflow_backend.models.spec_reference import SpecReferenceCache from spiffworkflow_backend.models.spec_reference import SpecReferenceSchema +from spiffworkflow_backend.models.task import TaskModel # noqa: F401 from spiffworkflow_backend.services.authorization_service import AuthorizationService from spiffworkflow_backend.services.git_service import GitService from spiffworkflow_backend.services.process_instance_processor import ( ProcessInstanceProcessor, ) from spiffworkflow_backend.services.process_model_service import ProcessModelService +from spiffworkflow_backend.services.task_service import TaskService process_api_blueprint = Blueprint("process_api", __name__) @@ -180,34 +184,33 @@ def task_data_update( f" It is currently: {process_instance.status}" ) - process_instance_data = process_instance.process_instance_data - if process_instance_data is None: + task_model = TaskModel.query.filter_by(guid=task_id).first() + if task_model is None: raise ApiError( - error_code="process_instance_data_not_found", - message=f"Could not find task data related to process instance: {process_instance.id}", + error_code="update_task_data_error", + message=f"Could not find Task: {task_id} in Instance: {process_instance_id}.", ) - process_instance_data_dict = json.loads(process_instance_data.runtime_json) if "new_task_data" in body: new_task_data_str: str = body["new_task_data"] new_task_data_dict = json.loads(new_task_data_str) - if task_id in process_instance_data_dict["tasks"]: - process_instance_data_dict["tasks"][task_id]["data"] = new_task_data_dict - process_instance_data.runtime_json = json.dumps(process_instance_data_dict) - db.session.add(process_instance_data) - try: - db.session.commit() - except Exception as e: - db.session.rollback() - raise ApiError( - error_code="update_task_data_error", - message=f"Could not update the Instance. Original error is {e}", - ) from e - else: + json_data_dict = TaskService.update_task_data_on_task_model( + task_model, new_task_data_dict, "json_data_hash" + ) + if json_data_dict is not None: + json_data = JsonDataModel(**json_data_dict) + db.session.add(json_data) + ProcessInstanceProcessor.add_event_to_process_instance( + process_instance, ProcessInstanceEventType.task_data_edited.value, task_guid=task_id + ) + try: + db.session.commit() + except Exception as e: + db.session.rollback() raise ApiError( error_code="update_task_data_error", - message=f"Could not find Task: {task_id} in Instance: {process_instance_id}.", - ) + message=f"Could not update the Instance. Original error is {e}", + ) from e else: raise ApiError( error_code="update_task_data_error", 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 e82a1aa9f..23d71442b 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_instances_controller.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_instances_controller.py @@ -266,8 +266,9 @@ def process_instance_log_list( ) logs = ( - log_query.order_by(ProcessInstanceEventModel.timestamp.desc(), - ProcessInstanceEventModel.id.desc()) # type: ignore + log_query.order_by( + ProcessInstanceEventModel.timestamp.desc(), ProcessInstanceEventModel.id.desc() # type: ignore + ) .outerjoin(UserModel, UserModel.id == ProcessInstanceEventModel.user_id) .add_columns( TaskModel.guid.label("spiff_task_guid"), # type: ignore 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 0338aee31..ecc3c9f88 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py @@ -1,7 +1,5 @@ """Process_instance_processor.""" import _strptime # type: ignore -from flask import g -from spiffworkflow_backend.models.process_instance_event import ProcessInstanceEventModel, ProcessInstanceEventType import decimal import json import logging @@ -25,6 +23,7 @@ from uuid import UUID import dateparser import pytz from flask import current_app +from flask import g from lxml import etree # type: ignore from lxml.etree import XMLSyntaxError # type: ignore from RestrictedPython import safe_globals # type: ignore @@ -75,6 +74,8 @@ from spiffworkflow_backend.models.message_instance_correlation import ( ) from spiffworkflow_backend.models.process_instance import ProcessInstanceModel from spiffworkflow_backend.models.process_instance import ProcessInstanceStatus +from spiffworkflow_backend.models.process_instance_event import ProcessInstanceEventModel +from spiffworkflow_backend.models.process_instance_event import ProcessInstanceEventType from spiffworkflow_backend.models.process_instance_metadata import ( ProcessInstanceMetadataModel, ) @@ -1240,6 +1241,7 @@ class ProcessInstanceProcessor: def manual_complete_task(self, task_id: str, execute: bool) -> None: """Mark the task complete optionally executing it.""" spiff_task = self.bpmn_process_instance.get_task(UUID(task_id)) + event_type = ProcessInstanceEventType.task_skipped.value if execute: current_app.logger.info( f"Manually executing Task {spiff_task.task_spec.name} of process" @@ -1255,6 +1257,7 @@ class ProcessInstanceProcessor: break else: spiff_task.complete() + event_type = ProcessInstanceEventType.task_executed_manually.value else: spiff_logger = logging.getLogger("spiff") spiff_logger.info(f"Skipped task {spiff_task.task_spec.name}", extra=spiff_task.log_info()) @@ -1275,6 +1278,7 @@ class ProcessInstanceProcessor: self.increment_spiff_step() self.add_step() + self.add_event_to_process_instance(self.process_instance_model, event_type, task_guid=task_id) self.save() # Saving the workflow seems to reset the status self.suspend() @@ -1813,8 +1817,9 @@ class ProcessInstanceProcessor: json_data = JsonDataModel(**json_data_dict) db.session.add(json_data) - self.add_event_to_process_instance(self.process_instance_model, - ProcessInstanceEventType.task_completed.value, task_guid=task_model.guid) + self.add_event_to_process_instance( + self.process_instance_model, ProcessInstanceEventType.task_completed.value, task_guid=task_model.guid + ) # this is the thing that actually commits the db transaction (on behalf of the other updates above as well) self.save() @@ -1940,33 +1945,39 @@ class ProcessInstanceProcessor: self.save() self.process_instance_model.status = "terminated" db.session.add(self.process_instance_model) - self.add_event_to_process_instance(self.process_instance_model, - ProcessInstanceEventType.process_instance_terminated.value) + self.add_event_to_process_instance( + self.process_instance_model, ProcessInstanceEventType.process_instance_terminated.value + ) db.session.commit() def suspend(self) -> None: """Suspend.""" self.process_instance_model.status = ProcessInstanceStatus.suspended.value db.session.add(self.process_instance_model) - self.add_event_to_process_instance(self.process_instance_model, - ProcessInstanceEventType.process_instance_suspended.value) + self.add_event_to_process_instance( + self.process_instance_model, ProcessInstanceEventType.process_instance_suspended.value + ) db.session.commit() def resume(self) -> None: """Resume.""" self.process_instance_model.status = ProcessInstanceStatus.waiting.value db.session.add(self.process_instance_model) - self.add_event_to_process_instance(self.process_instance_model, - ProcessInstanceEventType.process_instance_resumed.value) + self.add_event_to_process_instance( + self.process_instance_model, ProcessInstanceEventType.process_instance_resumed.value + ) db.session.commit() @classmethod - def add_event_to_process_instance(cls, process_instance: ProcessInstanceModel, event_type: str, task_guid: Optional[str] = None) -> None: + def add_event_to_process_instance( + cls, process_instance: ProcessInstanceModel, event_type: str, task_guid: Optional[str] = None + ) -> None: user_id = None if g.user: user_id = g.user.id process_instance_event = ProcessInstanceEventModel( - process_instance_id=process_instance.id, event_type=event_type, timestamp=time.time(), user_id=user_id) + process_instance_id=process_instance.id, event_type=event_type, timestamp=time.time(), user_id=user_id + ) if task_guid: process_instance_event.task_guid = task_guid db.session.add(process_instance_event) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py index 5e7bf88cd..5a03f3877 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py @@ -60,8 +60,8 @@ class TaskService: python_env_data_dict = cls._get_python_env_data_dict_from_spiff_task(spiff_task, serializer) task_model.properties_json = new_properties_json task_model.state = TaskStateNames[new_properties_json["state"]] - json_data_dict = cls._update_task_data_on_task_model(task_model, spiff_task_data, "json_data_hash") - python_env_dict = cls._update_task_data_on_task_model(task_model, python_env_data_dict, "python_env_data_hash") + json_data_dict = cls.update_task_data_on_task_model(task_model, spiff_task_data, "json_data_hash") + python_env_dict = cls.update_task_data_on_task_model(task_model, python_env_data_dict, "python_env_data_hash") return [json_data_dict, python_env_dict] @classmethod @@ -246,14 +246,14 @@ class TaskService: task_model.properties_json = task_properties new_task_models[task_model.guid] = task_model - json_data_dict = TaskService._update_task_data_on_task_model( + json_data_dict = TaskService.update_task_data_on_task_model( task_model, task_data_dict, "json_data_hash" ) if json_data_dict is not None: new_json_data_dicts[json_data_dict["hash"]] = json_data_dict python_env_data_dict = cls._get_python_env_data_dict_from_spiff_task(spiff_task, serializer) - python_env_dict = TaskService._update_task_data_on_task_model( + python_env_dict = TaskService.update_task_data_on_task_model( task_model, python_env_data_dict, "python_env_data_hash" ) if python_env_dict is not None: @@ -274,7 +274,7 @@ class TaskService: return json_data_dict @classmethod - def _update_task_data_on_task_model( + def update_task_data_on_task_model( cls, task_model: TaskModel, task_data_dict: dict, task_model_data_column: str ) -> Optional[JsonDataDict]: task_data_json = json.dumps(task_data_dict, sort_keys=True) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py index f7320e341..0f2a6d0d0 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py @@ -154,7 +154,11 @@ class TaskModelSavingDelegate(EngineStepDelegate): # which script tasks execute when READY. timestamp = task_model.end_in_seconds or task_model.start_in_seconds or time.time() process_instance_event = ProcessInstanceEventModel( - task_guid=task_model.guid, process_instance_id=self.process_instance.id, event_type=event_type, timestamp=timestamp) + task_guid=task_model.guid, + process_instance_id=self.process_instance.id, + event_type=event_type, + timestamp=timestamp, + ) self.process_instance_events[task_model.guid] = process_instance_event return task_model diff --git a/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx b/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx index 652e07b03..36c06d23b 100644 --- a/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx @@ -381,40 +381,6 @@ export default function ProcessInstanceShow({ variant }: OwnProps) { {lastUpdatedTimeTag} - {/* - - - Suspended at:{' '} - - - 2023-03-17 10:12:05 (by jason) - - - - - Resumed at:{' '} - - - 2023-03-17 10:13:05 (by jason) - - - - - Suspended at:{' '} - - - 2023-03-17 10:14:05 (by jason) - - - - - Terminated at:{' '} - - - 2023-03-17 10:15:05 (by jason) - - - */} Process model revision:{' '} @@ -434,43 +400,6 @@ export default function ProcessInstanceShow({ variant }: OwnProps) { - - {/* -
- - - Suspended at:{' '} - - - 2023-03-17 10:12:05 (by jason) - - - - - Resumed at:{' '} - - - 2023-03-17 10:13:05 (by jason) - - - - - Suspended at:{' '} - - - 2023-03-17 10:14:05 (by jason) - - - - - Terminated at:{' '} - - - 2023-03-17 10:15:05 (by jason) - - - */} -