filtering by metadata works w/ burnettk

This commit is contained in:
jasquat 2022-12-02 13:47:04 -05:00
parent a4edb5d766
commit ae2bc38588
8 changed files with 172 additions and 52 deletions

View File

@ -785,7 +785,6 @@ def process_instance_list(
report_id: Optional[int] = None, report_id: Optional[int] = None,
) -> flask.wrappers.Response: ) -> flask.wrappers.Response:
"""Process_instance_list.""" """Process_instance_list."""
process_instance_report = ProcessInstanceReportService.report_with_identifier( process_instance_report = ProcessInstanceReportService.report_with_identifier(
g.user, report_id, report_identifier g.user, report_id, report_identifier
) )
@ -942,12 +941,18 @@ def process_instance_list(
if column["accessor"] in stock_columns: if column["accessor"] in stock_columns:
continue continue
instance_metadata_alias = aliased(ProcessInstanceMetadataModel) instance_metadata_alias = aliased(ProcessInstanceMetadataModel)
process_instance_query = process_instance_query.outerjoin(
filter_for_column = next((f for f in process_instance_report.report_metadata['filter_by'] if f['field_name'] == column['accessor']), None)
isouter = True
conditions = [ProcessInstanceModel.id == instance_metadata_alias.process_instance_id,
instance_metadata_alias.key == column["accessor"]]
if filter_for_column:
isouter = False
conditions.append(instance_metadata_alias.value == filter_for_column["field_value"])
process_instance_query = process_instance_query.join(
instance_metadata_alias, instance_metadata_alias,
and_( and_(*conditions),
ProcessInstanceModel.id == instance_metadata_alias.process_instance_id, isouter=isouter
instance_metadata_alias.key == column["accessor"],
),
).add_columns(func.max(instance_metadata_alias.value).label(column["accessor"])) ).add_columns(func.max(instance_metadata_alias.value).label(column["accessor"]))
process_instances = ( process_instances = (

View File

@ -29,8 +29,6 @@ class ProcessInstanceReportFilter:
"""To_dict.""" """To_dict."""
d = {} d = {}
print(f"dir(self): {dir(self)}")
if self.process_model_identifier is not None: if self.process_model_identifier is not None:
d["process_model_identifier"] = self.process_model_identifier d["process_model_identifier"] = self.process_model_identifier
if self.start_from is not None: if self.start_from is not None:
@ -86,7 +84,7 @@ class ProcessInstanceReportService:
# TODO replace with system reports that are loaded on launch (or similar) # TODO replace with system reports that are loaded on launch (or similar)
temp_system_metadata_map = { temp_system_metadata_map = {
"default": {"columns": cls.builtin_column_options()}, "default": {"columns": cls.builtin_column_options(), "filter_by": [], 'order_by': ['-start_in_seconds', '-id']},
"system_report_instances_initiated_by_me": { "system_report_instances_initiated_by_me": {
"columns": [ "columns": [
{"Header": "id", "accessor": "id"}, {"Header": "id", "accessor": "id"},
@ -98,13 +96,13 @@ class ProcessInstanceReportService:
{"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}], "filter_by": [{"field_name": "initiated_by_me", "field_value": True}],'order_by': ['-start_in_seconds', '-id']
}, },
"system_report_instances_with_tasks_completed_by_me": { "system_report_instances_with_tasks_completed_by_me": {
"columns": cls.builtin_column_options(), "columns": cls.builtin_column_options(),
"filter_by": [ "filter_by": [
{"field_name": "with_tasks_completed_by_me", "field_value": True} {"field_name": "with_tasks_completed_by_me", "field_value": True}
], ],'order_by': ['-start_in_seconds', '-id']
}, },
"system_report_instances_with_tasks_completed_by_my_groups": { "system_report_instances_with_tasks_completed_by_my_groups": {
"columns": cls.builtin_column_options(), "columns": cls.builtin_column_options(),
@ -113,16 +111,15 @@ class ProcessInstanceReportService:
"field_name": "with_tasks_completed_by_my_group", "field_name": "with_tasks_completed_by_my_group",
"field_value": True, "field_value": True,
} }
], ],'order_by': ['-start_in_seconds', '-id']
}, },
} }
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], # type: ignore report_metadata=temp_system_metadata_map[report_identifier],
) )
# db.session.add(pro
return process_instance_report # type: ignore return process_instance_report # type: ignore

View File

@ -6,12 +6,18 @@ import {
Stack, Stack,
// @ts-ignore // @ts-ignore
} from '@carbon/react'; } from '@carbon/react';
import { ProcessInstanceReport, ProcessModel } from '../interfaces'; import {
ReportFilter,
ProcessInstanceReport,
ProcessModel,
ReportColumn,
ReportMetadata,
} from '../interfaces';
import HttpService from '../services/HttpService'; import HttpService from '../services/HttpService';
type OwnProps = { type OwnProps = {
onSuccess: (..._args: any[]) => any; onSuccess: (..._args: any[]) => any;
columnArray: { Header: string; accessor: string }; columnArray: ReportColumn[];
orderBy: string; orderBy: string;
processModelSelection: ProcessModel | null; processModelSelection: ProcessModel | null;
processStatusSelection: string[]; processStatusSelection: string[];
@ -22,6 +28,7 @@ type OwnProps = {
buttonText?: string; buttonText?: string;
buttonClassName?: string; buttonClassName?: string;
processInstanceReportSelection?: ProcessInstanceReport | null; processInstanceReportSelection?: ProcessInstanceReport | null;
reportMetadata: ReportMetadata;
}; };
export default function ProcessInstanceListSaveAsReport({ export default function ProcessInstanceListSaveAsReport({
@ -37,6 +44,7 @@ export default function ProcessInstanceListSaveAsReport({
endToSeconds, endToSeconds,
buttonText = 'Save as Perspective', buttonText = 'Save as Perspective',
buttonClassName, buttonClassName,
reportMetadata,
}: OwnProps) { }: OwnProps) {
const [identifier, setIdentifier] = useState<string>( const [identifier, setIdentifier] = useState<string>(
processInstanceReportSelection?.identifier || '' processInstanceReportSelection?.identifier || ''
@ -59,7 +67,11 @@ export default function ProcessInstanceListSaveAsReport({
const addProcessInstanceReport = (event: any) => { const addProcessInstanceReport = (event: any) => {
event.preventDefault(); event.preventDefault();
const orderByArray = orderBy.split(',').filter((n) => n); // TODO: make a field to set this
let orderByArray = ['-start_in_seconds', '-id'];
if (orderBy) {
orderByArray = orderBy.split(',').filter((n) => n);
}
const filterByArray: any = []; const filterByArray: any = [];
if (processModelSelection) { if (processModelSelection) {
@ -72,7 +84,8 @@ export default function ProcessInstanceListSaveAsReport({
if (processStatusSelection.length > 0) { if (processStatusSelection.length > 0) {
filterByArray.push({ filterByArray.push({
field_name: 'process_status', field_name: 'process_status',
field_value: processStatusSelection[0], // TODO: support more than one status field_value: processStatusSelection.join(','),
operator: 'in',
}); });
} }
@ -104,6 +117,17 @@ export default function ProcessInstanceListSaveAsReport({
}); });
} }
reportMetadata.filter_by.forEach((reportFilter: ReportFilter) => {
columnArray.forEach((reportColumn: ReportColumn) => {
if (
reportColumn.accessor === reportFilter.field_name &&
reportColumn.filterable
) {
filterByArray.push(reportFilter);
}
});
});
let path = `/process-instances/reports`; let path = `/process-instances/reports`;
let httpMethod = 'POST'; let httpMethod = 'POST';
if (isEditMode() && processInstanceReportSelection) { if (isEditMode() && processInstanceReportSelection) {

View File

@ -57,6 +57,9 @@ import {
ProcessInstanceReport, ProcessInstanceReport,
ProcessInstance, ProcessInstance,
ReportColumn, ReportColumn,
ReportColumnForEditing,
ReportMetadata,
ReportFilter,
} from '../interfaces'; } from '../interfaces';
import ProcessModelSearch from './ProcessModelSearch'; import ProcessModelSearch from './ProcessModelSearch';
import ProcessInstanceReportSearch from './ProcessInstanceReportSearch'; import ProcessInstanceReportSearch from './ProcessInstanceReportSearch';
@ -98,7 +101,7 @@ export default function ProcessInstanceListTable({
const navigate = useNavigate(); const navigate = useNavigate();
const [processInstances, setProcessInstances] = useState([]); const [processInstances, setProcessInstances] = useState([]);
const [reportMetadata, setReportMetadata] = useState({}); const [reportMetadata, setReportMetadata] = useState<ReportMetadata | null>();
const [pagination, setPagination] = useState<PaginationObject | null>(null); const [pagination, setPagination] = useState<PaginationObject | null>(null);
const [processInstanceFilters, setProcessInstanceFilters] = useState({}); const [processInstanceFilters, setProcessInstanceFilters] = useState({});
@ -143,7 +146,7 @@ export default function ProcessInstanceListTable({
const [showReportColumnForm, setShowReportColumnForm] = const [showReportColumnForm, setShowReportColumnForm] =
useState<boolean>(false); useState<boolean>(false);
const [reportColumnToOperateOn, setReportColumnToOperateOn] = const [reportColumnToOperateOn, setReportColumnToOperateOn] =
useState<ReportColumn | null>(null); useState<ReportColumnForEditing | null>(null);
const [reportColumnFormMode, setReportColumnFormMode] = useState<string>(''); const [reportColumnFormMode, setReportColumnFormMode] = useState<string>('');
const dateParametersToAlwaysFilterBy: dateParameters = useMemo(() => { const dateParametersToAlwaysFilterBy: dateParameters = useMemo(() => {
@ -630,17 +633,19 @@ export default function ProcessInstanceListTable({
endToSeconds, endToSeconds,
} = calculateStartAndEndSeconds(); } = calculateStartAndEndSeconds();
if (!valid) { if (!valid || !reportMetadata) {
return null; return null;
} }
return ( return (
<ProcessInstanceListSaveAsReport <ProcessInstanceListSaveAsReport
onSuccess={onSaveReportSuccess} onSuccess={onSaveReportSuccess}
buttonClassName="narrow-button"
columnArray={reportColumns()} columnArray={reportColumns()}
orderBy="" orderBy=""
buttonText="Save As New Perspective" buttonText="Save New Perspective"
processModelSelection={processModelSelection} processModelSelection={processModelSelection}
processStatusSelection={processStatusSelection} processStatusSelection={processStatusSelection}
reportMetadata={reportMetadata}
startFromSeconds={startFromSeconds} startFromSeconds={startFromSeconds}
startToSeconds={startToSeconds} startToSeconds={startToSeconds}
endFromSeconds={endFromSeconds} endFromSeconds={endFromSeconds}
@ -650,12 +655,14 @@ export default function ProcessInstanceListTable({
}; };
const removeColumn = (reportColumn: ReportColumn) => { const removeColumn = (reportColumn: ReportColumn) => {
const reportMetadataCopy = { ...reportMetadata }; if (reportMetadata) {
const newColumns = reportColumns().filter( const reportMetadataCopy = { ...reportMetadata };
(rc: ReportColumn) => rc.accessor !== reportColumn.accessor const newColumns = reportColumns().filter(
); (rc: ReportColumn) => rc.accessor !== reportColumn.accessor
Object.assign(reportMetadataCopy, { columns: newColumns }); );
setReportMetadata(reportMetadataCopy); Object.assign(reportMetadataCopy, { columns: newColumns });
setReportMetadata(reportMetadataCopy);
}
}; };
const handleColumnFormClose = () => { const handleColumnFormClose = () => {
@ -664,8 +671,49 @@ export default function ProcessInstanceListTable({
setReportColumnToOperateOn(null); setReportColumnToOperateOn(null);
}; };
const handleUpdateColumn = () => { const getFilterByFromReportMetadata = (reportColumnAccessor: string) => {
if (reportColumnToOperateOn) { if (reportMetadata) {
return reportMetadata.filter_by.find((reportFilter: ReportFilter) => {
return reportColumnAccessor === reportFilter.field_name;
});
}
return null;
};
const getNewFiltersFromReportForEditing = (
reportColumnForEditing: ReportColumnForEditing
) => {
if (!reportMetadata) {
return null;
}
const reportMetadataCopy = { ...reportMetadata };
let newReportFilters = reportMetadataCopy.filter_by;
if (reportColumnForEditing.filterable) {
const newReportFilter: ReportFilter = {
field_name: reportColumnForEditing.accessor,
field_value: reportColumnForEditing.filter_field_value,
operator: reportColumnForEditing.filter_operator || 'equals',
};
const existingReportFilter = getFilterByFromReportMetadata(
reportColumnForEditing.accessor
);
if (existingReportFilter) {
const existingReportFilterIndex =
reportMetadataCopy.filter_by.indexOf(existingReportFilter);
if (reportColumnForEditing.filter_field_value) {
newReportFilters[existingReportFilterIndex] = newReportFilter;
} else {
newReportFilters.splice(existingReportFilterIndex, 1);
}
} else {
newReportFilters = newReportFilters.concat([newReportFilter]);
}
}
return newReportFilters;
};
const handleUpdateReportColumn = () => {
if (reportColumnToOperateOn && reportMetadata) {
const reportMetadataCopy = { ...reportMetadata }; const reportMetadataCopy = { ...reportMetadata };
let newReportColumns = null; let newReportColumns = null;
if (reportColumnFormMode === 'new') { if (reportColumnFormMode === 'new') {
@ -680,6 +728,7 @@ export default function ProcessInstanceListTable({
} }
Object.assign(reportMetadataCopy, { Object.assign(reportMetadataCopy, {
columns: newReportColumns, columns: newReportColumns,
filter_by: getNewFiltersFromReportForEditing(reportColumnToOperateOn),
}); });
setReportMetadata(reportMetadataCopy); setReportMetadata(reportMetadataCopy);
setReportColumnToOperateOn(null); setReportColumnToOperateOn(null);
@ -688,8 +737,27 @@ export default function ProcessInstanceListTable({
} }
}; };
const reportColumnToReportColumnForEditing = (reportColumn: ReportColumn) => {
const reportColumnForEditing: ReportColumnForEditing = Object.assign(
reportColumn,
{ filter_field_value: '', filter_operator: '' }
);
const reportFilter = getFilterByFromReportMetadata(
reportColumnForEditing.accessor
);
if (reportFilter) {
reportColumnForEditing.filter_field_value = reportFilter.field_value;
reportColumnForEditing.filter_operator =
reportFilter.operator || 'equals';
}
return reportColumnForEditing;
};
const updateReportColumn = (event: any) => { const updateReportColumn = (event: any) => {
setReportColumnToOperateOn(event.selectedItem); const reportColumnForEditing = reportColumnToReportColumnForEditing(
event.selectedItem
);
setReportColumnToOperateOn(reportColumnForEditing);
}; };
// options includes item and inputValue // options includes item and inputValue
@ -709,7 +777,7 @@ export default function ProcessInstanceListTable({
const reportColumnToOperateOnCopy = { const reportColumnToOperateOnCopy = {
...reportColumnToOperateOn, ...reportColumnToOperateOn,
}; };
reportColumnToOperateOnCopy.condition_value = event.target.value; reportColumnToOperateOnCopy.filter_field_value = event.target.value;
setReportColumnToOperateOn(reportColumnToOperateOnCopy); setReportColumnToOperateOn(reportColumnToOperateOnCopy);
} }
}; };
@ -737,7 +805,6 @@ export default function ProcessInstanceListTable({
/>, />,
]; ];
if (reportColumnToOperateOn && reportColumnToOperateOn.filterable) { if (reportColumnToOperateOn && reportColumnToOperateOn.filterable) {
console.log('reportColumnToOperateOn', reportColumnToOperateOn);
formElements.push( formElements.push(
<TextInput <TextInput
id="report-column-condition-value" id="report-column-condition-value"
@ -745,7 +812,7 @@ export default function ProcessInstanceListTable({
labelText="Condition Value" labelText="Condition Value"
value={ value={
reportColumnToOperateOn reportColumnToOperateOn
? reportColumnToOperateOn.condition_value ? reportColumnToOperateOn.filter_field_value
: '' : ''
} }
onChange={setReportColumnConditionValue} onChange={setReportColumnConditionValue}
@ -785,7 +852,7 @@ export default function ProcessInstanceListTable({
modalHeading={modalHeading} modalHeading={modalHeading}
primaryButtonText="Save" primaryButtonText="Save"
primaryButtonDisabled={!reportColumnToOperateOn} primaryButtonDisabled={!reportColumnToOperateOn}
onRequestSubmit={handleUpdateColumn} onRequestSubmit={handleUpdateReportColumn}
onRequestClose={handleColumnFormClose} onRequestClose={handleColumnFormClose}
hasScrollingContent hasScrollingContent
> >
@ -799,13 +866,16 @@ export default function ProcessInstanceListTable({
const tags: any = []; const tags: any = [];
(reportColumns() as any).forEach((reportColumn: ReportColumn) => { (reportColumns() as any).forEach((reportColumn: ReportColumn) => {
const reportColumnForEditing =
reportColumnToReportColumnForEditing(reportColumn);
let tagType = 'cool-gray'; let tagType = 'cool-gray';
if (reportColumn.filterable) { if (reportColumnForEditing.filterable) {
tagType = 'green'; tagType = 'green';
} }
let reportColumnLabel = reportColumn.Header; let reportColumnLabel = reportColumnForEditing.Header;
if (reportColumn.condition_value) { if (reportColumnForEditing.filter_field_value) {
reportColumnLabel = `${reportColumnLabel}=${reportColumn.condition_value}`; reportColumnLabel = `${reportColumnLabel}=${reportColumnForEditing.filter_field_value}`;
} }
tags.push( tags.push(
<Tag type={tagType} size="sm"> <Tag type={tagType} size="sm">
@ -813,9 +883,9 @@ export default function ProcessInstanceListTable({
kind="ghost" kind="ghost"
size="sm" size="sm"
className="button-tag-icon" className="button-tag-icon"
title={`Edit ${reportColumn.accessor}`} title={`Edit ${reportColumnForEditing.accessor}`}
onClick={() => { onClick={() => {
setReportColumnToOperateOn(reportColumn); setReportColumnToOperateOn(reportColumnForEditing);
setShowReportColumnForm(true); setShowReportColumnForm(true);
setReportColumnFormMode('edit'); setReportColumnFormMode('edit');
}} }}
@ -830,7 +900,7 @@ export default function ProcessInstanceListTable({
hasIconOnly hasIconOnly
size="sm" size="sm"
kind="ghost" kind="ghost"
onClick={() => removeColumn(reportColumn)} onClick={() => removeColumn(reportColumnForEditing)}
/> />
</Tag> </Tag>
); );
@ -933,11 +1003,14 @@ export default function ProcessInstanceListTable({
</Column> </Column>
</Grid> </Grid>
<Grid fullWidth className="with-bottom-margin"> <Grid fullWidth className="with-bottom-margin">
<Column md={4}> <Column sm={4} md={4} lg={8}>
{saveAsReportComponent()}
</Column>
<Column sm={4} md={4} lg={8}>
<ButtonSet> <ButtonSet>
<Button <Button
kind="" kind=""
className="button-white-background" className="button-white-background narrow-button"
onClick={clearFilters} onClick={clearFilters}
> >
Clear Clear
@ -946,17 +1019,13 @@ export default function ProcessInstanceListTable({
kind="secondary" kind="secondary"
onClick={applyFilter} onClick={applyFilter}
data-qa="filter-button" data-qa="filter-button"
className="narrow-button"
> >
Filter Filter
</Button> </Button>
</ButtonSet> </ButtonSet>
</Column> </Column>
</Grid> </Grid>
<Grid fullWidth className="with-bottom-margin">
<Column md={7} lg={13} sm={3}>
{saveAsReportComponent()}
</Column>
</Grid>
</> </>
); );
}; };
@ -1079,7 +1148,11 @@ export default function ProcessInstanceListTable({
/> />
</Column>, </Column>,
]; ];
if (processInstanceReportSelection && showFilterOptions) { if (
processInstanceReportSelection &&
showFilterOptions &&
reportMetadata
) {
columns.push( columns.push(
<Column sm={2} md={4} lg={2}> <Column sm={2} md={4} lg={2}>
<ProcessInstanceListSaveAsReport <ProcessInstanceListSaveAsReport
@ -1090,6 +1163,7 @@ export default function ProcessInstanceListTable({
buttonText="Save" buttonText="Save"
processModelSelection={processModelSelection} processModelSelection={processModelSelection}
processStatusSelection={processStatusSelection} processStatusSelection={processStatusSelection}
reportMetadata={reportMetadata}
startFromSeconds={startFromSeconds} startFromSeconds={startFromSeconds}
startToSeconds={startToSeconds} startToSeconds={startToSeconds}
endFromSeconds={endFromSeconds} endFromSeconds={endFromSeconds}

View File

@ -41,7 +41,7 @@ export default function ProcessInstanceReportSearch({
const reportSelectionString = ( const reportSelectionString = (
processInstanceReport: ProcessInstanceReport processInstanceReport: ProcessInstanceReport
) => { ) => {
return `${truncateString(processInstanceReport.identifier, 20)} (${ return `${truncateString(processInstanceReport.identifier, 20)} (Id: ${
processInstanceReport.id processInstanceReport.id
})`; })`;
}; };

View File

@ -14,6 +14,7 @@ export const PROCESS_STATUSES = [
'complete', 'complete',
'error', 'error',
'suspended', 'suspended',
'terminated',
]; ];
// with time: yyyy-MM-dd HH:mm:ss // with time: yyyy-MM-dd HH:mm:ss

View File

@ -346,3 +346,10 @@ td.actions-cell {
.combo-box-in-modal { .combo-box-in-modal {
height: 300px; height: 300px;
} }
.cds--btn.narrow-button {
max-width: 10rem;
min-width: 5rem;
word-break: normal;
}

View File

@ -63,15 +63,27 @@ export interface MessageInstance {
message_correlations?: MessageCorrelations; message_correlations?: MessageCorrelations;
} }
export interface ReportFilter {
field_name: string;
field_value: string;
operator?: string;
}
export interface ReportColumn { export interface ReportColumn {
Header: string; Header: string;
accessor: string; accessor: string;
filterable: boolean; filterable: boolean;
condition_value?: string; }
export interface ReportColumnForEditing extends ReportColumn {
filter_field_value: string;
filter_operator: string;
} }
export interface ReportMetadata { export interface ReportMetadata {
columns: ReportColumn[]; columns: ReportColumn[];
filter_by: ReportFilter[];
order_by: string[];
} }
export interface ProcessInstanceReport { export interface ProcessInstanceReport {