Start of system report filters (#57)

This commit is contained in:
jbirddog 2022-11-22 17:14:51 -05:00 committed by GitHub
parent 8b562b0bf2
commit ceefa9a2f7
6 changed files with 196 additions and 14 deletions

View File

@ -514,6 +514,24 @@ paths:
description: For filtering - not_started, user_input_required, waiting, complete, error, or suspended description: For filtering - not_started, user_input_required, waiting, complete, error, or suspended
schema: schema:
type: string type: string
- name: initiated_by_me
in: query
required: false
description: For filtering - show instances initiated by me
schema:
type: boolean
- name: with_tasks_completed_by_me
in: query
required: false
description: For filtering - show instances with tasks completed by me
schema:
type: boolean
- name: with_tasks_completed_by_my_group
in: query
required: false
description: For filtering - show instances with tasks completed by my group
schema:
type: boolean
- name: user_filter - name: user_filter
in: query in: query
required: false required: false

View File

@ -30,6 +30,7 @@ from SpiffWorkflow.task import TaskState
from sqlalchemy import and_ from sqlalchemy import and_
from sqlalchemy import asc from sqlalchemy import asc
from sqlalchemy import desc from sqlalchemy import desc
from sqlalchemy import select
from spiffworkflow_backend.exceptions.process_entity_not_found_error import ( from spiffworkflow_backend.exceptions.process_entity_not_found_error import (
ProcessEntityNotFoundError, ProcessEntityNotFoundError,
@ -63,6 +64,7 @@ from spiffworkflow_backend.models.spec_reference import SpecReferenceSchema
from spiffworkflow_backend.models.spiff_logging import SpiffLoggingModel from spiffworkflow_backend.models.spiff_logging import SpiffLoggingModel
from spiffworkflow_backend.models.spiff_step_details import SpiffStepDetailsModel from spiffworkflow_backend.models.spiff_step_details import SpiffStepDetailsModel
from spiffworkflow_backend.models.user import UserModel from spiffworkflow_backend.models.user import UserModel
from spiffworkflow_backend.models.user_group_assignment import UserGroupAssignmentModel
from spiffworkflow_backend.routes.user import verify_token from spiffworkflow_backend.routes.user import verify_token
from spiffworkflow_backend.services.authorization_service import AuthorizationService from spiffworkflow_backend.services.authorization_service import AuthorizationService
from spiffworkflow_backend.services.error_handling_service import ErrorHandlingService from spiffworkflow_backend.services.error_handling_service import ErrorHandlingService
@ -762,6 +764,9 @@ def process_instance_list(
end_from: Optional[int] = None, end_from: Optional[int] = None,
end_to: Optional[int] = None, end_to: Optional[int] = None,
process_status: Optional[str] = None, process_status: Optional[str] = None,
initiated_by_me: Optional[bool] = None,
with_tasks_completed_by_me: Optional[bool] = None,
with_tasks_completed_by_my_group: Optional[bool] = None,
user_filter: Optional[bool] = False, user_filter: Optional[bool] = False,
report_identifier: Optional[str] = None, report_identifier: Optional[str] = None,
) -> flask.wrappers.Response: ) -> flask.wrappers.Response:
@ -778,6 +783,9 @@ def process_instance_list(
end_from, end_from,
end_to, end_to,
process_status.split(",") if process_status else None, process_status.split(",") if process_status else None,
initiated_by_me,
with_tasks_completed_by_me,
with_tasks_completed_by_my_group,
) )
else: else:
report_filter = ( report_filter = (
@ -789,6 +797,9 @@ def process_instance_list(
end_from, end_from,
end_to, end_to,
process_status, process_status,
initiated_by_me,
with_tasks_completed_by_me,
with_tasks_completed_by_my_group,
) )
) )
@ -837,9 +848,79 @@ def process_instance_list(
ProcessInstanceModel.status.in_(report_filter.process_status) # type: ignore ProcessInstanceModel.status.in_(report_filter.process_status) # type: ignore
) )
process_instances = process_instance_query.order_by( if report_filter.initiated_by_me is True:
process_instance_query = process_instance_query.filter(
ProcessInstanceModel.status == "complete"
)
process_instance_query = process_instance_query.filter_by(
process_initiator=g.user
)
# TODO: not sure if this is exactly what is wanted
if report_filter.with_tasks_completed_by_me is True:
process_instance_query = process_instance_query.filter(
ProcessInstanceModel.status == "complete"
)
process_instance_query = process_instance_query.join(
SpiffStepDetailsModel,
ProcessInstanceModel.id == SpiffStepDetailsModel.process_instance_id,
)
process_instance_query = process_instance_query.join(
SpiffLoggingModel,
ProcessInstanceModel.id == SpiffLoggingModel.process_instance_id,
)
process_instance_query = process_instance_query.filter(
SpiffLoggingModel.message.contains("COMPLETED") # type: ignore
)
process_instance_query = process_instance_query.filter(
SpiffLoggingModel.spiff_step == SpiffStepDetailsModel.spiff_step
)
process_instance_query = process_instance_query.filter(
SpiffStepDetailsModel.completed_by_user_id == g.user.id
)
# TODO: not sure if this is exactly what is wanted
if report_filter.with_tasks_completed_by_my_group is True:
process_instance_query = process_instance_query.filter(
ProcessInstanceModel.status == "complete"
)
process_instance_query = process_instance_query.join(
SpiffStepDetailsModel,
ProcessInstanceModel.id == SpiffStepDetailsModel.process_instance_id,
)
process_instance_query = process_instance_query.join(
SpiffLoggingModel,
ProcessInstanceModel.id == SpiffLoggingModel.process_instance_id,
)
process_instance_query = process_instance_query.filter(
SpiffLoggingModel.message.contains("COMPLETED") # type: ignore
)
process_instance_query = process_instance_query.filter(
SpiffLoggingModel.spiff_step == SpiffStepDetailsModel.spiff_step
)
my_groups = (
select(UserGroupAssignmentModel) # type: ignore
.where(UserGroupAssignmentModel.user_id == g.user.id)
.with_only_columns(UserGroupAssignmentModel.group_id)
)
users_in_my_groups = (
select(UserGroupAssignmentModel) # type: ignore
.where(UserGroupAssignmentModel.group_id.in_(my_groups))
.with_only_columns(UserGroupAssignmentModel.user_id)
)
process_instance_query = process_instance_query.filter(
SpiffStepDetailsModel.completed_by_user_id.in_(users_in_my_groups) # type: ignore
)
process_instances = (
process_instance_query.distinct()
.order_by(
ProcessInstanceModel.start_in_seconds.desc(), ProcessInstanceModel.id.desc() # type: ignore ProcessInstanceModel.start_in_seconds.desc(), ProcessInstanceModel.id.desc() # type: ignore
).paginate(page=page, per_page=per_page, error_out=False) )
.paginate(page=page, per_page=per_page, error_out=False)
)
results = list( results = list(
map( map(

View File

@ -18,6 +18,9 @@ class ProcessInstanceReportFilter:
end_from: Optional[int] = None end_from: Optional[int] = None
end_to: Optional[int] = None end_to: Optional[int] = None
process_status: Optional[list[str]] = None process_status: Optional[list[str]] = None
initiated_by_me: Optional[bool] = None
with_tasks_completed_by_me: Optional[bool] = None
with_tasks_completed_by_my_group: Optional[bool] = None
def to_dict(self) -> dict[str, str]: def to_dict(self) -> dict[str, str]:
"""To_dict.""" """To_dict."""
@ -35,6 +38,16 @@ class ProcessInstanceReportFilter:
d["end_to"] = str(self.end_to) d["end_to"] = str(self.end_to)
if self.process_status is not None: if self.process_status is not None:
d["process_status"] = ",".join(self.process_status) d["process_status"] = ",".join(self.process_status)
if self.initiated_by_me is not None:
d["initiated_by_me"] = str(self.initiated_by_me).lower()
if self.with_tasks_completed_by_me is not None:
d["with_tasks_completed_by_me"] = str(
self.with_tasks_completed_by_me
).lower()
if self.with_tasks_completed_by_my_group is not None:
d["with_tasks_completed_by_my_group"] = str(
self.with_tasks_completed_by_my_group
).lower()
return d return d
@ -73,30 +86,35 @@ class ProcessInstanceReportService:
}, },
"system_report_instances_initiated_by_me": { "system_report_instances_initiated_by_me": {
"columns": [ "columns": [
{"Header": "id", "accessor": "id"},
{ {
"Header": "process_model_identifier", "Header": "process_model_identifier",
"accessor": "process_model_identifier", "accessor": "process_model_identifier",
}, },
{"Header": "start_in_seconds", "accessor": "start_in_seconds"}, {"Header": "start_in_seconds", "accessor": "start_in_seconds"},
{"Header": "id", "accessor": "id"},
{"Header": "end_in_seconds", "accessor": "end_in_seconds"}, {"Header": "end_in_seconds", "accessor": "end_in_seconds"},
{"Header": "status", "accessor": "status"}, {"Header": "status", "accessor": "status"},
], ],
"filter_by": [{"field_name": "initiated_by_me", "field_value": True}],
}, },
"system_report_instances_with_tasks_completed_by_me": { "system_report_instances_with_tasks_completed_by_me": {
"columns": [ "columns": [
{"Header": "start_in_seconds", "accessor": "start_in_seconds"},
{"Header": "end_in_seconds", "accessor": "end_in_seconds"},
{"Header": "status", "accessor": "status"},
{"Header": "id", "accessor": "id"}, {"Header": "id", "accessor": "id"},
{ {
"Header": "process_model_identifier", "Header": "process_model_identifier",
"accessor": "process_model_identifier", "accessor": "process_model_identifier",
}, },
{"Header": "start_in_seconds", "accessor": "start_in_seconds"},
{"Header": "end_in_seconds", "accessor": "end_in_seconds"},
{"Header": "status", "accessor": "status"},
],
"filter_by": [
{"field_name": "with_tasks_completed_by_me", "field_value": True}
], ],
}, },
"system_report_instances_with_tasks_completed_by_my_groups": { "system_report_instances_with_tasks_completed_by_my_groups": {
"columns": [ "columns": [
{"Header": "id", "accessor": "id"},
{ {
"Header": "process_model_identifier", "Header": "process_model_identifier",
"accessor": "process_model_identifier", "accessor": "process_model_identifier",
@ -104,7 +122,12 @@ class ProcessInstanceReportService:
{"Header": "start_in_seconds", "accessor": "start_in_seconds"}, {"Header": "start_in_seconds", "accessor": "start_in_seconds"},
{"Header": "end_in_seconds", "accessor": "end_in_seconds"}, {"Header": "end_in_seconds", "accessor": "end_in_seconds"},
{"Header": "status", "accessor": "status"}, {"Header": "status", "accessor": "status"},
{"Header": "id", "accessor": "id"}, ],
"filter_by": [
{
"field_name": "with_tasks_completed_by_my_group",
"field_value": True,
}
], ],
}, },
} }
@ -112,7 +135,7 @@ class ProcessInstanceReportService:
process_instance_report = ProcessInstanceReportModel( process_instance_report = ProcessInstanceReportModel(
identifier=report_identifier, identifier=report_identifier,
created_by_id=user.id, created_by_id=user.id,
report_metadata=temp_system_metadata_map[report_identifier], report_metadata=temp_system_metadata_map[report_identifier], # type: ignore
) )
return process_instance_report # type: ignore return process_instance_report # type: ignore
@ -138,6 +161,10 @@ class ProcessInstanceReportService:
"""Filter_from_metadata.""" """Filter_from_metadata."""
filters = cls.filter_by_to_dict(process_instance_report) filters = cls.filter_by_to_dict(process_instance_report)
def bool_value(key: str) -> Optional[bool]:
"""Bool_value."""
return bool(filters[key]) if key in filters else None
def int_value(key: str) -> Optional[int]: def int_value(key: str) -> Optional[int]:
"""Int_value.""" """Int_value."""
return int(filters[key]) if key in filters else None return int(filters[key]) if key in filters else None
@ -152,6 +179,11 @@ class ProcessInstanceReportService:
end_from = int_value("end_from") end_from = int_value("end_from")
end_to = int_value("end_to") end_to = int_value("end_to")
process_status = list_value("process_status") process_status = list_value("process_status")
initiated_by_me = bool_value("initiated_by_me")
with_tasks_completed_by_me = bool_value("with_tasks_completed_by_me")
with_tasks_completed_by_my_group = bool_value(
"with_tasks_completed_by_my_group"
)
report_filter = ProcessInstanceReportFilter( report_filter = ProcessInstanceReportFilter(
process_model_identifier, process_model_identifier,
@ -160,6 +192,9 @@ class ProcessInstanceReportService:
end_from, end_from,
end_to, end_to,
process_status, process_status,
initiated_by_me,
with_tasks_completed_by_me,
with_tasks_completed_by_my_group,
) )
return report_filter return report_filter
@ -174,6 +209,9 @@ class ProcessInstanceReportService:
end_from: Optional[int] = None, end_from: Optional[int] = None,
end_to: Optional[int] = None, end_to: Optional[int] = None,
process_status: Optional[str] = None, process_status: Optional[str] = None,
initiated_by_me: Optional[bool] = None,
with_tasks_completed_by_me: Optional[bool] = None,
with_tasks_completed_by_my_group: Optional[bool] = None,
) -> ProcessInstanceReportFilter: ) -> ProcessInstanceReportFilter:
"""Filter_from_metadata_with_overrides.""" """Filter_from_metadata_with_overrides."""
report_filter = cls.filter_from_metadata(process_instance_report) report_filter = cls.filter_from_metadata(process_instance_report)
@ -190,5 +228,13 @@ class ProcessInstanceReportService:
report_filter.end_to = end_to report_filter.end_to = end_to
if process_status is not None: if process_status is not None:
report_filter.process_status = process_status.split(",") report_filter.process_status = process_status.split(",")
if initiated_by_me is not None:
report_filter.initiated_by_me = initiated_by_me
if with_tasks_completed_by_me is not None:
report_filter.with_tasks_completed_by_me = with_tasks_completed_by_me
if with_tasks_completed_by_my_group is not None:
report_filter.with_tasks_completed_by_my_group = (
with_tasks_completed_by_my_group
)
return report_filter return report_filter

View File

@ -8,6 +8,8 @@ export default function MyCompletedInstances() {
filtersEnabled={false} filtersEnabled={false}
paginationQueryParamPrefix={paginationQueryParamPrefix} paginationQueryParamPrefix={paginationQueryParamPrefix}
perPageOptions={[2, 5, 25]} perPageOptions={[2, 5, 25]}
reportIdentifier="system_report_instances_initiated_by_me"
showReports={false}
/> />
); );
} }

View File

@ -57,6 +57,7 @@ type OwnProps = {
paginationQueryParamPrefix?: string; paginationQueryParamPrefix?: string;
perPageOptions?: number[]; perPageOptions?: number[];
showReports?: boolean; showReports?: boolean;
reportIdentifier?: string;
}; };
interface dateParameters { interface dateParameters {
@ -69,6 +70,7 @@ export default function ProcessInstanceListTable({
paginationQueryParamPrefix, paginationQueryParamPrefix,
perPageOptions, perPageOptions,
showReports = true, showReports = true,
reportIdentifier,
}: OwnProps) { }: OwnProps) {
const params = useParams(); const params = useParams();
const [searchParams] = useSearchParams(); const [searchParams] = useSearchParams();
@ -173,9 +175,14 @@ export default function ProcessInstanceListTable({
queryParamString += `&user_filter=${userAppliedFilter}`; queryParamString += `&user_filter=${userAppliedFilter}`;
} }
const reportIdentifier = searchParams.get('report_identifier'); let reportIdentifierToUse: any = reportIdentifier;
if (reportIdentifier) {
queryParamString += `&report_identifier=${reportIdentifier}`; if (!reportIdentifierToUse) {
reportIdentifierToUse = searchParams.get('report_identifier');
}
if (reportIdentifierToUse) {
queryParamString += `&report_identifier=${reportIdentifierToUse}`;
} }
Object.keys(dateParametersToAlwaysFilterBy).forEach( Object.keys(dateParametersToAlwaysFilterBy).forEach(
@ -273,6 +280,7 @@ export default function ProcessInstanceListTable({
paginationQueryParamPrefix, paginationQueryParamPrefix,
processModelFullIdentifier, processModelFullIdentifier,
perPageOptions, perPageOptions,
reportIdentifier,
]); ]);
// This sets the filter data using the saved reports returned from the initial instance_list query. // This sets the filter data using the saved reports returned from the initial instance_list query.

View File

@ -1,5 +1,32 @@
import MyCompletedInstances from '../components/MyCompletedInstances'; import ProcessInstanceListTable from '../components/ProcessInstanceListTable';
export default function CompletedInstances() { export default function CompletedInstances() {
return <MyCompletedInstances />; return (
<>
<h1>Initiated By Me</h1>
<ProcessInstanceListTable
filtersEnabled={false}
paginationQueryParamPrefix="my_completed_instances"
perPageOptions={[2, 5, 25]}
reportIdentifier="system_report_instances_initiated_by_me"
showReports={false}
/>
<h1 style={{ marginTop: '1em' }}>With Tasks Completed By Me</h1>
<ProcessInstanceListTable
filtersEnabled={false}
paginationQueryParamPrefix="my_completed_tasks"
perPageOptions={[2, 5, 25]}
reportIdentifier="system_report_instances_with_tasks_completed_by_me"
showReports={false}
/>
<h1 style={{ marginTop: '1em' }}>With Tasks Completed By My Group</h1>
<ProcessInstanceListTable
filtersEnabled={false}
paginationQueryParamPrefix="group_completed_tasks"
perPageOptions={[2, 5, 25]}
reportIdentifier="system_report_instances_with_tasks_completed_by_my_groups"
showReports={false}
/>
</>
);
} }