added all users to waiting for column on task list tables w/ burnettk

This commit is contained in:
jasquat 2022-12-30 12:30:23 -05:00
parent 76c3f13630
commit 840d2f0937
8 changed files with 114 additions and 48 deletions

View File

@ -63,7 +63,7 @@ groups:
admin-ro:
users:
[
j,
j@sartography.com,
]
permissions:

View File

@ -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)

View File

@ -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."""

View File

@ -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 = (
<>

View File

@ -81,7 +81,7 @@ export default function NavigationBar() {
return (
<>
<HeaderGlobalAction className="username-header-text">
{UserService.getUsername()}
{UserService.getPreferredUsername()}
</HeaderGlobalAction>
<HeaderGlobalAction
aria-label="Logout"

View File

@ -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>

View File

@ -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 {

View File

@ -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,
};