Merge branch 'main' into feature/waku-fault-message

This commit is contained in:
mike cullerton 2023-01-13 12:11:40 -05:00
commit 043dea0796
7 changed files with 153 additions and 80 deletions

View File

@ -38,12 +38,19 @@ function run_fix_docstrings() {
} }
function run_autoflake() { function run_autoflake() {
if ! command -v autoflake8 >/dev/null ; then # checking command -v autoflake8 is not good enough, since the asdf shim may be installed, which will make command -v succeed,
# but autoflake8 may not have been pip installed inside the correct version of python.
if ! autoflake8 --help >/dev/null ; then
pip install autoflake8 pip install autoflake8
asdf reshim python asdf reshim python
fi fi
if ! command -v autopep8 >/dev/null ; then if ! autoflake --help >/dev/null ; then
pip install autoflake
asdf reshim python
fi
if ! autopep8 --help >/dev/null ; then
pip install autopep8 pip install autopep8
asdf reshim python asdf reshim python
fi fi

View File

@ -556,9 +556,11 @@ def process_instance_task_list(
else: else:
spiff_tasks = processor.get_all_user_tasks() spiff_tasks = processor.get_all_user_tasks()
subprocesses_by_child_task_ids = processor.get_subprocesses_by_child_task_ids() subprocesses_by_child_task_ids, task_typename_by_task_id = (
processor.get_highest_level_subprocesses_by_child_task_ids( processor.get_subprocesses_by_child_task_ids()
subprocesses_by_child_task_ids )
processor.get_highest_level_calling_subprocesses_by_child_task_ids(
subprocesses_by_child_task_ids, task_typename_by_task_id
) )
tasks = [] tasks = []

View File

@ -625,7 +625,25 @@ class ProcessInstanceProcessor:
db.session.add(pim) db.session.add(pim)
db.session.commit() db.session.commit()
def get_subprocesses_by_child_task_ids(self) -> dict: def get_all_task_specs(self) -> dict[str, dict]:
"""This looks both at top level task_specs and subprocess_specs in the serialized data.
It returns a dict of all task specs based on the task name like it is in the serialized form.
NOTE: this may not fully work for tasks that are NOT call activities since their task_name may no be unique
but in our current use case we only care about the call activities here.
"""
serialized_data = json.loads(self.serialize())
spiff_task_json = serialized_data["spec"]["task_specs"] or {}
if "subprocess_specs" in serialized_data:
for _subprocess_task_name, subprocess_details in serialized_data[
"subprocess_specs"
].items():
if "task_specs" in subprocess_details:
spiff_task_json = spiff_task_json | subprocess_details["task_specs"]
return spiff_task_json
def get_subprocesses_by_child_task_ids(self) -> Tuple[dict, dict]:
"""Get all subprocess ids based on the child task ids. """Get all subprocess ids based on the child task ids.
This is useful when trying to link the child task of a call activity back to This is useful when trying to link the child task of a call activity back to
@ -641,27 +659,45 @@ class ProcessInstanceProcessor:
call activities like subprocesses in terms of the serialization. call activities like subprocesses in terms of the serialization.
""" """
bpmn_json = json.loads(self.serialize()) bpmn_json = json.loads(self.serialize())
spiff_task_json = self.get_all_task_specs()
subprocesses_by_child_task_ids = {} subprocesses_by_child_task_ids = {}
task_typename_by_task_id = {}
if "subprocesses" in bpmn_json: if "subprocesses" in bpmn_json:
for subprocess_id, subprocess_details in bpmn_json["subprocesses"].items(): for subprocess_id, subprocess_details in bpmn_json["subprocesses"].items():
for task_id in subprocess_details["tasks"]: for task_id, task_details in subprocess_details["tasks"].items():
subprocesses_by_child_task_ids[task_id] = subprocess_id subprocesses_by_child_task_ids[task_id] = subprocess_id
return subprocesses_by_child_task_ids task_name = task_details["task_spec"]
if task_name in spiff_task_json:
task_typename_by_task_id[task_id] = spiff_task_json[task_name][
"typename"
]
return (subprocesses_by_child_task_ids, task_typename_by_task_id)
def get_highest_level_subprocesses_by_child_task_ids( def get_highest_level_calling_subprocesses_by_child_task_ids(
self, subprocesses_by_child_task_ids: dict self, subprocesses_by_child_task_ids: dict, task_typename_by_task_id: dict
) -> dict: ) -> dict:
"""Ensure task ids point to the top level subprocess id. """Ensure task ids point to the top level subprocess id.
This is done by checking if a subprocess is also a task until the subprocess is no longer a task. This is done by checking if a subprocess is also a task until the subprocess is no longer a task or a Call Activity.
""" """
for task_id, subprocess_id in subprocesses_by_child_task_ids.items(): for task_id, subprocess_id in subprocesses_by_child_task_ids.items():
if subprocess_id in subprocesses_by_child_task_ids: if subprocess_id in subprocesses_by_child_task_ids:
current_subprocess_id_for_task = subprocesses_by_child_task_ids[task_id]
if current_subprocess_id_for_task in task_typename_by_task_id:
# a call activity is like the top-level subprocess since it is the calling subprocess
# according to spiff and the top-level calling subprocess is really what we care about
if (
task_typename_by_task_id[current_subprocess_id_for_task]
== "CallActivity"
):
continue
subprocesses_by_child_task_ids[task_id] = ( subprocesses_by_child_task_ids[task_id] = (
subprocesses_by_child_task_ids[subprocess_id] subprocesses_by_child_task_ids[subprocess_id]
) )
self.get_highest_level_subprocesses_by_child_task_ids( self.get_highest_level_calling_subprocesses_by_child_task_ids(
subprocesses_by_child_task_ids subprocesses_by_child_task_ids, task_typename_by_task_id
) )
return subprocesses_by_child_task_ids return subprocesses_by_child_task_ids

View File

@ -0,0 +1,63 @@
// @ts-ignore
import { Tabs, TabList, Tab } from '@carbon/react';
import { Can } from '@casl/react';
import { useNavigate } from 'react-router-dom';
import { usePermissionFetcher } from '../hooks/PermissionService';
import { useUriListForPermissions } from '../hooks/UriListForPermissions';
import { PermissionsToCheck } from '../interfaces';
type OwnProps = {
variant: string;
};
export default function ProcessInstanceListTabs({ variant }: OwnProps) {
const navigate = useNavigate();
const { targetUris } = useUriListForPermissions();
const permissionRequestData: PermissionsToCheck = {
[targetUris.processInstanceListPath]: ['GET'],
};
const { ability } = usePermissionFetcher(permissionRequestData);
let selectedTabIndex = 0;
if (variant === 'all') {
selectedTabIndex = 1;
} else if (variant === 'find-by-id') {
selectedTabIndex = 2;
}
return (
<Tabs selectedIndex={selectedTabIndex}>
<TabList aria-label="List of tabs">
<Tab
title="Only show process instances for the current user."
data-qa="process-instance-list-for-me"
onClick={() => {
navigate('/admin/process-instances/for-me');
}}
>
For Me
</Tab>
<Can I="GET" a={targetUris.processInstanceListPath} ability={ability}>
<Tab
title="Show all process instances for all users."
data-qa="process-instance-list-all"
onClick={() => {
navigate('/admin/process-instances/all');
}}
>
All
</Tab>
</Can>
<Tab
title="Search for a process instance by id."
data-qa="process-instance-list-find-by-id"
onClick={() => {
navigate('/admin/process-instances/find-by-id');
}}
>
Find By Id
</Tab>
</TabList>
</Tabs>
);
}

View File

@ -4,6 +4,7 @@ import { useNavigate } from 'react-router-dom';
import { Button, ButtonSet, Form, Stack, TextInput } from '@carbon/react'; import { Button, ButtonSet, Form, Stack, TextInput } from '@carbon/react';
import { isInteger, modifyProcessIdentifierForPathParam } from '../helpers'; import { isInteger, modifyProcessIdentifierForPathParam } from '../helpers';
import HttpService from '../services/HttpService'; import HttpService from '../services/HttpService';
import ProcessInstanceListTabs from '../components/ProcessInstanceListTabs';
import { ProcessInstance } from '../interfaces'; import { ProcessInstance } from '../interfaces';
export default function ProcessInstanceFindById() { export default function ProcessInstanceFindById() {
@ -69,11 +70,15 @@ export default function ProcessInstanceFindById() {
}; };
return ( return (
<Form onSubmit={handleFormSubmission}> <>
<Stack gap={5}> <ProcessInstanceListTabs variant="find-by-id" />
{formElements()} <br />
{formButtons()} <Form onSubmit={handleFormSubmission}>
</Stack> <Stack gap={5}>
</Form> {formElements()}
{formButtons()}
</Stack>
</Form>
</>
); );
} }

View File

@ -1,18 +1,13 @@
import { useNavigate, useSearchParams } from 'react-router-dom'; import { useSearchParams } from 'react-router-dom';
import 'react-datepicker/dist/react-datepicker.css'; import 'react-datepicker/dist/react-datepicker.css';
import 'react-bootstrap-typeahead/css/Typeahead.css'; import 'react-bootstrap-typeahead/css/Typeahead.css';
import 'react-bootstrap-typeahead/css/Typeahead.bs5.css'; import 'react-bootstrap-typeahead/css/Typeahead.bs5.css';
// @ts-ignore
import { Tabs, TabList, Tab } from '@carbon/react';
import { Can } from '@casl/react';
import ProcessBreadcrumb from '../components/ProcessBreadcrumb'; import ProcessBreadcrumb from '../components/ProcessBreadcrumb';
import ProcessInstanceListTable from '../components/ProcessInstanceListTable'; import ProcessInstanceListTable from '../components/ProcessInstanceListTable';
import { getProcessModelFullIdentifierFromSearchParams } from '../helpers'; import { getProcessModelFullIdentifierFromSearchParams } from '../helpers';
import { useUriListForPermissions } from '../hooks/UriListForPermissions'; import ProcessInstanceListTabs from '../components/ProcessInstanceListTabs';
import { PermissionsToCheck } from '../interfaces';
import { usePermissionFetcher } from '../hooks/PermissionService';
type OwnProps = { type OwnProps = {
variant: string; variant: string;
@ -20,13 +15,6 @@ type OwnProps = {
export default function ProcessInstanceList({ variant }: OwnProps) { export default function ProcessInstanceList({ variant }: OwnProps) {
const [searchParams] = useSearchParams(); const [searchParams] = useSearchParams();
const navigate = useNavigate();
const { targetUris } = useUriListForPermissions();
const permissionRequestData: PermissionsToCheck = {
[targetUris.processInstanceListPath]: ['GET'],
};
const { ability } = usePermissionFetcher(permissionRequestData);
const processInstanceBreadcrumbElement = () => { const processInstanceBreadcrumbElement = () => {
const processModelFullIdentifier = const processModelFullIdentifier =
@ -57,45 +45,9 @@ export default function ProcessInstanceList({ variant }: OwnProps) {
return <h1>My Process Instances</h1>; return <h1>My Process Instances</h1>;
}; };
let selectedTabIndex = 0;
if (variant === 'all') {
selectedTabIndex = 1;
}
return ( return (
<> <>
<Tabs selectedIndex={selectedTabIndex}> <ProcessInstanceListTabs variant={variant} />
<TabList aria-label="List of tabs">
<Tab
title="Only show process instances for the current user."
data-qa="process-instance-list-for-me"
onClick={() => {
navigate('/admin/process-instances/for-me');
}}
>
For Me
</Tab>
<Can I="GET" a={targetUris.processInstanceListPath} ability={ability}>
<Tab
title="Show all process instances for all users."
data-qa="process-instance-list-all"
onClick={() => {
navigate('/admin/process-instances/all');
}}
>
All
</Tab>
</Can>
<Tab
title="Search for a process instance by id."
data-qa="process-instance-list-find-by-id"
onClick={() => {
navigate('/admin/process-instances/find-by-id');
}}
>
Find By Id
</Tab>
</TabList>
</Tabs>
<br /> <br />
{processInstanceBreadcrumbElement()} {processInstanceBreadcrumbElement()}
{processInstanceTitleElement()} {processInstanceTitleElement()}

View File

@ -200,19 +200,21 @@ export default function ProcessInstanceShow({ variant }: OwnProps) {
const getTaskIds = () => { const getTaskIds = () => {
const taskIds = { completed: [], readyOrWaiting: [] }; const taskIds = { completed: [], readyOrWaiting: [] };
if (tasks) { if (tasks) {
const callingSubprocessId = searchParams.get('call_activity_task_id');
tasks.forEach(function getUserTasksElement(task: ProcessInstanceTask) { tasks.forEach(function getUserTasksElement(task: ProcessInstanceTask) {
const callingSubprocessId = searchParams.get('call_activity_task_id');
if ( if (
!callingSubprocessId || callingSubprocessId &&
callingSubprocessId === task.calling_subprocess_task_id callingSubprocessId !== task.calling_subprocess_task_id
) { ) {
if (task.state === 'COMPLETED') { return null;
(taskIds.completed as any).push(task);
}
if (task.state === 'READY' || task.state === 'WAITING') {
(taskIds.readyOrWaiting as any).push(task);
}
} }
if (task.state === 'COMPLETED') {
(taskIds.completed as any).push(task);
}
if (task.state === 'READY' || task.state === 'WAITING') {
(taskIds.readyOrWaiting as any).push(task);
}
return null;
}); });
} }
return taskIds; return taskIds;
@ -904,6 +906,10 @@ export default function ProcessInstanceShow({ variant }: OwnProps) {
const taskToUse: any = { ...taskToDisplay, data: taskDataToDisplay }; const taskToUse: any = { ...taskToDisplay, data: taskDataToDisplay };
const candidateEvents: any = getEvents(taskToUse); const candidateEvents: any = getEvents(taskToUse);
if (taskToDisplay) { if (taskToDisplay) {
let taskTitleText = taskToUse.id;
if (taskToUse.title) {
taskTitleText += ` (${taskToUse.title})`;
}
return ( return (
<Modal <Modal
open={!!taskToUse} open={!!taskToUse}
@ -911,7 +917,9 @@ export default function ProcessInstanceShow({ variant }: OwnProps) {
onRequestClose={handleTaskDataDisplayClose} onRequestClose={handleTaskDataDisplayClose}
> >
<Stack orientation="horizontal" gap={2}> <Stack orientation="horizontal" gap={2}>
{taskToUse.name} ({taskToUse.type}): {taskToUse.state} <span title={taskTitleText}>{taskToUse.name}</span> (
{taskToUse.type}
): {taskToUse.state}
{taskDisplayButtons(taskToUse)} {taskDisplayButtons(taskToUse)}
</Stack> </Stack>
{selectingEvent {selectingEvent