diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/models/task.py b/spiffworkflow-backend/src/spiffworkflow_backend/models/task.py index 413be5e5a..4012b0779 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/models/task.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/models/task.py @@ -22,78 +22,7 @@ class MultiInstanceType(enum.Enum): class Task: """Task.""" - ########################################################################## - # Custom properties and validations defined in Camunda form fields # - ########################################################################## - - # Custom task title - PROP_EXTENSIONS_TITLE = "display_name" - PROP_EXTENSIONS_CLEAR_DATA = "clear_data" - - # Field Types - FIELD_TYPE_STRING = "string" - FIELD_TYPE_LONG = "long" - FIELD_TYPE_BOOLEAN = "boolean" - FIELD_TYPE_DATE = "date" - FIELD_TYPE_ENUM = "enum" - FIELD_TYPE_TEXTAREA = "textarea" # textarea: Multiple lines of text - FIELD_TYPE_AUTO_COMPLETE = "autocomplete" - FIELD_TYPE_FILE = "file" - FIELD_TYPE_FILES = "files" # files: Multiple files - FIELD_TYPE_TEL = "tel" # tel: Phone number - FIELD_TYPE_EMAIL = "email" # email: Email address - FIELD_TYPE_URL = "url" # url: Website address - - FIELD_PROP_AUTO_COMPLETE_MAX = ( # Not used directly, passed in from the front end. - "autocomplete_num" - ) - - # Required field - FIELD_CONSTRAINT_REQUIRED = "required" - - # Field properties and expressions Expressions - FIELD_PROP_REPEAT = "repeat" - FIELD_PROP_READ_ONLY = "read_only" - FIELD_PROP_LDAP_LOOKUP = "ldap.lookup" - FIELD_PROP_READ_ONLY_EXPRESSION = "read_only_expression" - FIELD_PROP_HIDE_EXPRESSION = "hide_expression" - FIELD_PROP_REQUIRED_EXPRESSION = "required_expression" - FIELD_PROP_LABEL_EXPRESSION = "label_expression" - FIELD_PROP_REPEAT_HIDE_EXPRESSION = "repeat_hide_expression" - FIELD_PROP_VALUE_EXPRESSION = "value_expression" - - # Enum field options - FIELD_PROP_SPREADSHEET_NAME = "spreadsheet.name" - FIELD_PROP_DATA_NAME = "data.name" - FIELD_PROP_VALUE_COLUMN = "value.column" - FIELD_PROP_LABEL_COLUMN = "label.column" - - # Enum field options values pulled from task data - - # Group and Repeat functions - FIELD_PROP_GROUP = "group" - FIELD_PROP_REPLEAT = "repeat" - FIELD_PROP_REPLEAT_TITLE = "repeat_title" - FIELD_PROP_REPLEAT_BUTTON = "repeat_button_label" - - # File specific field properties - FIELD_PROP_DOC_CODE = "doc_code" # to associate a file upload field with a doc code - FIELD_PROP_FILE_DATA = ( # to associate a bit of data with a specific file upload file. - "file_data" - ) - - # Additional properties - FIELD_PROP_ENUM_TYPE = "enum_type" - FIELD_PROP_BOOLEAN_TYPE = "boolean_type" - FIELD_PROP_TEXT_AREA_ROWS = "rows" - FIELD_PROP_TEXT_AREA_COLS = "cols" - FIELD_PROP_TEXT_AREA_AUTO = "autosize" - FIELD_PROP_PLACEHOLDER = "placeholder" - FIELD_PROP_DESCRIPTION = "description" - FIELD_PROP_MARKDOWN_DESCRIPTION = "markdown_description" - FIELD_PROP_HELP = "help" - - ########################################################################## + HUMAN_TASK_TYPES = ["User Task", "Manual Task"] def __init__( self, @@ -202,20 +131,6 @@ class Task: "task_spiff_step": self.task_spiff_step, } - @classmethod - def valid_property_names(cls) -> list[str]: - """Valid_property_names.""" - return [ - value for name, value in vars(cls).items() if name.startswith("FIELD_PROP") - ] - - @classmethod - def valid_field_types(cls) -> list[str]: - """Valid_field_types.""" - return [ - value for name, value in vars(cls).items() if name.startswith("FIELD_TYPE") - ] - @classmethod def task_state_name_to_int(cls, task_state_name: str) -> int: task_state_integers = {v: k for k, v in TaskStateNames.items()} diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/logging_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/logging_service.py index 8d7fa9d02..36d1ea77c 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/logging_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/logging_service.py @@ -10,6 +10,7 @@ from flask.app import Flask from spiffworkflow_backend.models.db import db from spiffworkflow_backend.models.spiff_logging import SpiffLoggingModel +from spiffworkflow_backend.models.task import Task # flask logging formats: @@ -218,9 +219,13 @@ class DBHandler(logging.Handler): bpmn_task_type = record.task_type if hasattr(record, "task_type") else None # type: ignore timestamp = record.created message = record.msg if hasattr(record, "msg") else None - current_user_id = ( - record.current_user_id if hasattr(record, "current_user_id") else None # type: ignore - ) + + current_user_id = None + if bpmn_task_type in Task.HUMAN_TASK_TYPES and hasattr( + record, "current_user_id" + ): + current_user_id = record.current_user_id # type: ignore + spiff_step = ( record.spiff_step # type: ignore if hasattr(record, "spiff_step") and record.spiff_step is not None # type: ignore diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_service.py index 7b74ef502..be2394dd4 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_service.py @@ -234,23 +234,6 @@ class ProcessInstanceService: # maybe move this out once we have the interstitial page since this is here just so we can get the next human task processor.do_engine_steps(save=True) - @staticmethod - def extract_form_data(latest_data: dict, task: SpiffTask) -> dict: - """Extracts data from the latest_data that is directly related to the form that is being submitted.""" - data = {} - - if hasattr(task.task_spec, "form"): - for field in task.task_spec.form.fields: - if field.has_property(Task.FIELD_PROP_REPEAT): - group = field.get_property(Task.FIELD_PROP_REPEAT) - if group in latest_data: - data[group] = latest_data[group] - else: - value = ProcessInstanceService.get_dot_value(field.id, latest_data) - if value is not None: - ProcessInstanceService.set_dot_value(field.id, value, data) - return data - @staticmethod def create_dot_dict(data: dict) -> dict[str, Any]: """Create_dot_dict.""" diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_api.py b/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_api.py index b3bb90df2..29d1d33f4 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_api.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_api.py @@ -2352,7 +2352,7 @@ class TestProcessApi(BaseTest): assert len(response.json["results"]) == 2 @pytest.mark.skipif( - os.environ.get("SPIFFWORKFLOW_BACKEND_DATABASE_TYPE") == 'postgres', + os.environ.get("SPIFFWORKFLOW_BACKEND_DATABASE_TYPE") == "postgres", reason="look at comment in tasks_controller method task_list_my_tasks", ) def test_correct_user_can_get_and_update_a_task( diff --git a/spiffworkflow-frontend/src/routes/ProcessInstanceLogList.tsx b/spiffworkflow-frontend/src/routes/ProcessInstanceLogList.tsx index b2f58adc1..d68deba7b 100644 --- a/spiffworkflow-frontend/src/routes/ProcessInstanceLogList.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessInstanceLogList.tsx @@ -10,6 +10,7 @@ import { } from '../helpers'; import HttpService from '../services/HttpService'; import { useUriListForPermissions } from '../hooks/UriListForPermissions'; +import UserService from '../services/UserService'; type OwnProps = { variant: string; @@ -28,6 +29,8 @@ export default function ProcessInstanceLogList({ variant }: OwnProps) { processInstanceShowPageBaseUrl = `/admin/process-instances/${params.process_model_id}`; } + const userEmail = UserService.getUserEmail(); + useEffect(() => { const setProcessInstanceLogListFromResult = (result: any) => { setProcessInstanceLogs(result.results); @@ -48,53 +51,87 @@ export default function ProcessInstanceLogList({ variant }: OwnProps) { const buildTable = () => { const rows = processInstanceLogs.map((row) => { const rowToUse = row as any; - return ( - - {rowToUse.id} - - {rowToUse.bpmn_task_name || - (rowToUse.bpmn_task_type === 'Default Start Event' - ? 'Process Started' - : '') || - (rowToUse.bpmn_task_type === 'End Event' ? 'Process Ended' : '')} - - {isDetailedView && ( - <> - {rowToUse.message} - {rowToUse.bpmn_task_identifier} - {rowToUse.bpmn_task_type} - - )} - {rowToUse.bpmn_process_identifier} - {rowToUse.username} - - - {convertSecondsToFormattedDateTime(rowToUse.timestamp)} - - - + const tableRow = []; + const taskNameCell = ( + + {rowToUse.bpmn_task_name || + (rowToUse.bpmn_task_type === 'Default Start Event' + ? 'Process Started' + : '') || + (rowToUse.bpmn_task_type === 'End Event' ? 'Process Ended' : '')} + ); + if (isDetailedView) { + tableRow.push( + <> + {rowToUse.id} + {rowToUse.bpmn_process_identifier} + {taskNameCell} + + ); + } else { + tableRow.push( + <> + {taskNameCell} + {rowToUse.bpmn_process_identifier} + + ); + } + if (isDetailedView) { + tableRow.push( + <> + {rowToUse.bpmn_task_type} + {rowToUse.message} + + {rowToUse.username === userEmail ? 'me 🔥' : rowToUse.username} + + + ); + } + tableRow.push( + + + {convertSecondsToFormattedDateTime(rowToUse.timestamp)} + + + ); + return {tableRow}; }); + + const tableHeaders = []; + if (isDetailedView) { + tableHeaders.push( + <> + Id + Bpmn Process + Task Name + + ); + } else { + tableHeaders.push( + <> + Event + Bpmn Process + + ); + } + if (isDetailedView) { + tableHeaders.push( + <> + Task Type + Message + User + + ); + } + tableHeaders.push(Timestamp); return ( - - - - {isDetailedView && ( - <> - - - - - )} - - - - + {tableHeaders}{rows}
IdTask NameMessageTask IdentifierTask TypeBpmn Process IdentifierUserTimestamp