added all users to waiting for column on task list tables w/ burnettk
This commit is contained in:
parent
45097a10d9
commit
0af82d137d
|
@ -63,7 +63,7 @@ groups:
|
|||
admin-ro:
|
||||
users:
|
||||
[
|
||||
j,
|
||||
j@sartography.com,
|
||||
]
|
||||
|
||||
permissions:
|
||||
|
|
|
@ -15,11 +15,14 @@ from flask import jsonify
|
|||
from flask import make_response
|
||||
from flask.wrappers import Response
|
||||
from flask_bpmn.api.api_error import ApiError
|
||||
from flask_bpmn.models.db import db
|
||||
from SpiffWorkflow.task import Task as SpiffTask # type: ignore
|
||||
from SpiffWorkflow.task import TaskState
|
||||
from sqlalchemy import and_
|
||||
from sqlalchemy import asc
|
||||
from sqlalchemy import desc
|
||||
from sqlalchemy import func
|
||||
from sqlalchemy.orm import aliased
|
||||
|
||||
from spiffworkflow_backend.models.group import GroupModel
|
||||
from spiffworkflow_backend.models.human_task import HumanTaskModel
|
||||
|
@ -147,6 +150,18 @@ def task_show(process_instance_id: int, task_id: str) -> flask.wrappers.Response
|
|||
process_instance.process_model_identifier,
|
||||
)
|
||||
|
||||
human_task = HumanTaskModel.query.filter_by(
|
||||
process_instance_id=process_instance_id, task_id=task_id
|
||||
).first()
|
||||
if human_task is None:
|
||||
raise (
|
||||
ApiError(
|
||||
error_code="no_human_task",
|
||||
message=f"Cannot find a task to complete for task id '{task_id}' and process instance {process_instance_id}.",
|
||||
status_code=500,
|
||||
)
|
||||
)
|
||||
|
||||
form_schema_file_name = ""
|
||||
form_ui_schema_file_name = ""
|
||||
spiff_task = _get_spiff_task_from_process_instance(task_id, process_instance)
|
||||
|
@ -302,7 +317,7 @@ def task_submit(
|
|||
raise (
|
||||
ApiError(
|
||||
error_code="no_human_task",
|
||||
message="Cannot find an human task with task id '{task_id}' for process instance {process_instance_id}.",
|
||||
message=f"Cannot find a task to complete for task id '{task_id}' and process instance {process_instance_id}.",
|
||||
status_code=500,
|
||||
)
|
||||
)
|
||||
|
@ -357,22 +372,25 @@ def _get_tasks(
|
|||
# pagination later on
|
||||
# https://stackoverflow.com/q/34582014/6090676
|
||||
human_tasks_query = (
|
||||
HumanTaskModel.query.distinct()
|
||||
db.session.query(HumanTaskModel)
|
||||
.group_by(HumanTaskModel.id) # type: ignore
|
||||
.outerjoin(GroupModel, GroupModel.id == HumanTaskModel.lane_assignment_id)
|
||||
.join(ProcessInstanceModel)
|
||||
.join(UserModel, UserModel.id == ProcessInstanceModel.process_initiator_id)
|
||||
.filter(HumanTaskModel.completed == False) # noqa: E712
|
||||
)
|
||||
|
||||
assigned_user = aliased(UserModel)
|
||||
if processes_started_by_user:
|
||||
human_tasks_query = human_tasks_query.filter(
|
||||
ProcessInstanceModel.process_initiator_id == user_id
|
||||
).outerjoin(
|
||||
HumanTaskUserModel,
|
||||
and_(
|
||||
HumanTaskUserModel.user_id == user_id,
|
||||
human_tasks_query = (
|
||||
human_tasks_query.filter(
|
||||
ProcessInstanceModel.process_initiator_id == user_id
|
||||
)
|
||||
.outerjoin(
|
||||
HumanTaskUserModel,
|
||||
HumanTaskModel.id == HumanTaskUserModel.human_task_id,
|
||||
),
|
||||
)
|
||||
.outerjoin(assigned_user, assigned_user.id == HumanTaskUserModel.user_id)
|
||||
)
|
||||
else:
|
||||
human_tasks_query = human_tasks_query.filter(
|
||||
|
@ -402,13 +420,15 @@ def _get_tasks(
|
|||
ProcessInstanceModel.status.label("process_instance_status"), # type: ignore
|
||||
ProcessInstanceModel.updated_at_in_seconds,
|
||||
ProcessInstanceModel.created_at_in_seconds,
|
||||
UserModel.username,
|
||||
GroupModel.identifier.label("user_group_identifier"),
|
||||
UserModel.username.label("process_initiator_username"),
|
||||
GroupModel.identifier.label("assigned_user_group_identifier"),
|
||||
HumanTaskModel.task_name,
|
||||
HumanTaskModel.task_title,
|
||||
HumanTaskModel.process_model_display_name,
|
||||
HumanTaskModel.process_instance_id,
|
||||
HumanTaskUserModel.user_id.label("current_user_is_potential_owner"),
|
||||
func.group_concat(assigned_user.username.distinct()).label(
|
||||
"potential_owner_usernames"
|
||||
),
|
||||
)
|
||||
.order_by(desc(HumanTaskModel.id)) # type: ignore
|
||||
.paginate(page=page, per_page=per_page, error_out=False)
|
||||
|
@ -422,6 +442,7 @@ def _get_tasks(
|
|||
"pages": human_tasks.pages,
|
||||
},
|
||||
}
|
||||
|
||||
return make_response(jsonify(response_json), 200)
|
||||
|
||||
|
||||
|
|
|
@ -581,12 +581,6 @@ class ProcessInstanceProcessor:
|
|||
)
|
||||
return details_model
|
||||
|
||||
def save_spiff_step_details(self) -> None:
|
||||
"""SaveSpiffStepDetails."""
|
||||
details_model = self.spiff_step_details()
|
||||
db.session.add(details_model)
|
||||
db.session.commit()
|
||||
|
||||
def extract_metadata(self, process_model_info: ProcessModelInfo) -> None:
|
||||
"""Extract_metadata."""
|
||||
metadata_extraction_paths = process_model_info.metadata_extraction_paths
|
||||
|
@ -1233,9 +1227,13 @@ class ProcessInstanceProcessor:
|
|||
self.increment_spiff_step()
|
||||
self.bpmn_process_instance.complete_task_from_id(task.id)
|
||||
human_task.completed_by_user_id = user.id
|
||||
human_task.completed = True
|
||||
db.session.add(human_task)
|
||||
db.session.commit()
|
||||
self.save_spiff_step_details()
|
||||
details_model = self.spiff_step_details()
|
||||
db.session.add(details_model)
|
||||
|
||||
# this is the thing that actually commits the db transaction (on behalf of the other updates above as well)
|
||||
self.save()
|
||||
|
||||
def get_data(self) -> dict[str, Any]:
|
||||
"""Get_data."""
|
||||
|
|
|
@ -49,7 +49,7 @@ export default function App() {
|
|||
|
||||
let message = <div>{errorObject.message}</div>;
|
||||
let title = 'Error:';
|
||||
if ('task_name' in errorObject) {
|
||||
if ('task_name' in errorObject && errorObject.task_name) {
|
||||
title = `Error in python script:`;
|
||||
message = (
|
||||
<>
|
||||
|
|
|
@ -81,7 +81,7 @@ export default function NavigationBar() {
|
|||
return (
|
||||
<>
|
||||
<HeaderGlobalAction className="username-header-text">
|
||||
{UserService.getUsername()}
|
||||
{UserService.getPreferredUsername()}
|
||||
</HeaderGlobalAction>
|
||||
<HeaderGlobalAction
|
||||
aria-label="Logout"
|
||||
|
|
|
@ -2,6 +2,7 @@ import { useEffect, useState } from 'react';
|
|||
// @ts-ignore
|
||||
import { Button, Table } from '@carbon/react';
|
||||
import { Link, useSearchParams } from 'react-router-dom';
|
||||
import UserService from '../services/UserService';
|
||||
import PaginationForTable from './PaginationForTable';
|
||||
import {
|
||||
convertSecondsToFormattedDateTime,
|
||||
|
@ -46,6 +47,9 @@ export default function TaskListTable({
|
|||
const [tasks, setTasks] = useState<ProcessInstanceTask[] | null>(null);
|
||||
const [pagination, setPagination] = useState<PaginationObject | null>(null);
|
||||
|
||||
const preferredUsername = UserService.getPreferredUsername();
|
||||
const userEmail = UserService.getUserEmail();
|
||||
|
||||
useEffect(() => {
|
||||
const getTasks = () => {
|
||||
const { page, perPage } = getPageInfoFromSearchParams(
|
||||
|
@ -80,56 +84,82 @@ export default function TaskListTable({
|
|||
autoReload,
|
||||
]);
|
||||
|
||||
const getWaitingForTableCellComponent = (
|
||||
processInstanceTask: ProcessInstanceTask
|
||||
) => {
|
||||
let fullUsernameString = '';
|
||||
let shortUsernameString = '';
|
||||
if (processInstanceTask.assigned_user_group_identifier) {
|
||||
fullUsernameString = processInstanceTask.assigned_user_group_identifier;
|
||||
shortUsernameString = processInstanceTask.assigned_user_group_identifier;
|
||||
}
|
||||
if (processInstanceTask.potential_owner_usernames) {
|
||||
fullUsernameString = processInstanceTask.potential_owner_usernames;
|
||||
const usernames =
|
||||
processInstanceTask.potential_owner_usernames.split(',');
|
||||
const firstTwoUsernames = usernames.slice(0, 2);
|
||||
if (usernames.length > 2) {
|
||||
firstTwoUsernames.push('...');
|
||||
}
|
||||
shortUsernameString = firstTwoUsernames.join(',');
|
||||
}
|
||||
return <span title={fullUsernameString}>{shortUsernameString}</span>;
|
||||
};
|
||||
|
||||
const buildTable = () => {
|
||||
if (!tasks) {
|
||||
return null;
|
||||
}
|
||||
const rows = tasks.map((row) => {
|
||||
const rowToUse = row as any;
|
||||
const taskUrl = `/tasks/${rowToUse.process_instance_id}/${rowToUse.task_id}`;
|
||||
const rows = tasks.map((row: ProcessInstanceTask) => {
|
||||
const taskUrl = `/tasks/${row.process_instance_id}/${row.task_id}`;
|
||||
const modifiedProcessModelIdentifier =
|
||||
modifyProcessIdentifierForPathParam(rowToUse.process_model_identifier);
|
||||
modifyProcessIdentifierForPathParam(row.process_model_identifier);
|
||||
|
||||
const regex = new RegExp(`\\b(${preferredUsername}|${userEmail})\\b`);
|
||||
let hasAccessToCompleteTask = false;
|
||||
if (row.potential_owner_usernames.match(regex)) {
|
||||
hasAccessToCompleteTask = true;
|
||||
}
|
||||
return (
|
||||
<tr key={rowToUse.id}>
|
||||
<tr key={row.id}>
|
||||
<td>
|
||||
<Link
|
||||
data-qa="process-instance-show-link"
|
||||
to={`/admin/process-instances/for-me/${modifiedProcessModelIdentifier}/${rowToUse.process_instance_id}`}
|
||||
title={`View process instance ${rowToUse.process_instance_id}`}
|
||||
to={`/admin/process-instances/for-me/${modifiedProcessModelIdentifier}/${row.process_instance_id}`}
|
||||
title={`View process instance ${row.process_instance_id}`}
|
||||
>
|
||||
{rowToUse.process_instance_id}
|
||||
{row.process_instance_id}
|
||||
</Link>
|
||||
</td>
|
||||
<td>
|
||||
<Link
|
||||
data-qa="process-model-show-link"
|
||||
to={`/admin/process-models/${modifiedProcessModelIdentifier}`}
|
||||
title={rowToUse.process_model_identifier}
|
||||
title={row.process_model_identifier}
|
||||
>
|
||||
{rowToUse.process_model_display_name}
|
||||
{row.process_model_display_name}
|
||||
</Link>
|
||||
</td>
|
||||
<td
|
||||
title={`task id: ${rowToUse.name}, spiffworkflow task guid: ${rowToUse.id}`}
|
||||
title={`task id: ${row.name}, spiffworkflow task guid: ${row.id}`}
|
||||
>
|
||||
{rowToUse.task_title}
|
||||
{row.task_title}
|
||||
</td>
|
||||
{showStartedBy ? <td>{rowToUse.username}</td> : ''}
|
||||
{showWaitingOn ? <td>{rowToUse.group_identifier || '-'}</td> : ''}
|
||||
{showStartedBy ? <td>{row.process_initiator_username}</td> : ''}
|
||||
{showWaitingOn ? <td>{getWaitingForTableCellComponent(row)}</td> : ''}
|
||||
<td>
|
||||
{convertSecondsToFormattedDateTime(
|
||||
rowToUse.created_at_in_seconds
|
||||
) || '-'}
|
||||
{convertSecondsToFormattedDateTime(row.created_at_in_seconds) ||
|
||||
'-'}
|
||||
</td>
|
||||
<TableCellWithTimeAgoInWords
|
||||
timeInSeconds={rowToUse.updated_at_in_seconds}
|
||||
timeInSeconds={row.updated_at_in_seconds}
|
||||
/>
|
||||
<td>
|
||||
<Button
|
||||
variant="primary"
|
||||
href={taskUrl}
|
||||
hidden={rowToUse.process_instance_status === 'suspended'}
|
||||
disabled={!rowToUse.current_user_is_potential_owner}
|
||||
hidden={row.process_instance_status === 'suspended'}
|
||||
disabled={!hasAccessToCompleteTask}
|
||||
>
|
||||
Go
|
||||
</Button>
|
||||
|
|
|
@ -17,16 +17,23 @@ export interface RecentProcessModel {
|
|||
}
|
||||
|
||||
export interface ProcessInstanceTask {
|
||||
id: string;
|
||||
id: number;
|
||||
task_id: string;
|
||||
process_instance_id: number;
|
||||
process_model_display_name: string;
|
||||
process_model_identifier: string;
|
||||
task_title: string;
|
||||
lane_assignment_id: string;
|
||||
process_instance_status: number;
|
||||
updated_at_in_seconds: number;
|
||||
process_instance_status: string;
|
||||
state: string;
|
||||
process_identifier: string;
|
||||
name: string;
|
||||
process_initiator_username: string;
|
||||
assigned_user_group_identifier: string;
|
||||
created_at_in_seconds: number;
|
||||
updated_at_in_seconds: number;
|
||||
current_user_is_potential_owner: number;
|
||||
potential_owner_usernames: string;
|
||||
}
|
||||
|
||||
export interface ProcessReference {
|
||||
|
|
|
@ -39,7 +39,16 @@ const isLoggedIn = () => {
|
|||
return !!getAuthToken();
|
||||
};
|
||||
|
||||
const getUsername = () => {
|
||||
const getUserEmail = () => {
|
||||
const idToken = getIdToken();
|
||||
if (idToken) {
|
||||
const idObject = jwt(idToken);
|
||||
return (idObject as any).email;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const getPreferredUsername = () => {
|
||||
const idToken = getIdToken();
|
||||
if (idToken) {
|
||||
const idObject = jwt(idToken);
|
||||
|
@ -78,7 +87,8 @@ const UserService = {
|
|||
isLoggedIn,
|
||||
getAuthToken,
|
||||
getAuthTokenFromParams,
|
||||
getUsername,
|
||||
getPreferredUsername,
|
||||
getUserEmail,
|
||||
hasRole,
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue