actually filter by process initiator w/ burnettk

This commit is contained in:
jasquat 2023-01-06 15:50:47 -05:00
parent 9c028ac6a1
commit 9049a64925
6 changed files with 205 additions and 30 deletions

View File

@ -628,6 +628,12 @@ paths:
description: The identifier of the group to get the process instances for
schema:
type: string
- name: process_initiator_username
in: query
required: false
description: The username of the process initiator
schema:
type: string
get:
operationId: spiffworkflow_backend.routes.process_instances_controller.process_instance_list_for_me
summary: Returns a list of process instances that are associated with me.
@ -741,6 +747,12 @@ paths:
description: The identifier of the group to get the process instances for
schema:
type: string
- name: process_initiator_username
in: query
required: false
description: The username of the process initiator
schema:
type: string
get:
operationId: spiffworkflow_backend.routes.process_instances_controller.process_instance_list
summary: Returns a list of process instances.

View File

@ -219,6 +219,7 @@ def process_instance_list_for_me(
report_identifier: Optional[str] = None,
report_id: Optional[int] = None,
user_group_identifier: Optional[str] = None,
process_initiator_username: Optional[str] = None,
) -> flask.wrappers.Response:
"""Process_instance_list_for_me."""
return process_instance_list(
@ -252,6 +253,7 @@ def process_instance_list(
report_identifier: Optional[str] = None,
report_id: Optional[int] = None,
user_group_identifier: Optional[str] = None,
process_initiator_username: Optional[str] = None,
) -> flask.wrappers.Response:
"""Process_instance_list."""
process_instance_report = ProcessInstanceReportService.report_with_identifier(
@ -268,6 +270,7 @@ def process_instance_list(
end_to=end_to,
with_relation_to_me=with_relation_to_me,
process_status=process_status.split(",") if process_status else None,
process_initiator_username=process_initiator_username,
)
else:
report_filter = (
@ -281,6 +284,7 @@ def process_instance_list(
end_to=end_to,
process_status=process_status,
with_relation_to_me=with_relation_to_me,
process_initiator_username=process_initiator_username,
)
)

View File

@ -28,6 +28,10 @@ from spiffworkflow_backend.models.user_group_assignment import UserGroupAssignme
from spiffworkflow_backend.services.process_model_service import ProcessModelService
class ProcessInstanceReportNotFoundError(Exception):
"""ProcessInstanceReportNotFoundError."""
@dataclass
class ProcessInstanceReportFilter:
"""ProcessInstanceReportFilter."""
@ -44,6 +48,7 @@ class ProcessInstanceReportFilter:
with_tasks_completed_by_me: Optional[bool] = None
with_tasks_assigned_to_my_group: Optional[bool] = None
with_relation_to_me: Optional[bool] = None
process_initiator_username: Optional[str] = (None,)
def to_dict(self) -> dict[str, str]:
"""To_dict."""
@ -77,6 +82,8 @@ class ProcessInstanceReportFilter:
).lower()
if self.with_relation_to_me is not None:
d["with_relation_to_me"] = str(self.with_relation_to_me).lower()
if self.process_initiator_username is not None:
d["process_initiator_username"] = str(self.process_initiator_username)
return d
@ -85,7 +92,7 @@ class ProcessInstanceReportService:
"""ProcessInstanceReportService."""
@classmethod
def system_metadata_map(cls, metadata_key: str) -> dict[str, Any]:
def system_metadata_map(cls, metadata_key: str) -> Optional[dict[str, Any]]:
"""System_metadata_map."""
# TODO replace with system reports that are loaded on launch (or similar)
temp_system_metadata_map = {
@ -131,6 +138,9 @@ class ProcessInstanceReportService:
"order_by": ["-start_in_seconds", "-id"],
},
}
if metadata_key not in temp_system_metadata_map:
return None
return temp_system_metadata_map[metadata_key]
@classmethod
@ -157,10 +167,17 @@ class ProcessInstanceReportService:
if process_instance_report is not None:
return process_instance_report # type: ignore
report_metadata = cls.system_metadata_map(report_identifier)
if report_metadata is None:
raise ProcessInstanceReportNotFoundError(
f"Could not find a report with identifier '{report_identifier}' for"
f" user '{user.username}'"
)
process_instance_report = ProcessInstanceReportModel(
identifier=report_identifier,
created_by_id=user.id,
report_metadata=cls.system_metadata_map(report_identifier),
report_metadata=report_metadata,
)
return process_instance_report # type: ignore
@ -210,20 +227,22 @@ class ProcessInstanceReportService:
with_tasks_completed_by_me = bool_value("with_tasks_completed_by_me")
with_tasks_assigned_to_my_group = bool_value("with_tasks_assigned_to_my_group")
with_relation_to_me = bool_value("with_relation_to_me")
process_initiator_username = filters.get("process_initiator_username")
report_filter = ProcessInstanceReportFilter(
process_model_identifier,
user_group_identifier,
start_from,
start_to,
end_from,
end_to,
process_status,
initiated_by_me,
has_terminal_status,
with_tasks_completed_by_me,
with_tasks_assigned_to_my_group,
with_relation_to_me,
process_model_identifier=process_model_identifier,
user_group_identifier=user_group_identifier,
start_from=start_from,
start_to=start_to,
end_from=end_from,
end_to=end_to,
process_status=process_status,
initiated_by_me=initiated_by_me,
has_terminal_status=has_terminal_status,
with_tasks_completed_by_me=with_tasks_completed_by_me,
with_tasks_assigned_to_my_group=with_tasks_assigned_to_my_group,
with_relation_to_me=with_relation_to_me,
process_initiator_username=process_initiator_username,
)
return report_filter
@ -244,6 +263,7 @@ class ProcessInstanceReportService:
with_tasks_completed_by_me: Optional[bool] = None,
with_tasks_assigned_to_my_group: Optional[bool] = None,
with_relation_to_me: Optional[bool] = None,
process_initiator_username: Optional[str] = None,
) -> ProcessInstanceReportFilter:
"""Filter_from_metadata_with_overrides."""
report_filter = cls.filter_from_metadata(process_instance_report)
@ -268,6 +288,8 @@ class ProcessInstanceReportService:
report_filter.has_terminal_status = has_terminal_status
if with_tasks_completed_by_me is not None:
report_filter.with_tasks_completed_by_me = with_tasks_completed_by_me
if process_initiator_username is not None:
report_filter.process_initiator_username = process_initiator_username
if with_tasks_assigned_to_my_group is not None:
report_filter.with_tasks_assigned_to_my_group = (
with_tasks_assigned_to_my_group
@ -386,6 +408,17 @@ class ProcessInstanceReportService:
ProcessInstanceModel.status.in_(ProcessInstanceModel.terminal_statuses()) # type: ignore
)
if report_filter.process_initiator_username is not None:
user = UserModel.query.filter_by(
username=report_filter.process_initiator_username
).first()
process_initiator_id = -1
if user:
process_initiator_id = user.id
process_instance_query = process_instance_query.filter_by(
process_initiator_id=process_initiator_id
)
if (
not report_filter.with_tasks_completed_by_me
and not report_filter.with_tasks_assigned_to_my_group

View File

@ -3046,6 +3046,95 @@ class TestProcessApi(BaseTest):
assert response.json["pagination"]["pages"] == 1
assert response.json["pagination"]["total"] == 1
def test_can_get_process_instance_list_with_report_metadata_and_process_initator(
self,
app: Flask,
client: FlaskClient,
with_db_and_bpmn_file_cleanup: None,
with_super_admin_user: UserModel,
) -> None:
"""Test_can_get_process_instance_list_with_report_metadata_and_process_initator."""
user_one = self.create_user_with_permission(username="user_one")
process_model = load_test_spec(
process_model_id=(
"save_process_instance_metadata/save_process_instance_metadata"
),
bpmn_file_name="save_process_instance_metadata.bpmn",
process_model_source_directory="save_process_instance_metadata",
)
self.create_process_instance_from_process_model(
process_model=process_model, user=user_one
)
self.create_process_instance_from_process_model(
process_model=process_model, user=user_one
)
self.create_process_instance_from_process_model(
process_model=process_model, user=with_super_admin_user
)
dne_report_metadata = {
"columns": [
{"Header": "ID", "accessor": "id"},
{"Header": "Status", "accessor": "status"},
{"Header": "Process Initiator", "accessor": "username"},
],
"order_by": ["status"],
"filter_by": [
{
"field_name": "process_initiator_username",
"field_value": "DNE",
"operator": "equals",
}
],
}
user_one_report_metadata = {
"columns": [
{"Header": "ID", "accessor": "id"},
{"Header": "Status", "accessor": "status"},
{"Header": "Process Initiator", "accessor": "username"},
],
"order_by": ["status"],
"filter_by": [
{
"field_name": "process_initiator_username",
"field_value": user_one.username,
"operator": "equals",
}
],
}
process_instance_report_dne = ProcessInstanceReportModel.create_with_attributes(
identifier="dne_report",
report_metadata=dne_report_metadata,
user=user_one,
)
process_instance_report_user_one = (
ProcessInstanceReportModel.create_with_attributes(
identifier="user_one_report",
report_metadata=user_one_report_metadata,
user=user_one,
)
)
response = client.get(
f"/v1.0/process-instances?report_identifier={process_instance_report_user_one.identifier}",
headers=self.logged_in_headers(user_one),
)
assert response.json is not None
assert response.status_code == 200
assert len(response.json["results"]) == 2
assert response.json["results"][0]["username"] == user_one.username
assert response.json["results"][1]["username"] == user_one.username
response = client.get(
f"/v1.0/process-instances?report_identifier={process_instance_report_dne.identifier}",
headers=self.logged_in_headers(user_one),
)
assert response.json is not None
assert response.status_code == 200
assert len(response.json["results"]) == 0
def test_can_get_process_instance_report_column_list(
self,
app: Flask,

View File

@ -12,6 +12,7 @@ import {
ProcessModel,
ReportColumn,
ReportMetadata,
User,
} from '../interfaces';
import HttpService from '../services/HttpService';
@ -20,6 +21,7 @@ type OwnProps = {
columnArray: ReportColumn[];
orderBy: string;
processModelSelection: ProcessModel | null;
processInitiatorSelection: User | null;
processStatusSelection: string[];
startFromSeconds: string | null;
startToSeconds: string | null;
@ -36,6 +38,7 @@ export default function ProcessInstanceListSaveAsReport({
columnArray,
orderBy,
processModelSelection,
processInitiatorSelection,
processInstanceReportSelection,
processStatusSelection,
startFromSeconds,
@ -86,6 +89,13 @@ export default function ProcessInstanceListSaveAsReport({
});
}
if (processInitiatorSelection) {
filterByArray.push({
field_name: 'process_initiator_username',
field_value: processInitiatorSelection.username,
});
}
if (processStatusSelection.length > 0) {
filterByArray.push({
field_name: 'process_status',

View File

@ -193,11 +193,42 @@ export default function ProcessInstanceListTable({
setEndToTime,
]);
const handleProcessInstanceInitiatorSearchResult = (
result: any,
inputText: string
) => {
if (lastRequestedInitatorSearchTerm.current === result.username_prefix) {
setProcessInstanceInitiatorOptions(result.users);
result.users.forEach((user: User) => {
if (user.username === inputText) {
setProcessInitiatorSelection(user);
}
});
}
};
const searchForProcessInitiator = (inputText: string) => {
if (inputText) {
lastRequestedInitatorSearchTerm.current = inputText;
HttpService.makeCallToBackend({
path: `/users/search?username_prefix=${inputText}`,
successCallback: (result: any) =>
handleProcessInstanceInitiatorSearchResult(result, inputText),
});
}
};
const parametersToGetFromSearchParams = useMemo(() => {
const figureOutProcessInitiator = (processInitiatorSearchText: string) => {
searchForProcessInitiator(processInitiatorSearchText);
};
return {
process_model_identifier: null,
process_status: null,
process_initiator_username: figureOutProcessInitiator,
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
// eslint-disable-next-line sonarjs/cognitive-complexity
@ -384,6 +415,12 @@ export default function ProcessInstanceListTable({
}
});
if (filters.process_initiator_username) {
const functionToCall =
parametersToGetFromSearchParams.process_initiator_username;
functionToCall(filters.process_initiator_username);
}
const processStatusSelectedArray: string[] = [];
if (filters.process_status) {
PROCESS_STATUSES.forEach((processStatusOption: any) => {
@ -538,8 +575,13 @@ export default function ProcessInstanceListTable({
queryParamString += `&report_id=${processInstanceReportSelection.id}`;
}
if (processInitiatorSelection) {
queryParamString += `&process_initiator_username=${processInitiatorSelection.username}`;
}
setErrorObject(null);
setProcessInstanceReportJustSaved(null);
setProcessInstanceFilters({});
navigate(`${processInstanceListPathPrefix}?${queryParamString}`);
};
@ -682,6 +724,7 @@ export default function ProcessInstanceListTable({
orderBy=""
buttonText="Save"
processModelSelection={processModelSelection}
processInitiatorSelection={processInitiatorSelection}
processStatusSelection={processStatusSelection}
processInstanceReportSelection={processInstanceReportSelection}
reportMetadata={reportMetadata}
@ -987,22 +1030,6 @@ export default function ProcessInstanceListTable({
return null;
};
const handleProcessInstanceInitiatorSearchResult = (result: any) => {
if (lastRequestedInitatorSearchTerm.current === result.username_prefix) {
setProcessInstanceInitiatorOptions(result.users);
}
};
const searchForProcessInitiator = (inputText: string) => {
if (inputText) {
lastRequestedInitatorSearchTerm.current = inputText;
HttpService.makeCallToBackend({
path: `/users/search?username_prefix=${inputText}`,
successCallback: handleProcessInstanceInitiatorSearchResult,
});
}
};
const filterOptions = () => {
if (!showFilterOptions) {
return null;