From 840d2f0937230aee49cf673ee93b2794c2f9da56 Mon Sep 17 00:00:00 2001 From: jasquat Date: Fri, 30 Dec 2022 12:30:23 -0500 Subject: [PATCH] added all users to waiting for column on task list tables w/ burnettk --- .../config/permissions/development.yml | 2 +- .../routes/tasks_controller.py | 45 ++++++++---- .../services/process_instance_processor.py | 14 ++-- spiffworkflow-frontend/src/App.tsx | 2 +- .../src/components/NavigationBar.tsx | 2 +- .../src/components/TaskListTable.tsx | 70 +++++++++++++------ spiffworkflow-frontend/src/interfaces.ts | 13 +++- .../src/services/UserService.ts | 14 +++- 8 files changed, 114 insertions(+), 48 deletions(-) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/development.yml b/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/development.yml index 397f10c1..ee40f839 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/development.yml +++ b/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/development.yml @@ -63,7 +63,7 @@ groups: admin-ro: users: [ - j, + j@sartography.com, ] permissions: diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/routes/tasks_controller.py b/spiffworkflow-backend/src/spiffworkflow_backend/routes/tasks_controller.py index c80da91f..a8f899b5 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/routes/tasks_controller.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/routes/tasks_controller.py @@ -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) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py index f9e6c620..6b03d4ce 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py @@ -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.""" diff --git a/spiffworkflow-frontend/src/App.tsx b/spiffworkflow-frontend/src/App.tsx index c052b6a0..73489083 100644 --- a/spiffworkflow-frontend/src/App.tsx +++ b/spiffworkflow-frontend/src/App.tsx @@ -49,7 +49,7 @@ export default function App() { let message =
{errorObject.message}
; let title = 'Error:'; - if ('task_name' in errorObject) { + if ('task_name' in errorObject && errorObject.task_name) { title = `Error in python script:`; message = ( <> diff --git a/spiffworkflow-frontend/src/components/NavigationBar.tsx b/spiffworkflow-frontend/src/components/NavigationBar.tsx index 7a0ffd3e..e482ae52 100644 --- a/spiffworkflow-frontend/src/components/NavigationBar.tsx +++ b/spiffworkflow-frontend/src/components/NavigationBar.tsx @@ -81,7 +81,7 @@ export default function NavigationBar() { return ( <> - {UserService.getUsername()} + {UserService.getPreferredUsername()} (null); const [pagination, setPagination] = useState(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 {shortUsernameString}; + }; + 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 ( - + - {rowToUse.process_instance_id} + {row.process_instance_id} - {rowToUse.process_model_display_name} + {row.process_model_display_name} - {rowToUse.task_title} + {row.task_title} - {showStartedBy ? {rowToUse.username} : ''} - {showWaitingOn ? {rowToUse.group_identifier || '-'} : ''} + {showStartedBy ? {row.process_initiator_username} : ''} + {showWaitingOn ? {getWaitingForTableCellComponent(row)} : ''} - {convertSecondsToFormattedDateTime( - rowToUse.created_at_in_seconds - ) || '-'} + {convertSecondsToFormattedDateTime(row.created_at_in_seconds) || + '-'} diff --git a/spiffworkflow-frontend/src/interfaces.ts b/spiffworkflow-frontend/src/interfaces.ts index e58747b1..bb368013 100644 --- a/spiffworkflow-frontend/src/interfaces.ts +++ b/spiffworkflow-frontend/src/interfaces.ts @@ -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 { diff --git a/spiffworkflow-frontend/src/services/UserService.ts b/spiffworkflow-frontend/src/services/UserService.ts index df0f213e..53717b8d 100644 --- a/spiffworkflow-frontend/src/services/UserService.ts +++ b/spiffworkflow-frontend/src/services/UserService.ts @@ -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, };