added more task tables w/ burnettk
This commit is contained in:
parent
7e97ce674f
commit
d4e984a5ce
|
@ -38,10 +38,13 @@ class MyJSONEncoder(DefaultJSONProvider):
|
|||
return_dict = {}
|
||||
for row_key in obj.keys():
|
||||
row_value = obj[row_key]
|
||||
if hasattr(row_value, "__dict__"):
|
||||
if hasattr(row_value, "serialized"):
|
||||
return_dict.update(row_value.serialized)
|
||||
elif hasattr(row_value, "__dict__"):
|
||||
return_dict.update(row_value.__dict__)
|
||||
else:
|
||||
return_dict.update({row_key: row_value})
|
||||
if "_sa_instance_state" in return_dict:
|
||||
return_dict.pop("_sa_instance_state")
|
||||
return return_dict
|
||||
return super().default(obj)
|
||||
|
|
|
@ -901,6 +901,35 @@ paths:
|
|||
items:
|
||||
$ref: "#/components/schemas/Task"
|
||||
|
||||
/tasks/for-processes-started-by-others:
|
||||
parameters:
|
||||
- name: page
|
||||
in: query
|
||||
required: false
|
||||
description: The page number to return. Defaults to page 1.
|
||||
schema:
|
||||
type: integer
|
||||
- name: per_page
|
||||
in: query
|
||||
required: false
|
||||
description: The page number to return. Defaults to page 1.
|
||||
schema:
|
||||
type: integer
|
||||
get:
|
||||
tags:
|
||||
- Process Instances
|
||||
operationId: spiffworkflow_backend.routes.process_api_blueprint.task_list_for_processes_started_by_others
|
||||
summary: returns the list of tasks for given user's open process instances
|
||||
responses:
|
||||
"200":
|
||||
description: list of tasks
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/Task"
|
||||
|
||||
/process-instance/{process_instance_id}/tasks:
|
||||
parameters:
|
||||
- name: process_instance_id
|
||||
|
|
|
@ -99,17 +99,17 @@ class ProcessInstanceModel(SpiffworkflowBaseDBModel):
|
|||
local_bpmn_xml_file_contents = ""
|
||||
if self.bpmn_xml_file_contents:
|
||||
local_bpmn_xml_file_contents = self.bpmn_xml_file_contents.decode("utf-8")
|
||||
|
||||
return {
|
||||
"id": self.id,
|
||||
"process_model_identifier": self.process_model_identifier,
|
||||
"process_group_identifier": self.process_group_identifier,
|
||||
"status": self.status,
|
||||
"bpmn_json": self.bpmn_json,
|
||||
"start_in_seconds": self.start_in_seconds,
|
||||
"end_in_seconds": self.end_in_seconds,
|
||||
"process_initiator_id": self.process_initiator_id,
|
||||
"bpmn_xml_file_contents": local_bpmn_xml_file_contents,
|
||||
"bpmn_version_control_identifier": self.bpmn_version_control_identifier,
|
||||
"bpmn_version_control_type": self.bpmn_version_control_type,
|
||||
"spiff_step": self.spiff_step,
|
||||
}
|
||||
|
||||
|
|
|
@ -76,7 +76,7 @@ from spiffworkflow_backend.services.secret_service import SecretService
|
|||
from spiffworkflow_backend.services.service_task_service import ServiceTaskService
|
||||
from spiffworkflow_backend.services.spec_file_service import SpecFileService
|
||||
from spiffworkflow_backend.services.user_service import UserService
|
||||
from sqlalchemy import asc
|
||||
from sqlalchemy import and_, asc
|
||||
from sqlalchemy import desc
|
||||
|
||||
|
||||
|
@ -996,14 +996,30 @@ def task_list_my_tasks(page: int = 1, per_page: int = 100) -> flask.wrappers.Res
|
|||
return make_response(jsonify(response_json), 200)
|
||||
|
||||
|
||||
# @process_api_blueprint.route("/v1.0/tasks", methods=["GET"])
|
||||
def task_list_for_my_open_processes(page: int = 1, per_page: int = 100) -> flask.wrappers.Response:
|
||||
return get_tasks(page=page, per_page=per_page)
|
||||
|
||||
|
||||
def task_list_for_processes_started_by_others(page: int = 1, per_page: int = 100) -> flask.wrappers.Response:
|
||||
return get_tasks(processes_started_by_user=False, page=page, per_page=per_page)
|
||||
|
||||
|
||||
def get_tasks(processes_started_by_user: bool = True, page: int = 1, per_page: int = 100) -> flask.wrappers.Response:
|
||||
user_id = g.user.id
|
||||
active_tasks = (
|
||||
ActiveTaskModel.query.order_by(desc(ActiveTaskModel.id)) # type: ignore
|
||||
active_tasks_query = (
|
||||
ActiveTaskModel.query
|
||||
.join(ProcessInstanceModel)
|
||||
.filter_by(process_initiator_id=user_id)
|
||||
.outerjoin(GroupModel)
|
||||
.order_by(desc(ProcessInstanceModel.created_at_in_seconds)) # type: ignore
|
||||
)
|
||||
|
||||
if processes_started_by_user:
|
||||
active_tasks_query = active_tasks_query.filter_by(process_initiator_id=user_id)
|
||||
else:
|
||||
active_tasks_query = active_tasks_query.filter(ProcessInstanceModel.process_initiator_id != user_id)
|
||||
|
||||
active_tasks = (
|
||||
active_tasks_query.outerjoin(GroupModel)
|
||||
.outerjoin(ActiveTaskUserModel, and_(ActiveTaskUserModel.user_id == user_id))
|
||||
# just need this add_columns to add the process_model_identifier. Then add everything back that was removed.
|
||||
.add_columns(
|
||||
ProcessInstanceModel.process_model_identifier,
|
||||
|
@ -1015,10 +1031,10 @@ def task_list_for_my_open_processes(page: int = 1, per_page: int = 100) -> flask
|
|||
ActiveTaskModel.task_title,
|
||||
ActiveTaskModel.process_model_display_name,
|
||||
ActiveTaskModel.process_instance_id,
|
||||
ActiveTaskUserModel.user_id.label("current_user_is_potential_owner")
|
||||
)
|
||||
.paginate(page=page, per_page=per_page, error_out=False)
|
||||
)
|
||||
# tasks = [ActiveTaskModel.to_task(active_task) for active_task in active_tasks.items]
|
||||
|
||||
response_json = {
|
||||
"results": active_tasks.items,
|
||||
|
@ -1028,7 +1044,6 @@ def task_list_for_my_open_processes(page: int = 1, per_page: int = 100) -> flask
|
|||
"pages": active_tasks.pages,
|
||||
},
|
||||
}
|
||||
|
||||
return make_response(jsonify(response_json), 200)
|
||||
|
||||
|
||||
|
@ -1383,12 +1398,21 @@ def find_principal_or_raise() -> PrincipalModel:
|
|||
|
||||
|
||||
def find_process_instance_by_id_or_raise(
|
||||
process_instance_id: int,
|
||||
process_instance_id: int
|
||||
) -> ProcessInstanceModel:
|
||||
"""Find_process_instance_by_id_or_raise."""
|
||||
process_instance = ProcessInstanceModel.query.filter_by(
|
||||
process_instance_query = ProcessInstanceModel.query.filter_by(
|
||||
id=process_instance_id
|
||||
).first()
|
||||
)
|
||||
|
||||
# we had a frustrating session trying to do joins and access columns from two tables. here's some notes for our future selves:
|
||||
# this returns an object that allows you to do: process_instance.UserModel.username
|
||||
# process_instance = db.session.query(ProcessInstanceModel, UserModel).filter_by(id=process_instance_id).first()
|
||||
# you can also use splat with add_columns, but it still didn't ultimately give us access to the process instance
|
||||
# attributes or username like we wanted:
|
||||
# process_instance_query.join(UserModel).add_columns(*ProcessInstanceModel.__table__.columns, UserModel.username)
|
||||
|
||||
process_instance = process_instance_query.first()
|
||||
if process_instance is None:
|
||||
raise (
|
||||
ApiError(
|
||||
|
|
|
@ -0,0 +1,137 @@
|
|||
import { useEffect, useState } from 'react';
|
||||
// @ts-ignore
|
||||
import { Button, Table } from '@carbon/react';
|
||||
import { Link, useSearchParams } from 'react-router-dom';
|
||||
import PaginationForTable from './PaginationForTable';
|
||||
import {
|
||||
convertSecondsToFormattedDateTime,
|
||||
getPageInfoFromSearchParams,
|
||||
modifyProcessModelPath,
|
||||
} from '../helpers';
|
||||
import HttpService from '../services/HttpService';
|
||||
import { PaginationObject } from '../interfaces';
|
||||
|
||||
const PER_PAGE_FOR_TASKS_ON_HOME_PAGE = 5;
|
||||
|
||||
export default function MyTasksForProcessesStartedByOthers() {
|
||||
const [searchParams] = useSearchParams();
|
||||
const [tasks, setTasks] = useState([]);
|
||||
const [pagination, setPagination] = useState<PaginationObject | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const { page, perPage } = getPageInfoFromSearchParams(
|
||||
searchParams,
|
||||
PER_PAGE_FOR_TASKS_ON_HOME_PAGE
|
||||
);
|
||||
const setTasksFromResult = (result: any) => {
|
||||
setTasks(result.results);
|
||||
setPagination(result.pagination);
|
||||
};
|
||||
HttpService.makeCallToBackend({
|
||||
path: `/tasks/for-processes-started-by-others?per_page=${perPage}&page=${page}`,
|
||||
successCallback: setTasksFromResult,
|
||||
});
|
||||
}, [searchParams]);
|
||||
|
||||
const buildTable = () => {
|
||||
const rows = tasks.map((row) => {
|
||||
const rowToUse = row as any;
|
||||
const taskUrl = `/tasks/${rowToUse.process_instance_id}/${rowToUse.task_id}`;
|
||||
const modifiedProcessModelIdentifier = modifyProcessModelPath(
|
||||
rowToUse.process_model_identifier
|
||||
);
|
||||
return (
|
||||
<tr key={rowToUse.id}>
|
||||
<td>
|
||||
<Link
|
||||
data-qa="process-model-show-link"
|
||||
to={`/admin/process-models/${modifiedProcessModelIdentifier}`}
|
||||
>
|
||||
{rowToUse.process_model_display_name}
|
||||
</Link>
|
||||
</td>
|
||||
<td>
|
||||
<Link
|
||||
data-qa="process-instance-show-link"
|
||||
to={`/admin/process-models/${modifiedProcessModelIdentifier}/process-instances/${rowToUse.process_instance_id}`}
|
||||
>
|
||||
View {rowToUse.process_instance_id}
|
||||
</Link>
|
||||
</td>
|
||||
<td
|
||||
title={`task id: ${rowToUse.name}, spiffworkflow task guid: ${rowToUse.id}`}
|
||||
>
|
||||
{rowToUse.task_title}
|
||||
</td>
|
||||
<td>{rowToUse.process_instance_status}</td>
|
||||
<td>{rowToUse.group_identifier || '-'}</td>
|
||||
<td>
|
||||
{convertSecondsToFormattedDateTime(
|
||||
rowToUse.created_at_in_seconds
|
||||
) || '-'}
|
||||
</td>
|
||||
<td>
|
||||
{convertSecondsToFormattedDateTime(
|
||||
rowToUse.updated_at_in_seconds
|
||||
) || '-'}
|
||||
</td>
|
||||
<td>
|
||||
<Button
|
||||
variant="primary"
|
||||
href={taskUrl}
|
||||
hidden={rowToUse.process_instance_status === 'suspended'}
|
||||
disabled={!rowToUse.current_user_is_potential_owner}
|
||||
>
|
||||
Go
|
||||
</Button>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
});
|
||||
return (
|
||||
<Table striped bordered>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Process Model</th>
|
||||
<th>Process Instance</th>
|
||||
<th>Task Name</th>
|
||||
<th>Process Instance Status</th>
|
||||
<th>Assigned Group</th>
|
||||
<th>Process Started</th>
|
||||
<th>Process Updated</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>{rows}</tbody>
|
||||
</Table>
|
||||
);
|
||||
};
|
||||
|
||||
const tasksComponent = () => {
|
||||
if (pagination && pagination.total < 1) {
|
||||
return null;
|
||||
}
|
||||
const { page, perPage } = getPageInfoFromSearchParams(
|
||||
searchParams,
|
||||
PER_PAGE_FOR_TASKS_ON_HOME_PAGE
|
||||
);
|
||||
return (
|
||||
<>
|
||||
<h1>Tasks waiting for me</h1>
|
||||
<PaginationForTable
|
||||
page={page}
|
||||
perPage={perPage}
|
||||
perPageOptions={[2, PER_PAGE_FOR_TASKS_ON_HOME_PAGE, 25]}
|
||||
pagination={pagination}
|
||||
tableToDisplay={buildTable()}
|
||||
path="/tasks/for-my-open-processes"
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
if (pagination) {
|
||||
return tasksComponent();
|
||||
}
|
||||
return null;
|
||||
}
|
|
@ -2,7 +2,7 @@ import { useEffect, useState } from 'react';
|
|||
// @ts-ignore
|
||||
import { Button, Table } from '@carbon/react';
|
||||
import { Link, useSearchParams } from 'react-router-dom';
|
||||
import PaginationForTable from '../components/PaginationForTable';
|
||||
import PaginationForTable from './PaginationForTable';
|
||||
import {
|
||||
convertSecondsToFormattedDateTime,
|
||||
getPageInfoFromSearchParams,
|
||||
|
@ -36,7 +36,7 @@ export default function MyOpenProcesses() {
|
|||
const buildTable = () => {
|
||||
const rows = tasks.map((row) => {
|
||||
const rowToUse = row as any;
|
||||
const taskUrl = `/tasks/${rowToUse.process_instance_id}/${rowToUse.id}`;
|
||||
const taskUrl = `/tasks/${rowToUse.process_instance_id}/${rowToUse.task_id}`;
|
||||
const modifiedProcessModelIdentifier = modifyProcessModelPath(
|
||||
rowToUse.process_model_identifier
|
||||
);
|
||||
|
@ -80,6 +80,7 @@ export default function MyOpenProcesses() {
|
|||
variant="primary"
|
||||
href={taskUrl}
|
||||
hidden={rowToUse.process_instance_status === 'suspended'}
|
||||
disabled={!rowToUse.current_user_is_potential_owner}
|
||||
>
|
||||
Go
|
||||
</Button>
|
||||
|
@ -106,7 +107,7 @@ export default function MyOpenProcesses() {
|
|||
);
|
||||
};
|
||||
|
||||
const tasksWaitingForMeComponent = () => {
|
||||
const tasksComponent = () => {
|
||||
if (pagination && pagination.total < 1) {
|
||||
return null;
|
||||
}
|
||||
|
@ -129,13 +130,8 @@ export default function MyOpenProcesses() {
|
|||
);
|
||||
};
|
||||
|
||||
const tasksWaitingForMe = tasksWaitingForMeComponent();
|
||||
|
||||
if (pagination) {
|
||||
if (tasksWaitingForMe === null) {
|
||||
return <p>No tasks are waiting for you.</p>;
|
||||
}
|
||||
return <>{tasksWaitingForMe}</>;
|
||||
return tasksComponent();
|
||||
}
|
||||
return null;
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
import MyTasksForProcessesStartedByOthers from '../components/MyTasksForProcessesStartedByOthers';
|
||||
import TasksForMyOpenProcesses from '../components/TasksForMyOpenProcesses';
|
||||
|
||||
export default function GroupedTasks() {
|
||||
return (
|
||||
<>
|
||||
<TasksForMyOpenProcesses />
|
||||
<br />
|
||||
<MyTasksForProcessesStartedByOthers />
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -5,7 +5,7 @@ import { Tabs, TabList, Tab } from '@carbon/react';
|
|||
import TaskShow from './TaskShow';
|
||||
import ErrorContext from '../contexts/ErrorContext';
|
||||
import MyTasks from './MyTasks';
|
||||
import TasksForMyOpenProcesses from './TasksForMyOpenProcesses';
|
||||
import GroupedTasks from './GroupedTasks';
|
||||
|
||||
export default function HomePageRoutes() {
|
||||
const location = useLocation();
|
||||
|
@ -16,7 +16,7 @@ export default function HomePageRoutes() {
|
|||
useEffect(() => {
|
||||
setErrorMessage(null);
|
||||
let newSelectedTabIndex = 0;
|
||||
if (location.pathname.match(/^\/tasks\/for-my-open-processes/)) {
|
||||
if (location.pathname.match(/^\/tasks\/grouped\b/)) {
|
||||
newSelectedTabIndex = 1;
|
||||
}
|
||||
setSelectedTabIndex(newSelectedTabIndex);
|
||||
|
@ -27,9 +27,7 @@ export default function HomePageRoutes() {
|
|||
<Tabs selectedIndex={selectedTabIndex}>
|
||||
<TabList aria-label="List of tabs">
|
||||
<Tab onClick={() => navigate('/tasks/my-tasks')}>My Tasks</Tab>
|
||||
<Tab onClick={() => navigate('/tasks/for-my-open-processes')}>
|
||||
Tasks for My Open Processes
|
||||
</Tab>
|
||||
<Tab onClick={() => navigate('/tasks/grouped')}>Grouped Tasks</Tab>
|
||||
</TabList>
|
||||
</Tabs>
|
||||
<br />
|
||||
|
@ -37,10 +35,7 @@ export default function HomePageRoutes() {
|
|||
<Route path="/" element={<MyTasks />} />
|
||||
<Route path="my-tasks" element={<MyTasks />} />
|
||||
<Route path=":process_instance_id/:task_id" element={<TaskShow />} />
|
||||
<Route
|
||||
path="for-my-open-processes"
|
||||
element={<TasksForMyOpenProcesses />}
|
||||
/>
|
||||
<Route path="grouped" element={<GroupedTasks />} />
|
||||
</Routes>
|
||||
</>
|
||||
);
|
||||
|
|
Loading…
Reference in New Issue