From ee8af5df13bc21490a3e1400084f00c222f4f27b Mon Sep 17 00:00:00 2001 From: jasquat Date: Fri, 20 Oct 2023 14:34:55 -0400 Subject: [PATCH] support formatting data client side in markdown and support greater than and less than for metadata column filters w/ burnettk --- .../routes/extensions_controller.py | 5 +- .../process_instance_report_service.py | 4 + .../components/ProcessInstanceListTable.tsx | 20 +---- spiffworkflow-frontend/src/helpers.tsx | 18 +++++ .../src/routes/Extension.tsx | 77 ++++++++++++++++++- spiffworkflow-frontend/tsconfig.json | 2 +- 6 files changed, 100 insertions(+), 26 deletions(-) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/routes/extensions_controller.py b/spiffworkflow-backend/src/spiffworkflow_backend/routes/extensions_controller.py index 999910d1c..ddf57811c 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/routes/extensions_controller.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/routes/extensions_controller.py @@ -153,11 +153,12 @@ def _run_extension( script_engine=CustomBpmnScriptEngine(use_restricted_script_engine=False), process_id_to_run=process_id_to_run, ) + save_to_db = process_instance.persistence_level != "none" if body and "extension_input" in body: - processor.do_engine_steps(save=False, execution_strategy_name="run_current_ready_tasks") + processor.do_engine_steps(save=save_to_db, execution_strategy_name="run_current_ready_tasks") next_task = processor.next_task() next_task.update_data(body["extension_input"]) - processor.do_engine_steps(save=False, execution_strategy_name="greedy") + processor.do_engine_steps(save=save_to_db, execution_strategy_name="greedy") except ( ApiError, ProcessInstanceIsNotEnqueuedError, diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_report_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_report_service.py index fc72b2a67..8d39b2d36 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_report_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_report_service.py @@ -643,6 +643,10 @@ def run_process_instance_report( join_conditions.append(instance_metadata_alias.value == filter_for_column["field_value"]) elif filter_for_column["operator"] == "not_equals": join_conditions.append(instance_metadata_alias.value != filter_for_column["field_value"]) + elif filter_for_column["operator"] == "greater_than_or_equal_to": + join_conditions.append(instance_metadata_alias.value >= filter_for_column["field_value"]) + elif filter_for_column["operator"] == "less_than": + join_conditions.append(instance_metadata_alias.value < filter_for_column["field_value"]) elif filter_for_column["operator"] == "contains": join_conditions.append(instance_metadata_alias.value.like(f"%{filter_for_column['field_value']}%")) elif filter_for_column["operator"] == "is_empty": diff --git a/spiffworkflow-frontend/src/components/ProcessInstanceListTable.tsx b/spiffworkflow-frontend/src/components/ProcessInstanceListTable.tsx index 410cff3b4..c6e02a8b4 100644 --- a/spiffworkflow-frontend/src/components/ProcessInstanceListTable.tsx +++ b/spiffworkflow-frontend/src/components/ProcessInstanceListTable.tsx @@ -50,8 +50,8 @@ import { REFRESH_TIMEOUT_SECONDS, titleizeString, truncateString, - isANumber, formatDurationForDisplay, + formatDateTime, } from '../helpers'; import { useUriListForPermissions } from '../hooks/UriListForPermissions'; @@ -1697,24 +1697,6 @@ export default function ProcessInstanceListTable({ return value; }; - const formatDateTime = (_row: ProcessInstance, value: any) => { - if (value === undefined || value === null) { - return value; - } - let dateInSeconds = value; - if (!isANumber(value)) { - const timeArgs = value.split('T'); - dateInSeconds = convertDateAndTimeStringsToSeconds( - timeArgs[0], - timeArgs[1] - ); - } - if (dateInSeconds) { - return convertSecondsToFormattedDateTime(dateInSeconds); - } - return null; - }; - const formattedColumn = (row: ProcessInstance, column: ReportColumn) => { const reportColumnFormatters: Record = { id: formatProcessInstanceId, diff --git a/spiffworkflow-frontend/src/helpers.tsx b/spiffworkflow-frontend/src/helpers.tsx index e51f3c822..fbcdcd2bb 100644 --- a/spiffworkflow-frontend/src/helpers.tsx +++ b/spiffworkflow-frontend/src/helpers.tsx @@ -442,3 +442,21 @@ export const formatDurationForDisplay = (_row: any, value: any) => { } return durationTimes.join(' '); }; + +export const formatDateTime = (_row: any, value: any) => { + if (value === undefined || value === null) { + return value; + } + let dateInSeconds = value; + if (!isANumber(value)) { + const timeArgs = value.split('T'); + dateInSeconds = convertDateAndTimeStringsToSeconds( + timeArgs[0], + timeArgs[1] + ); + } + if (dateInSeconds) { + return convertSecondsToFormattedDateTime(dateInSeconds); + } + return null; +}; diff --git a/spiffworkflow-frontend/src/routes/Extension.tsx b/spiffworkflow-frontend/src/routes/Extension.tsx index 573b6eb52..03844eebe 100644 --- a/spiffworkflow-frontend/src/routes/Extension.tsx +++ b/spiffworkflow-frontend/src/routes/Extension.tsx @@ -1,4 +1,4 @@ -import { useCallback, useEffect, useState } from 'react'; +import { useCallback, useEffect, useMemo, useState } from 'react'; import { Button } from '@carbon/react'; import MDEditor from '@uiw/react-md-editor'; import { useParams, useSearchParams } from 'react-router-dom'; @@ -7,7 +7,11 @@ import { useUriListForPermissions } from '../hooks/UriListForPermissions'; import { ProcessFile, ProcessModel } from '../interfaces'; import HttpService from '../services/HttpService'; import useAPIError from '../hooks/UseApiError'; -import { recursivelyChangeNullAndUndefined } from '../helpers'; +import { + formatDateTime, + formatDurationForDisplay, + recursivelyChangeNullAndUndefined, +} from '../helpers'; import CustomForm from '../components/CustomForm'; import { BACKEND_BASE_URL } from '../config'; import { @@ -41,13 +45,50 @@ export default function Extension() { const { addError, removeError } = useAPIError(); + const spiffConversionFunctions: { [key: string]: Function } = useMemo(() => { + return { + convert_seconds_to_date_time_for_display: formatDateTime, + convert_seconds_to_duration_for_display: formatDurationForDisplay, + }; + }, []); + + const checkForSpiffConversions = useCallback( + (markdown: string) => { + const replacer = ( + match: string, + spiffConversion: string, + originalValue: string + ) => { + if (spiffConversion in spiffConversionFunctions) { + return spiffConversionFunctions[spiffConversion]( + undefined, + originalValue + ); + } + console.warn( + `attempted: ${match}, but ${spiffConversion} is not a valid conversion function` + ); + + return match; + }; + return markdown.replaceAll( + /SPIFF_CONVERSION:::(\w+)\(([^)]+)\)/g, + replacer + ); + }, + [spiffConversionFunctions] + ); + const setConfigsIfDesiredSchemaFile = useCallback( // eslint-disable-next-line sonarjs/cognitive-complexity (extensionUiSchemaFile: ProcessFile | null, pm: ProcessModel) => { const processLoadResult = (result: any) => { setFormData(result.task_data); if (result.rendered_results_markdown) { - setMarkdownToRenderOnLoad(result.rendered_results_markdown); + const newMarkdown = checkForSpiffConversions( + result.rendered_results_markdown + ); + setMarkdownToRenderOnLoad(newMarkdown); } }; @@ -99,6 +140,7 @@ export default function Extension() { params.page_identifier, searchParams, filesByName, + checkForSpiffConversions, ] ); @@ -125,6 +167,30 @@ export default function Extension() { targetUris.extensionPath, ]); + // this relies on the MDEditor rendering the markdown before + // this useEffect triggers which may or may not alwasy be the case + // so we may need to change implementation to a search/replace + // before renddering any markdown. + useEffect(() => { + const convertField = (className: string, conversionFunction: any) => { + const elements = document.getElementsByClassName(className); + if (elements.length > 0) { + for (let i = 0; i < elements.length; i += 1) { + const element = elements[i]; + if (element) { + const originalValue = element.innerHTML; + element.innerHTML = conversionFunction(undefined, originalValue); + } + } + } + }; + convertField('convert_seconds_to_date_time_for_display', formatDateTime); + convertField( + 'convert_seconds_to_duration_for_display', + formatDurationForDisplay + ); + }, [markdownToRenderOnLoad, markdownToRenderOnSubmit]); + const interpolateNavigationString = ( navigationString: string, baseData: any @@ -162,7 +228,10 @@ export default function Extension() { } else { setProcessedTaskData(result.task_data); if (result.rendered_results_markdown) { - setMarkdownToRenderOnSubmit(result.rendered_results_markdown); + const newMarkdown = checkForSpiffConversions( + result.rendered_results_markdown + ); + setMarkdownToRenderOnSubmit(newMarkdown); } setFormButtonsDisabled(false); } diff --git a/spiffworkflow-frontend/tsconfig.json b/spiffworkflow-frontend/tsconfig.json index 87997abad..ed6b17ab0 100644 --- a/spiffworkflow-frontend/tsconfig.json +++ b/spiffworkflow-frontend/tsconfig.json @@ -6,7 +6,7 @@ "module": "commonjs", "skipLibCheck": true, "strict": true, - "target": "es2016", + "target": "es2021", }, "include": ["src/**/*"] }