From a1a9decdb0bad126eb00d45f6a0b98a4b0f442f9 Mon Sep 17 00:00:00 2001 From: jasquat Date: Fri, 9 Dec 2022 11:23:18 -0500 Subject: [PATCH 1/3] some base work to try to get display names searchable for process models w/ burnettk --- .../src/spiffworkflow_backend/api.yml | 6 ++++++ .../routes/process_api_blueprint.py | 16 ++++++++++++---- .../src/components/ProcessModelSearch.tsx | 19 +++++++++++++++---- .../src/routes/ProcessGroupList.tsx | 2 +- 4 files changed, 34 insertions(+), 9 deletions(-) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/api.yml b/spiffworkflow-backend/src/spiffworkflow_backend/api.yml index f5836d63..52890114 100755 --- a/spiffworkflow-backend/src/spiffworkflow_backend/api.yml +++ b/spiffworkflow-backend/src/spiffworkflow_backend/api.yml @@ -285,6 +285,12 @@ paths: description: Get only the process models that the user can run schema: type: boolean + - name: include_parent_groups + in: query + required: false + description: Get the display names for the parent groups as well + schema: + type: boolean - name: page in: query required: false 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 d8effe0d..7e583de9 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py @@ -388,6 +388,7 @@ def process_model_list( process_group_identifier: Optional[str] = None, recursive: Optional[bool] = False, filter_runnable_by_user: Optional[bool] = False, + include_parent_groups: Optional[bool] = False, page: int = 1, per_page: int = 100, ) -> flask.wrappers.Response: @@ -397,22 +398,29 @@ def process_model_list( recursive=recursive, filter_runnable_by_user=filter_runnable_by_user, ) - batch = ProcessModelService().get_batch( + process_models_to_return = ProcessModelService().get_batch( process_models, page=page, per_page=per_page ) + + if include_parent_groups: + for process_model in process_models_to_return: + process_model.parent_groups = ProcessModelService.get_parent_group_array( + process_model.id + ) + pages = len(process_models) // per_page remainder = len(process_models) % per_page if remainder > 0: pages += 1 response_json = { - "results": ProcessModelInfoSchema(many=True).dump(batch), + "results": process_models_to_return, "pagination": { - "count": len(batch), + "count": len(process_models_to_return), "total": len(process_models), "pages": pages, }, } - return Response(json.dumps(response_json), status=200, mimetype="application/json") + return make_response(jsonify(response_json), 200) def process_list() -> Any: diff --git a/spiffworkflow-frontend/src/components/ProcessModelSearch.tsx b/spiffworkflow-frontend/src/components/ProcessModelSearch.tsx index 8a3c0b9f..06fa4d84 100644 --- a/spiffworkflow-frontend/src/components/ProcessModelSearch.tsx +++ b/spiffworkflow-frontend/src/components/ProcessModelSearch.tsx @@ -3,7 +3,7 @@ import { // @ts-ignore } from '@carbon/react'; import { truncateString } from '../helpers'; -import { ProcessModel } from '../interfaces'; +import { ProcessGroupLite, ProcessModel } from '../interfaces'; type OwnProps = { onChange: (..._args: any[]) => any; @@ -18,12 +18,23 @@ export default function ProcessModelSearch({ onChange, titleText = 'Process model', }: OwnProps) { + const getParentGroupsDisplayName = (processModel: ProcessModel) => { + if (processModel.parent_groups) { + return processModel.parent_groups + .map((parentGroup: ProcessGroupLite) => { + return parentGroup.display_name; + }) + .join(' '); + } + return ''; + }; + const shouldFilterProcessModel = (options: any) => { const processModel: ProcessModel = options.item; const { inputValue } = options; - return `${processModel.id} (${processModel.display_name})`.includes( - inputValue - ); + return `${processModel.id} (${getParentGroupsDisplayName(processModel)} ${ + processModel.display_name + })`.includes(inputValue); }; return ( Date: Wed, 4 Jan 2023 13:12:36 -0500 Subject: [PATCH 2/3] added process group display name to model search and cache the groups to avoid extra lookups w/ burnettk --- .../src/spiffworkflow_backend/interfaces.py | 20 ++++++++++++++++++ .../models/process_model.py | 3 ++- .../routes/process_models_controller.py | 17 +++++++++++---- .../services/process_model_service.py | 21 +++++++++++++++---- .../components/ProcessInstanceListTable.tsx | 2 +- .../src/components/ProcessModelSearch.tsx | 17 ++++++++------- 6 files changed, 62 insertions(+), 18 deletions(-) create mode 100644 spiffworkflow-backend/src/spiffworkflow_backend/interfaces.py diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/interfaces.py b/spiffworkflow-backend/src/spiffworkflow_backend/interfaces.py new file mode 100644 index 00000000..a707c22a --- /dev/null +++ b/spiffworkflow-backend/src/spiffworkflow_backend/interfaces.py @@ -0,0 +1,20 @@ +from typing import NewType, TypedDict +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from spiffworkflow_backend.models.process_group import ProcessGroup + + +IdToProcessGroupMapping = NewType("IdToProcessGroupMapping", dict[str, "ProcessGroup"]) + + +class ProcessGroupLite(TypedDict): + + id: str + display_name: str + + +class ProcessGroupLitesWithCache(TypedDict): + + cache: dict[str, "ProcessGroup"] + process_groups: list[ProcessGroupLite] diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/models/process_model.py b/spiffworkflow-backend/src/spiffworkflow_backend/models/process_model.py index e8d5eed1..0b3ffa23 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/models/process_model.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/models/process_model.py @@ -10,6 +10,7 @@ from typing import Any import marshmallow from marshmallow import Schema from marshmallow.decorators import post_load +from spiffworkflow_backend.interfaces import ProcessGroupLite from spiffworkflow_backend.models.file import File @@ -37,7 +38,7 @@ class ProcessModelInfo: files: list[File] | None = field(default_factory=list[File]) fault_or_suspend_on_exception: str = NotificationType.fault.value exception_notification_addresses: list[str] = field(default_factory=list) - parent_groups: list[dict] | None = None + parent_groups: list[ProcessGroupLite] | None = None metadata_extraction_paths: list[dict[str, str]] | None = None def __post_init__(self) -> None: diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_models_controller.py b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_models_controller.py index 0f877ce7..ac651415 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_models_controller.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_models_controller.py @@ -15,6 +15,7 @@ from flask import jsonify from flask import make_response from flask.wrappers import Response from flask_bpmn.api.api_error import ApiError +from spiffworkflow_backend.interfaces import IdToProcessGroupMapping from spiffworkflow_backend.models.file import FileSchema from spiffworkflow_backend.models.process_group import ProcessGroup @@ -172,6 +173,7 @@ def process_model_list( process_group_identifier: Optional[str] = None, recursive: Optional[bool] = False, filter_runnable_by_user: Optional[bool] = False, + include_parent_groups: Optional[bool] = False, page: int = 1, per_page: int = 100, ) -> flask.wrappers.Response: @@ -181,22 +183,29 @@ def process_model_list( recursive=recursive, filter_runnable_by_user=filter_runnable_by_user, ) - batch = ProcessModelService().get_batch( + process_models_to_return = ProcessModelService().get_batch( process_models, page=page, per_page=per_page ) + + if include_parent_groups: + process_group_cache = IdToProcessGroupMapping({}) + for process_model in process_models_to_return: + parent_group_lites_with_cache = ProcessModelService.get_parent_group_array_and_cache_it(process_model.id, process_group_cache) + process_model.parent_groups = parent_group_lites_with_cache['process_groups'] + pages = len(process_models) // per_page remainder = len(process_models) % per_page if remainder > 0: pages += 1 response_json = { - "results": ProcessModelInfoSchema(many=True).dump(batch), + "results": process_models_to_return, "pagination": { - "count": len(batch), + "count": len(process_models_to_return), "total": len(process_models), "pages": pages, }, } - return Response(json.dumps(response_json), status=200, mimetype="application/json") + return make_response(jsonify(response_json), 200) def process_model_file_update( diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_model_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_model_service.py index f9f34631..c81b0e81 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_model_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_model_service.py @@ -1,5 +1,6 @@ """Process_model_service.""" import json +from typing import TypedDict import os import shutil from glob import glob @@ -13,6 +14,7 @@ from flask_bpmn.api.api_error import ApiError from spiffworkflow_backend.exceptions.process_entity_not_found_error import ( ProcessEntityNotFoundError, ) +from spiffworkflow_backend.interfaces import ProcessGroupLite, ProcessGroupLitesWithCache from spiffworkflow_backend.models.process_group import ProcessGroup from spiffworkflow_backend.models.process_group import ProcessGroupSchema from spiffworkflow_backend.models.process_instance import ProcessInstanceModel @@ -237,21 +239,32 @@ class ProcessModelService(FileSystemService): return process_models @classmethod - def get_parent_group_array(cls, process_identifier: str) -> list[dict]: + def get_parent_group_array_and_cache_it(cls, process_identifier: str, process_group_cache: dict[str, ProcessGroup]) -> ProcessGroupLitesWithCache: """Get_parent_group_array.""" full_group_id_path = None - parent_group_array = [] + parent_group_array: list[ProcessGroupLite] = [] for process_group_id_segment in process_identifier.split("/")[0:-1]: if full_group_id_path is None: full_group_id_path = process_group_id_segment else: full_group_id_path = os.path.join(full_group_id_path, process_group_id_segment) # type: ignore - parent_group = ProcessModelService.get_process_group(full_group_id_path) + parent_group = process_group_cache.get(full_group_id_path, None) + if parent_group is None: + parent_group = ProcessModelService.get_process_group(full_group_id_path) + if parent_group: + if full_group_id_path not in process_group_cache: + process_group_cache[full_group_id_path] = parent_group parent_group_array.append( {"id": parent_group.id, "display_name": parent_group.display_name} ) - return parent_group_array + return {'cache': process_group_cache, 'process_groups': parent_group_array} + + @classmethod + def get_parent_group_array(cls, process_identifier: str) -> list[ProcessGroupLite]: + """Get_parent_group_array.""" + parent_group_lites_with_cache = cls.get_parent_group_array_and_cache_it(process_identifier, {}) + return parent_group_lites_with_cache['process_groups'] @classmethod def get_process_groups( diff --git a/spiffworkflow-frontend/src/components/ProcessInstanceListTable.tsx b/spiffworkflow-frontend/src/components/ProcessInstanceListTable.tsx index 2fc81265..0902aee9 100644 --- a/spiffworkflow-frontend/src/components/ProcessInstanceListTable.tsx +++ b/spiffworkflow-frontend/src/components/ProcessInstanceListTable.tsx @@ -309,7 +309,7 @@ export default function ProcessInstanceListTable({ if (filtersEnabled) { // populate process model selection HttpService.makeCallToBackend({ - path: `/process-models?per_page=1000&recursive=true`, + path: `/process-models?per_page=1000&recursive=true&include_parent_groups=true`, successCallback: processResultForProcessModels, }); } else { diff --git a/spiffworkflow-frontend/src/components/ProcessModelSearch.tsx b/spiffworkflow-frontend/src/components/ProcessModelSearch.tsx index 06fa4d84..790dff2e 100644 --- a/spiffworkflow-frontend/src/components/ProcessModelSearch.tsx +++ b/spiffworkflow-frontend/src/components/ProcessModelSearch.tsx @@ -24,17 +24,21 @@ export default function ProcessModelSearch({ .map((parentGroup: ProcessGroupLite) => { return parentGroup.display_name; }) - .join(' '); + .join(' / '); } return ''; }; + const getFullProcessModelLabel = (processModel: ProcessModel) => { + return `${processModel.id} (${getParentGroupsDisplayName(processModel)} ${ + processModel.display_name + })`; + }; + const shouldFilterProcessModel = (options: any) => { const processModel: ProcessModel = options.item; const { inputValue } = options; - return `${processModel.id} (${getParentGroupsDisplayName(processModel)} ${ - processModel.display_name - })`.includes(inputValue); + return getFullProcessModelLabel(processModel).includes(inputValue); }; return ( { if (processModel) { - return `${processModel.id} (${truncateString( - processModel.display_name, - 75 - )})`; + return getFullProcessModelLabel(processModel); } return null; }} From c6b5049d71f88e4bd9f91483173f2b40e43d5441 Mon Sep 17 00:00:00 2001 From: jasquat Date: Wed, 4 Jan 2023 13:18:05 -0500 Subject: [PATCH 3/3] pyl w/ burnettk --- .../src/spiffworkflow_backend/interfaces.py | 6 +++++- .../models/process_group.py | 3 ++- .../models/process_model.py | 2 +- .../routes/process_models_controller.py | 12 +++++++++--- .../services/process_model_service.py | 16 ++++++++++------ .../src/components/ProcessModelSearch.tsx | 1 - 6 files changed, 27 insertions(+), 13 deletions(-) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/interfaces.py b/spiffworkflow-backend/src/spiffworkflow_backend/interfaces.py index a707c22a..3d528042 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/interfaces.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/interfaces.py @@ -1,5 +1,7 @@ -from typing import NewType, TypedDict +"""Interfaces.""" +from typing import NewType from typing import TYPE_CHECKING +from typing import TypedDict if TYPE_CHECKING: from spiffworkflow_backend.models.process_group import ProcessGroup @@ -9,12 +11,14 @@ IdToProcessGroupMapping = NewType("IdToProcessGroupMapping", dict[str, "ProcessG class ProcessGroupLite(TypedDict): + """ProcessGroupLite.""" id: str display_name: str class ProcessGroupLitesWithCache(TypedDict): + """ProcessGroupLitesWithCache.""" cache: dict[str, "ProcessGroup"] process_groups: list[ProcessGroupLite] diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/models/process_group.py b/spiffworkflow-backend/src/spiffworkflow_backend/models/process_group.py index 1439b045..63c851a5 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/models/process_group.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/models/process_group.py @@ -11,6 +11,7 @@ import marshmallow from marshmallow import post_load from marshmallow import Schema +from spiffworkflow_backend.interfaces import ProcessGroupLite from spiffworkflow_backend.models.process_model import ProcessModelInfo @@ -29,7 +30,7 @@ class ProcessGroup: default_factory=list[ProcessModelInfo] ) process_groups: list[ProcessGroup] = field(default_factory=list["ProcessGroup"]) - parent_groups: list[dict] | None = None + parent_groups: list[ProcessGroupLite] | None = None def __post_init__(self) -> None: """__post_init__.""" diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/models/process_model.py b/spiffworkflow-backend/src/spiffworkflow_backend/models/process_model.py index 0b3ffa23..c737b274 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/models/process_model.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/models/process_model.py @@ -10,8 +10,8 @@ from typing import Any import marshmallow from marshmallow import Schema from marshmallow.decorators import post_load -from spiffworkflow_backend.interfaces import ProcessGroupLite +from spiffworkflow_backend.interfaces import ProcessGroupLite from spiffworkflow_backend.models.file import File diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_models_controller.py b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_models_controller.py index ac651415..1709357a 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_models_controller.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_models_controller.py @@ -15,8 +15,8 @@ from flask import jsonify from flask import make_response from flask.wrappers import Response from flask_bpmn.api.api_error import ApiError -from spiffworkflow_backend.interfaces import IdToProcessGroupMapping +from spiffworkflow_backend.interfaces import IdToProcessGroupMapping from spiffworkflow_backend.models.file import FileSchema from spiffworkflow_backend.models.process_group import ProcessGroup from spiffworkflow_backend.models.process_instance_report import ( @@ -190,8 +190,14 @@ def process_model_list( if include_parent_groups: process_group_cache = IdToProcessGroupMapping({}) for process_model in process_models_to_return: - parent_group_lites_with_cache = ProcessModelService.get_parent_group_array_and_cache_it(process_model.id, process_group_cache) - process_model.parent_groups = parent_group_lites_with_cache['process_groups'] + parent_group_lites_with_cache = ( + ProcessModelService.get_parent_group_array_and_cache_it( + process_model.id, process_group_cache + ) + ) + process_model.parent_groups = parent_group_lites_with_cache[ + "process_groups" + ] pages = len(process_models) // per_page remainder = len(process_models) % per_page diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_model_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_model_service.py index c81b0e81..8fa25bc0 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_model_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_model_service.py @@ -1,6 +1,5 @@ """Process_model_service.""" import json -from typing import TypedDict import os import shutil from glob import glob @@ -14,7 +13,8 @@ from flask_bpmn.api.api_error import ApiError from spiffworkflow_backend.exceptions.process_entity_not_found_error import ( ProcessEntityNotFoundError, ) -from spiffworkflow_backend.interfaces import ProcessGroupLite, ProcessGroupLitesWithCache +from spiffworkflow_backend.interfaces import ProcessGroupLite +from spiffworkflow_backend.interfaces import ProcessGroupLitesWithCache from spiffworkflow_backend.models.process_group import ProcessGroup from spiffworkflow_backend.models.process_group import ProcessGroupSchema from spiffworkflow_backend.models.process_instance import ProcessInstanceModel @@ -239,7 +239,9 @@ class ProcessModelService(FileSystemService): return process_models @classmethod - def get_parent_group_array_and_cache_it(cls, process_identifier: str, process_group_cache: dict[str, ProcessGroup]) -> ProcessGroupLitesWithCache: + def get_parent_group_array_and_cache_it( + cls, process_identifier: str, process_group_cache: dict[str, ProcessGroup] + ) -> ProcessGroupLitesWithCache: """Get_parent_group_array.""" full_group_id_path = None parent_group_array: list[ProcessGroupLite] = [] @@ -258,13 +260,15 @@ class ProcessModelService(FileSystemService): parent_group_array.append( {"id": parent_group.id, "display_name": parent_group.display_name} ) - return {'cache': process_group_cache, 'process_groups': parent_group_array} + return {"cache": process_group_cache, "process_groups": parent_group_array} @classmethod def get_parent_group_array(cls, process_identifier: str) -> list[ProcessGroupLite]: """Get_parent_group_array.""" - parent_group_lites_with_cache = cls.get_parent_group_array_and_cache_it(process_identifier, {}) - return parent_group_lites_with_cache['process_groups'] + parent_group_lites_with_cache = cls.get_parent_group_array_and_cache_it( + process_identifier, {} + ) + return parent_group_lites_with_cache["process_groups"] @classmethod def get_process_groups( diff --git a/spiffworkflow-frontend/src/components/ProcessModelSearch.tsx b/spiffworkflow-frontend/src/components/ProcessModelSearch.tsx index 790dff2e..bd995bc3 100644 --- a/spiffworkflow-frontend/src/components/ProcessModelSearch.tsx +++ b/spiffworkflow-frontend/src/components/ProcessModelSearch.tsx @@ -2,7 +2,6 @@ import { ComboBox, // @ts-ignore } from '@carbon/react'; -import { truncateString } from '../helpers'; import { ProcessGroupLite, ProcessModel } from '../interfaces'; type OwnProps = {