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() {
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
asdf reshim python
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
asdf reshim python
fi

View File

@ -556,9 +556,11 @@ def process_instance_task_list(
else:
spiff_tasks = processor.get_all_user_tasks()
subprocesses_by_child_task_ids = processor.get_subprocesses_by_child_task_ids()
processor.get_highest_level_subprocesses_by_child_task_ids(
subprocesses_by_child_task_ids
subprocesses_by_child_task_ids, task_typename_by_task_id = (
processor.get_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 = []

View File

@ -625,7 +625,25 @@ class ProcessInstanceProcessor:
db.session.add(pim)
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.
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.
"""
bpmn_json = json.loads(self.serialize())
spiff_task_json = self.get_all_task_specs()
subprocesses_by_child_task_ids = {}
task_typename_by_task_id = {}
if "subprocesses" in bpmn_json:
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
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(
self, subprocesses_by_child_task_ids: dict
def get_highest_level_calling_subprocesses_by_child_task_ids(
self, subprocesses_by_child_task_ids: dict, task_typename_by_task_id: dict
) -> dict:
"""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():
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[subprocess_id]
)
self.get_highest_level_subprocesses_by_child_task_ids(
subprocesses_by_child_task_ids
self.get_highest_level_calling_subprocesses_by_child_task_ids(
subprocesses_by_child_task_ids, task_typename_by_task_id
)
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 { isInteger, modifyProcessIdentifierForPathParam } from '../helpers';
import HttpService from '../services/HttpService';
import ProcessInstanceListTabs from '../components/ProcessInstanceListTabs';
import { ProcessInstance } from '../interfaces';
export default function ProcessInstanceFindById() {
@ -69,11 +70,15 @@ export default function ProcessInstanceFindById() {
};
return (
<Form onSubmit={handleFormSubmission}>
<Stack gap={5}>
{formElements()}
{formButtons()}
</Stack>
</Form>
<>
<ProcessInstanceListTabs variant="find-by-id" />
<br />
<Form onSubmit={handleFormSubmission}>
<Stack gap={5}>
{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-bootstrap-typeahead/css/Typeahead.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 ProcessInstanceListTable from '../components/ProcessInstanceListTable';
import { getProcessModelFullIdentifierFromSearchParams } from '../helpers';
import { useUriListForPermissions } from '../hooks/UriListForPermissions';
import { PermissionsToCheck } from '../interfaces';
import { usePermissionFetcher } from '../hooks/PermissionService';
import ProcessInstanceListTabs from '../components/ProcessInstanceListTabs';
type OwnProps = {
variant: string;
@ -20,13 +15,6 @@ type OwnProps = {
export default function ProcessInstanceList({ variant }: OwnProps) {
const [searchParams] = useSearchParams();
const navigate = useNavigate();
const { targetUris } = useUriListForPermissions();
const permissionRequestData: PermissionsToCheck = {
[targetUris.processInstanceListPath]: ['GET'],
};
const { ability } = usePermissionFetcher(permissionRequestData);
const processInstanceBreadcrumbElement = () => {
const processModelFullIdentifier =
@ -57,45 +45,9 @@ export default function ProcessInstanceList({ variant }: OwnProps) {
return <h1>My Process Instances</h1>;
};
let selectedTabIndex = 0;
if (variant === 'all') {
selectedTabIndex = 1;
}
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>
<ProcessInstanceListTabs variant={variant} />
<br />
{processInstanceBreadcrumbElement()}
{processInstanceTitleElement()}

View File

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