reports seem to be working again w/ burnettk

This commit is contained in:
jasquat 2023-04-27 15:19:02 -04:00
parent e49734b9ef
commit b86ddf8a96
13 changed files with 401 additions and 399 deletions

View File

@ -0,0 +1,34 @@
"""empty message
Revision ID: 68adb1d504e1
Revises: 0c7428378d6e
Create Date: 2023-04-27 12:24:01.771698
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '68adb1d504e1'
down_revision = '0c7428378d6e'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('process_instance_report', schema=None) as batch_op:
batch_op.add_column(sa.Column('json_data_hash', sa.String(length=255), nullable=False))
batch_op.create_index(batch_op.f('ix_process_instance_report_json_data_hash'), ['json_data_hash'], unique=False)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('process_instance_report', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_process_instance_report_json_data_hash'))
batch_op.drop_column('json_data_hash')
# ### end Alembic commands ###

View File

@ -1298,17 +1298,29 @@ paths:
items: items:
$ref: "#/components/schemas/Workflow" $ref: "#/components/schemas/Workflow"
/process-instances/report-metadata/{report_hash}: /process-instances/report-metadata:
parameters: parameters:
- name: report_hash - name: report_hash
in: path in: query
required: true required: false
description: The unique id of an existing report description: The hash of a query that has been searched before.
schema:
type: string
- name: report_id
in: query
required: false
description: The unique id of an existing report.
schema:
type: integer
- name: report_identifier
in: query
required: false
description: Specifies the identifier of a report to use, if any
schema: schema:
type: string type: string
get: get:
operationId: spiffworkflow_backend.routes.process_instances_controller.process_instance_report_metadata_show operationId: spiffworkflow_backend.routes.process_instances_controller.process_instance_report_show
summary: Returns the metadata associated with a given report hash. summary: Returns the metadata associated with a given report key. This favors report_hash over report_id and report_identifier.
tags: tags:
- Process Instances - Process Instances
responses: responses:
@ -1341,20 +1353,20 @@ paths:
description: The page number to return. Defaults to page 1. description: The page number to return. Defaults to page 1.
schema: schema:
type: integer type: integer
get: # get:
operationId: spiffworkflow_backend.routes.process_instances_controller.process_instance_report_show # operationId: spiffworkflow_backend.routes.process_instances_controller.process_instance_report_show
summary: Returns a report of process instances for a given process model # summary: Returns a report of process instances for a given process model
tags: # tags:
- Process Instances # - Process Instances
responses: # responses:
"200": # "200":
description: Workflow. # description: Workflow.
content: # content:
application/json: # application/json:
schema: # schema:
type: array # type: array
items: # items:
$ref: "#/components/schemas/Workflow" # $ref: "#/components/schemas/Workflow"
put: put:
operationId: spiffworkflow_backend.routes.process_instances_controller.process_instance_report_update operationId: spiffworkflow_backend.routes.process_instances_controller.process_instance_report_update
summary: Updates a process instance report summary: Updates a process instance report

View File

@ -1,4 +1,10 @@
from __future__ import annotations from __future__ import annotations
import json
from hashlib import sha256
from flask import current_app
from typing import TypedDict
from sqlalchemy.dialects.mysql import insert as mysql_insert
from sqlalchemy.dialects.postgresql import insert as postgres_insert
from spiffworkflow_backend.models.db import db from spiffworkflow_backend.models.db import db
from spiffworkflow_backend.models.db import SpiffworkflowBaseDBModel from spiffworkflow_backend.models.db import SpiffworkflowBaseDBModel
@ -8,6 +14,11 @@ class JsonDataModelNotFoundError(Exception):
pass pass
class JsonDataDict(TypedDict):
hash: str
data: dict
# delta algorithm <- just to save it for when we want to try to implement it: # delta algorithm <- just to save it for when we want to try to implement it:
# a = {"hey": { "hey2": 2, "hey3": 3, "hey6": 7 }, "hey30": 3, "hey40": 4} # a = {"hey": { "hey2": 2, "hey3": 3, "hey6": 7 }, "hey30": 3, "hey40": 4}
# b = {"hey": { "hey2": 4, "hey5": 3 }, "hey20": 2, "hey30": 3} # b = {"hey": { "hey2": 4, "hey5": 3 }, "hey20": 2, "hey30": 3}
@ -42,3 +53,29 @@ class JsonDataModel(SpiffworkflowBaseDBModel):
@classmethod @classmethod
def find_data_dict_by_hash(cls, hash: str) -> dict: def find_data_dict_by_hash(cls, hash: str) -> dict:
return cls.find_object_by_hash(hash).data return cls.find_object_by_hash(hash).data
@classmethod
def insert_or_update_json_data_records(
cls, json_data_hash_to_json_data_dict_mapping: dict[str, JsonDataDict]
) -> None:
list_of_dicts = [*json_data_hash_to_json_data_dict_mapping.values()]
if len(list_of_dicts) > 0:
on_duplicate_key_stmt = None
if current_app.config["SPIFFWORKFLOW_BACKEND_DATABASE_TYPE"] == "mysql":
insert_stmt = mysql_insert(JsonDataModel).values(list_of_dicts)
on_duplicate_key_stmt = insert_stmt.on_duplicate_key_update(data=insert_stmt.inserted.data)
else:
insert_stmt = postgres_insert(JsonDataModel).values(list_of_dicts)
on_duplicate_key_stmt = insert_stmt.on_conflict_do_nothing(index_elements=["hash"])
db.session.execute(on_duplicate_key_stmt)
@classmethod
def insert_or_update_json_data_dict(cls, json_data_dict: JsonDataDict) -> None:
cls.insert_or_update_json_data_records({json_data_dict["hash"]: json_data_dict})
@classmethod
def create_and_insert_json_data_from_dict(cls, data: dict) -> str:
json_data_hash = sha256(json.dumps(data, sort_keys=True).encode("utf8")).hexdigest()
cls.insert_or_update_json_data_dict({'hash': json_data_hash, 'data': data})
db.session.commit()
return json_data_hash

View File

@ -1,5 +1,6 @@
"""Process_instance.""" """Process_instance."""
from __future__ import annotations from __future__ import annotations
from spiffworkflow_backend.models.json_data import JsonDataModel # noqa: F401
from dataclasses import dataclass from dataclasses import dataclass
from typing import Any from typing import Any
@ -88,6 +89,11 @@ class ProcessInstanceReportModel(SpiffworkflowBaseDBModel):
created_at_in_seconds = db.Column(db.Integer) created_at_in_seconds = db.Column(db.Integer)
updated_at_in_seconds = db.Column(db.Integer) updated_at_in_seconds = db.Column(db.Integer)
json_data_hash: str = db.Column(db.String(255), nullable=False, index=True)
def get_report_metadata(self) -> dict:
return JsonDataModel.find_data_dict_by_hash(self.json_data_hash)
@classmethod @classmethod
def default_order_by(cls) -> list[str]: def default_order_by(cls) -> list[str]:
"""Default_order_by.""" """Default_order_by."""
@ -154,10 +160,13 @@ class ProcessInstanceReportModel(SpiffworkflowBaseDBModel):
f"Process instance report with identifier already exists: {identifier}" f"Process instance report with identifier already exists: {identifier}"
) )
json_data_hash = JsonDataModel.create_and_insert_json_data_from_dict(report_metadata)
process_instance_report = cls( process_instance_report = cls(
identifier=identifier, identifier=identifier,
created_by_id=user.id, created_by_id=user.id,
report_metadata=report_metadata, report_metadata=report_metadata,
json_data_hash=json_data_hash,
) )
db.session.add(process_instance_report) db.session.add(process_instance_report)
db.session.commit() db.session.commit()

View File

@ -335,25 +335,42 @@ def process_instance_list(
user=g.user, user=g.user,
) )
json_data_hash = sha256(json.dumps(body['report_metadata'], sort_keys=True).encode("utf8")).hexdigest() json_data_hash = JsonDataModel.create_and_insert_json_data_from_dict(body['report_metadata'])
TaskService.insert_or_update_json_data_dict({'hash': json_data_hash, 'data': body['report_metadata']})
db.session.commit()
# json_data = JsonDataModel.query.filter_by(json_data_hash)
response_json['report_hash'] = json_data_hash response_json['report_hash'] = json_data_hash
db.session.commit()
return make_response(jsonify(response_json), 200) return make_response(jsonify(response_json), 200)
def process_instance_report_metadata_show( def process_instance_report_show(
report_hash: str, report_hash: Optional[str] = None,
report_id: Optional[int] = None,
report_identifier: Optional[str] = None,
) -> flask.wrappers.Response: ) -> flask.wrappers.Response:
if report_hash is None and report_id is None and report_identifier is None:
raise ApiError(
error_code="report_key_missing",
message="A report key is needed to lookup a report. Either choose a report_hash, report_id, or report_identifier.",
)
response_result = {}
if report_hash is not None:
json_data = JsonDataModel.query.filter_by(hash=report_hash).first() json_data = JsonDataModel.query.filter_by(hash=report_hash).first()
if json_data is None: if json_data is None:
raise ApiError( raise ApiError(
error_code="report_metadata_not_found", error_code="report_metadata_not_found",
message=f"Could not find report metadata for {report_hash}.", message=f"Could not find report metadata for {report_hash}.",
) )
return make_response(jsonify(json_data.data), 200) response_result = {
"id": 0,
"identifier": "custom",
"name": "custom",
"report_metadata": json_data.data,
}
else:
response_result = ProcessInstanceReportService.report_with_identifier(g.user, report_id, report_identifier)
return make_response(jsonify(response_result), 200)
def process_instance_report_column_list( def process_instance_report_column_list(
@ -490,39 +507,39 @@ def process_instance_report_delete(
return Response(json.dumps({"ok": True}), status=200, mimetype="application/json") return Response(json.dumps({"ok": True}), status=200, mimetype="application/json")
def process_instance_report_show( # def process_instance_report_show(
report_id: int, # report_id: int,
page: int = 1, # page: int = 1,
per_page: int = 100, # per_page: int = 100,
) -> flask.wrappers.Response: # ) -> flask.wrappers.Response:
"""Process_instance_report_show.""" # """Process_instance_report_show."""
process_instances = ProcessInstanceModel.query.order_by( # process_instances = ProcessInstanceModel.query.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)
#
process_instance_report = ProcessInstanceReportModel.query.filter_by( # process_instance_report = ProcessInstanceReportModel.query.filter_by(
id=report_id, # id=report_id,
created_by_id=g.user.id, # created_by_id=g.user.id,
).first() # ).first()
if process_instance_report is None: # if process_instance_report is None:
raise ApiError( # raise ApiError(
error_code="unknown_process_instance_report", # error_code="unknown_process_instance_report",
message="Unknown process instance report", # message="Unknown process instance report",
status_code=404, # status_code=404,
) # )
#
substitution_variables = request.args.to_dict() # substitution_variables = request.args.to_dict()
result_dict = process_instance_report.generate_report(process_instances.items, substitution_variables) # result_dict = process_instance_report.generate_report(process_instances.items, substitution_variables)
#
# update this if we go back to a database query instead of filtering in memory # # update this if we go back to a database query instead of filtering in memory
result_dict["pagination"] = { # result_dict["pagination"] = {
"count": len(result_dict["results"]), # "count": len(result_dict["results"]),
"total": len(result_dict["results"]), # "total": len(result_dict["results"]),
"pages": 1, # "pages": 1,
} # }
#
return Response(json.dumps(result_dict), status=200, mimetype="application/json") # return Response(json.dumps(result_dict), status=200, mimetype="application/json")
#
def process_instance_task_list_without_task_data_for_me( def process_instance_task_list_without_task_data_for_me(
modified_process_model_identifier: str, modified_process_model_identifier: str,

View File

@ -1,5 +1,6 @@
"""APIs for dealing with process groups, process models, and process instances.""" """APIs for dealing with process groups, process models, and process instances."""
import json import json
from spiffworkflow_backend.models.json_data import JsonDataModel # noqa: F401
import os import os
import uuid import uuid
from sys import exc_info from sys import exc_info
@ -220,7 +221,7 @@ def task_data_update(
task_model, new_task_data_dict, "json_data_hash" task_model, new_task_data_dict, "json_data_hash"
) )
if json_data_dict is not None: if json_data_dict is not None:
TaskService.insert_or_update_json_data_records({json_data_dict["hash"]: json_data_dict}) JsonDataModel.insert_or_update_json_data_records({json_data_dict["hash"]: json_data_dict})
ProcessInstanceTmpService.add_event_to_process_instance( ProcessInstanceTmpService.add_event_to_process_instance(
process_instance, ProcessInstanceEventType.task_data_edited.value, task_guid=task_guid process_instance, ProcessInstanceEventType.task_data_edited.value, task_guid=task_guid
) )
@ -537,7 +538,7 @@ def _task_submit_shared(
task_model, spiff_task.data, "json_data_hash" task_model, spiff_task.data, "json_data_hash"
) )
if json_data_dict is not None: if json_data_dict is not None:
TaskService.insert_or_update_json_data_dict(json_data_dict) JsonDataModel.insert_or_update_json_data_dict(json_data_dict)
db.session.add(task_model) db.session.add(task_model)
db.session.commit() db.session.commit()
else: else:

View File

@ -1737,7 +1737,7 @@ class ProcessInstanceProcessor:
bpmn_definition_to_task_definitions_mappings=self.bpmn_definition_to_task_definitions_mappings, bpmn_definition_to_task_definitions_mappings=self.bpmn_definition_to_task_definitions_mappings,
) )
task_service.update_task_model(task_model, spiff_task) task_service.update_task_model(task_model, spiff_task)
TaskService.insert_or_update_json_data_records(task_service.json_data_dicts) JsonDataModel.insert_or_update_json_data_records(task_service.json_data_dicts)
ProcessInstanceTmpService.add_event_to_process_instance( ProcessInstanceTmpService.add_event_to_process_instance(
self.process_instance_model, self.process_instance_model,

View File

@ -15,14 +15,12 @@ from SpiffWorkflow.exceptions import WorkflowException # type: ignore
from SpiffWorkflow.task import Task as SpiffTask # type: ignore from SpiffWorkflow.task import Task as SpiffTask # type: ignore
from SpiffWorkflow.task import TaskState from SpiffWorkflow.task import TaskState
from SpiffWorkflow.task import TaskStateNames from SpiffWorkflow.task import TaskStateNames
from sqlalchemy.dialects.mysql import insert as mysql_insert
from sqlalchemy.dialects.postgresql import insert as postgres_insert
from spiffworkflow_backend.models.bpmn_process import BpmnProcessModel from spiffworkflow_backend.models.bpmn_process import BpmnProcessModel
from spiffworkflow_backend.models.bpmn_process import BpmnProcessNotFoundError from spiffworkflow_backend.models.bpmn_process import BpmnProcessNotFoundError
from spiffworkflow_backend.models.bpmn_process_definition import BpmnProcessDefinitionModel from spiffworkflow_backend.models.bpmn_process_definition import BpmnProcessDefinitionModel
from spiffworkflow_backend.models.db import db from spiffworkflow_backend.models.db import db
from spiffworkflow_backend.models.json_data import JsonDataModel # noqa: F401 from spiffworkflow_backend.models.json_data import JsonDataDict, JsonDataModel # noqa: F401
from spiffworkflow_backend.models.process_instance import ProcessInstanceModel from spiffworkflow_backend.models.process_instance import ProcessInstanceModel
from spiffworkflow_backend.models.process_instance_event import ProcessInstanceEventModel from spiffworkflow_backend.models.process_instance_event import ProcessInstanceEventModel
from spiffworkflow_backend.models.process_instance_event import ProcessInstanceEventType from spiffworkflow_backend.models.process_instance_event import ProcessInstanceEventType
@ -38,11 +36,6 @@ class StartAndEndTimes(TypedDict):
end_in_seconds: Optional[float] end_in_seconds: Optional[float]
class JsonDataDict(TypedDict):
hash: str
data: dict
class TaskModelError(Exception): class TaskModelError(Exception):
"""Copied from SpiffWorkflow.exceptions.WorkflowTaskException. """Copied from SpiffWorkflow.exceptions.WorkflowTaskException.
@ -130,7 +123,7 @@ class TaskService:
db.session.bulk_save_objects(self.bpmn_processes.values()) db.session.bulk_save_objects(self.bpmn_processes.values())
db.session.bulk_save_objects(self.task_models.values()) db.session.bulk_save_objects(self.task_models.values())
db.session.bulk_save_objects(self.process_instance_events.values()) db.session.bulk_save_objects(self.process_instance_events.values())
self.__class__.insert_or_update_json_data_records(self.json_data_dicts) JsonDataModel.insert_or_update_json_data_records(self.json_data_dicts)
def process_parents_and_children_and_save_to_database( def process_parents_and_children_and_save_to_database(
self, self,
@ -483,10 +476,6 @@ class TaskService:
bpmn_process.json_data_hash = bpmn_process_data_hash bpmn_process.json_data_hash = bpmn_process_data_hash
return json_data_dict return json_data_dict
@classmethod
def insert_or_update_json_data_dict(cls, json_data_dict: JsonDataDict) -> None:
TaskService.insert_or_update_json_data_records({json_data_dict["hash"]: json_data_dict})
@classmethod @classmethod
def update_task_data_on_task_model_and_return_dict_if_updated( def update_task_data_on_task_model_and_return_dict_if_updated(
cls, task_model: TaskModel, task_data_dict: dict, task_model_data_column: str cls, task_model: TaskModel, task_data_dict: dict, task_model_data_column: str
@ -610,21 +599,6 @@ class TaskService:
new_properties_json["state"] = getattr(TaskState, state) new_properties_json["state"] = getattr(TaskState, state)
task_model.properties_json = new_properties_json task_model.properties_json = new_properties_json
@classmethod
def insert_or_update_json_data_records(
cls, json_data_hash_to_json_data_dict_mapping: dict[str, JsonDataDict]
) -> None:
list_of_dicts = [*json_data_hash_to_json_data_dict_mapping.values()]
if len(list_of_dicts) > 0:
on_duplicate_key_stmt = None
if current_app.config["SPIFFWORKFLOW_BACKEND_DATABASE_TYPE"] == "mysql":
insert_stmt = mysql_insert(JsonDataModel).values(list_of_dicts)
on_duplicate_key_stmt = insert_stmt.on_duplicate_key_update(data=insert_stmt.inserted.data)
else:
insert_stmt = postgres_insert(JsonDataModel).values(list_of_dicts)
on_duplicate_key_stmt = insert_stmt.on_conflict_do_nothing(index_elements=["hash"])
db.session.execute(on_duplicate_key_stmt)
@classmethod @classmethod
def get_extensions_from_task_model(cls, task_model: TaskModel) -> dict: def get_extensions_from_task_model(cls, task_model: TaskModel) -> dict:
task_definition = task_model.task_definition task_definition = task_model.task_definition

View File

@ -75,73 +75,73 @@ export default function ProcessInstanceListSaveAsReport({
const addProcessInstanceReport = (event: any) => { const addProcessInstanceReport = (event: any) => {
event.preventDefault(); event.preventDefault();
// TODO: make a field to set this // // TODO: make a field to set this
let orderByArray = ['-start_in_seconds', '-id']; // let orderByArray = ['-start_in_seconds', '-id'];
if (orderBy) { // if (orderBy) {
orderByArray = orderBy.split(',').filter((n) => n); // orderByArray = orderBy.split(',').filter((n) => n);
} // }
const filterByArray: any = []; // const filterByArray: any = [];
//
if (processModelSelection) { // if (processModelSelection) {
filterByArray.push({ // filterByArray.push({
field_name: 'process_model_identifier', // field_name: 'process_model_identifier',
field_value: processModelSelection.id, // field_value: processModelSelection.id,
}); // });
} // }
//
if (processInitiatorSelection) { // if (processInitiatorSelection) {
filterByArray.push({ // filterByArray.push({
field_name: 'process_initiator_username', // field_name: 'process_initiator_username',
field_value: processInitiatorSelection.username, // field_value: processInitiatorSelection.username,
}); // });
} // }
//
if (processStatusSelection.length > 0) { // if (processStatusSelection.length > 0) {
filterByArray.push({ // filterByArray.push({
field_name: 'process_status', // field_name: 'process_status',
field_value: processStatusSelection.join(','), // field_value: processStatusSelection.join(','),
operator: 'in', // operator: 'in',
}); // });
} // }
//
if (startFromSeconds) { // if (startFromSeconds) {
filterByArray.push({ // filterByArray.push({
field_name: 'start_from', // field_name: 'start_from',
field_value: startFromSeconds, // field_value: startFromSeconds,
}); // });
} // }
//
if (startToSeconds) { // if (startToSeconds) {
filterByArray.push({ // filterByArray.push({
field_name: 'start_to', // field_name: 'start_to',
field_value: startToSeconds, // field_value: startToSeconds,
}); // });
} // }
//
if (endFromSeconds) { // if (endFromSeconds) {
filterByArray.push({ // filterByArray.push({
field_name: 'end_from', // field_name: 'end_from',
field_value: endFromSeconds, // field_value: endFromSeconds,
}); // });
} // }
//
if (endToSeconds) { // if (endToSeconds) {
filterByArray.push({ // filterByArray.push({
field_name: 'end_to', // field_name: 'end_to',
field_value: endToSeconds, // field_value: endToSeconds,
}); // });
} // }
//
reportMetadata.filter_by.forEach((reportFilter: ReportFilter) => { // reportMetadata.filter_by.forEach((reportFilter: ReportFilter) => {
columnArray.forEach((reportColumn: ReportColumn) => { // columnArray.forEach((reportColumn: ReportColumn) => {
if ( // if (
reportColumn.accessor === reportFilter.field_name && // reportColumn.accessor === reportFilter.field_name &&
reportColumn.filterable // reportColumn.filterable
) { // ) {
filterByArray.push(reportFilter); // filterByArray.push(reportFilter);
} // }
}); // });
}); // });
let path = `/process-instances/reports`; let path = `/process-instances/reports`;
let httpMethod = 'POST'; let httpMethod = 'POST';
@ -156,11 +156,7 @@ export default function ProcessInstanceListSaveAsReport({
httpMethod, httpMethod,
postBody: { postBody: {
identifier, identifier,
report_metadata: { report_metadata: reportMetadata,
columns: columnArray,
order_by: orderByArray,
filter_by: filterByArray,
},
}, },
}); });
handleSaveFormClose(); handleSaveFormClose();

View File

@ -1,4 +1,4 @@
import { useEffect, useMemo, useRef, useState } from 'react'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useNavigate, useParams, useSearchParams } from 'react-router-dom'; import { useNavigate, useParams, useSearchParams } from 'react-router-dom';
// @ts-ignore // @ts-ignore
@ -37,10 +37,7 @@ import {
convertSecondsToFormattedDateString, convertSecondsToFormattedDateString,
convertSecondsToFormattedDateTime, convertSecondsToFormattedDateTime,
convertSecondsToFormattedTimeHoursMinutes, convertSecondsToFormattedTimeHoursMinutes,
decodeBase64,
encodeBase64,
getPageInfoFromSearchParams, getPageInfoFromSearchParams,
getProcessModelFullIdentifierFromSearchParams,
modifyProcessIdentifierForPathParam, modifyProcessIdentifierForPathParam,
refreshAtInterval, refreshAtInterval,
REFRESH_INTERVAL_SECONDS, REFRESH_INTERVAL_SECONDS,
@ -119,6 +116,7 @@ export default function ProcessInstanceListTable({
if (variant === 'all') { if (variant === 'all') {
processInstanceApiSearchPath = '/process-instances'; processInstanceApiSearchPath = '/process-instances';
} }
const params = useParams(); const params = useParams();
const [searchParams, setSearchParams] = useSearchParams(); const [searchParams, setSearchParams] = useSearchParams();
const navigate = useNavigate(); const navigate = useNavigate();
@ -282,9 +280,11 @@ export default function ProcessInstanceListTable({
setProcessInstanceFilters(result.filters); setProcessInstanceFilters(result.filters);
setReportMetadata(result.report.report_metadata); setReportMetadata(result.report.report_metadata);
if (result.report.id) { // if (processInstanceReport) {
setProcessInstanceReportSelection(result.report); // if (processInstanceReport.id > 0) {
} // setProcessInstanceReportSelection(processInstanceReport);
// }
// }
if (result.report_hash) { if (result.report_hash) {
searchParams.set('report_hash', result.report_hash); searchParams.set('report_hash', result.report_hash);
setSearchParams(searchParams); setSearchParams(searchParams);
@ -301,6 +301,11 @@ export default function ProcessInstanceListTable({
} }
}; };
const getData = useCallback(() => {
console.log('WE DO STUFF');
stopRefreshing(null);
}, []);
// eslint-disable-next-line sonarjs/cognitive-complexity // eslint-disable-next-line sonarjs/cognitive-complexity
useEffect(() => { useEffect(() => {
if (!permissionsLoaded) { if (!permissionsLoaded) {
@ -318,9 +323,6 @@ export default function ProcessInstanceListTable({
setPagination(result.pagination); setPagination(result.pagination);
setProcessInstanceFilters(result.filters); setProcessInstanceFilters(result.filters);
setReportMetadata(result.report.report_metadata); setReportMetadata(result.report.report_metadata);
if (result.report.id) {
setProcessInstanceReportSelection(result.report);
}
} }
// Useful to stop refreshing if an api call gets an error // Useful to stop refreshing if an api call gets an error
@ -334,18 +336,21 @@ export default function ProcessInstanceListTable({
} }
}; };
function getProcessInstances( function getProcessInstances(
reportMetadataBody: ReportMetadata | null = null processInstanceReport: ProcessInstanceReport | null = null
) { ) {
if (listHasBeenFiltered) { if (listHasBeenFiltered) {
return; return;
} }
let reportMetadataBodyToUse = reportMetadataBody; let reportMetadataBodyToUse: ReportMetadata = {
if (!reportMetadataBodyToUse) {
reportMetadataBodyToUse = {
columns: [], columns: [],
filter_by: [], filter_by: [],
order_by: [], order_by: [],
}; };
if (processInstanceReport) {
reportMetadataBodyToUse = processInstanceReport.report_metadata;
if (processInstanceReport.id > 0) {
setProcessInstanceReportSelection(processInstanceReport);
}
} }
let selectedProcessModelIdentifier = processModelFullIdentifier; let selectedProcessModelIdentifier = processModelFullIdentifier;
@ -391,22 +396,6 @@ export default function ProcessInstanceListTable({
} }
); );
// if (searchParams.get('report_id')) {
// queryParamString += `&report_id=${searchParams.get('report_id')}`;
// } else if (reportIdentifier) {
// queryParamString += `&report_identifier=${reportIdentifier}`;
// }
// if (searchParams.get('report_columns')) {
// const reportColumnsBase64 = searchParams.get('report_columns');
// if (reportColumnsBase64) {
// const reportColumnsList = JSON.parse(
// decodeBase64(reportColumnsBase64)
// );
// postBody.columns = reportColumnsList;
// }
// }
// eslint-disable-next-line prefer-const // eslint-disable-next-line prefer-const
let { page, perPage } = getPageInfoFromSearchParams( let { page, perPage } = getPageInfoFromSearchParams(
searchParams, searchParams,
@ -427,8 +416,8 @@ export default function ProcessInstanceListTable({
path: `${processInstanceApiSearchPath}?${queryParamString}`, path: `${processInstanceApiSearchPath}?${queryParamString}`,
successCallback: setProcessInstancesFromResult, successCallback: setProcessInstancesFromResult,
httpMethod: 'POST', httpMethod: 'POST',
failureCallback: stopRefreshing, failureCallback: getData,
onUnauthorized: stopRefreshing, onUnauthorized: getData,
postBody: { postBody: {
report_metadata: reportMetadataBodyToUse, report_metadata: reportMetadataBodyToUse,
}, },
@ -438,10 +427,19 @@ export default function ProcessInstanceListTable({
if (listHasBeenFiltered) { if (listHasBeenFiltered) {
return; return;
} }
const reportHash = searchParams.get('report_hash'); const queryParams: string[] = [];
if (reportHash) { ['report_hash', 'report_id', 'report_identifier'].forEach(
(paramName: string) => {
if (searchParams.get(paramName)) {
queryParams.push(`${paramName}=${searchParams.get(paramName)}`);
}
}
);
if (queryParams.length > 0) {
const queryParamString = `?${queryParams.join('&')}`;
HttpService.makeCallToBackend({ HttpService.makeCallToBackend({
path: `/process-instances/report-metadata/${reportHash}`, path: `/process-instances/report-metadata${queryParamString}`,
successCallback: getProcessInstances, successCallback: getProcessInstances,
}); });
} else { } else {
@ -697,6 +695,85 @@ export default function ProcessInstanceListTable({
} }
}; };
const getNewReportMetadataBasedOnPageWidgets = () => {
const {
valid,
startFromSeconds,
startToSeconds,
endFromSeconds,
endToSeconds,
} = calculateStartAndEndSeconds();
if (!valid) {
return null;
}
let newReportMetadata = null;
if (reportMetadata) {
newReportMetadata = { ...reportMetadata };
}
if (!newReportMetadata) {
newReportMetadata = {
columns: [],
filter_by: [],
order_by: [],
};
}
addFieldValueToReportMetadata(
newReportMetadata,
'start_from',
startFromSeconds
);
addFieldValueToReportMetadata(
newReportMetadata,
'start_to',
startToSeconds
);
addFieldValueToReportMetadata(
newReportMetadata,
'end_from',
endFromSeconds
);
addFieldValueToReportMetadata(newReportMetadata, 'end_to', endToSeconds);
if (processStatusSelection.length > 0) {
addFieldValueToReportMetadata(
newReportMetadata,
'process_status',
processStatusSelection.join(',')
);
} else {
removeFieldFromReportMetadata(newReportMetadata, 'process_status');
}
if (processModelSelection) {
addFieldValueToReportMetadata(
newReportMetadata,
'process_model_identifier',
processModelSelection.id
);
} else {
removeFieldFromReportMetadata(
newReportMetadata,
'process_model_identifier'
);
}
if (processInitiatorSelection) {
addFieldValueToReportMetadata(
newReportMetadata,
'process_initiator_username',
processInitiatorSelection.username
);
} else {
removeFieldFromReportMetadata(
newReportMetadata,
'process_initiator_username'
);
}
return newReportMetadata;
};
const applyFilter = (event: any) => { const applyFilter = (event: any) => {
event.preventDefault(); event.preventDefault();
setProcessInitiatorNotFoundErrorText(''); setProcessInitiatorNotFoundErrorText('');
@ -707,74 +784,10 @@ export default function ProcessInstanceListTable({
undefined, undefined,
paginationQueryParamPrefix paginationQueryParamPrefix
); );
const {
valid,
startFromSeconds,
startToSeconds,
endFromSeconds,
endToSeconds,
} = calculateStartAndEndSeconds();
if (!valid) {
return;
}
let postBody = null;
if (reportMetadata) {
postBody = { ...reportMetadata };
}
if (!postBody) {
postBody = {
columns: [],
filter_by: [],
order_by: [],
};
}
addFieldValueToReportMetadata(postBody, 'start_from', startFromSeconds);
addFieldValueToReportMetadata(postBody, 'start_to', startToSeconds);
addFieldValueToReportMetadata(postBody, 'end_from', endFromSeconds);
addFieldValueToReportMetadata(postBody, 'end_to', endToSeconds);
if (processStatusSelection.length > 0) {
addFieldValueToReportMetadata(
postBody,
'process_status',
processStatusSelection.join(',')
);
} else {
removeFieldFromReportMetadata(postBody, 'process_status');
}
if (processModelSelection) {
addFieldValueToReportMetadata(
postBody,
'process_model_identifier',
processModelSelection.id
);
} else {
removeFieldFromReportMetadata(postBody, 'process_model_identifier');
}
// if (processInstanceReportSelection) {
// addFieldValueToReportMetadata(
// postBody,
// 'report_id',
// processInstanceReportSelection.id.toString()
// );
// }
if (processInitiatorSelection) {
addFieldValueToReportMetadata(
postBody,
'process_initiator_username',
processInitiatorSelection.username
);
} else {
removeFieldFromReportMetadata(postBody, 'process_initiator_username');
}
const newReportMetadata = getNewReportMetadataBasedOnPageWidgets();
setListHasBeenFiltered(true); setListHasBeenFiltered(true);
setReportMetadata(postBody); setReportMetadata(newReportMetadata);
searchParams.set('per_page', perPage.toString()); searchParams.set('per_page', perPage.toString());
searchParams.set('page', page.toString()); searchParams.set('page', page.toString());
setSearchParams(searchParams); setSearchParams(searchParams);
@ -783,7 +796,7 @@ export default function ProcessInstanceListTable({
HttpService.makeCallToBackend({ HttpService.makeCallToBackend({
path: `${processInstanceApiSearchPath}?${queryParamString}`, path: `${processInstanceApiSearchPath}?${queryParamString}`,
httpMethod: 'POST', httpMethod: 'POST',
postBody: { report_metadata: postBody }, postBody: { report_metadata: newReportMetadata },
failureCallback: stopRefreshing, failureCallback: stopRefreshing,
onUnauthorized: stopRefreshing, onUnauthorized: stopRefreshing,
successCallback: (result: any) => { successCallback: (result: any) => {
@ -907,14 +920,12 @@ export default function ProcessInstanceListTable({
}); });
}; };
// TODO onSuccess reload/select the new report in the report search const onSaveReportSuccess = (
const onSaveReportSuccess = (result: any, mode: string) => { processInstanceReport: ProcessInstanceReport
processInstanceReportDidChange( ) => {
{ setProcessInstanceReportSelection(processInstanceReport);
selectedItem: result, searchParams.set('report_id', processInstanceReport.id.toString());
}, setSearchParams(searchParams);
mode
);
}; };
const saveAsReportComponent = () => { const saveAsReportComponent = () => {
@ -926,7 +937,9 @@ export default function ProcessInstanceListTable({
endToSeconds, endToSeconds,
} = calculateStartAndEndSeconds(false); } = calculateStartAndEndSeconds(false);
if (!valid || !reportMetadata) { const newReportMetadata = getNewReportMetadataBasedOnPageWidgets();
if (!valid || !reportMetadata || !newReportMetadata) {
return null; return null;
} }
return ( return (
@ -940,7 +953,7 @@ export default function ProcessInstanceListTable({
processInitiatorSelection={processInitiatorSelection} processInitiatorSelection={processInitiatorSelection}
processStatusSelection={processStatusSelection} processStatusSelection={processStatusSelection}
processInstanceReportSelection={processInstanceReportSelection} processInstanceReportSelection={processInstanceReportSelection}
reportMetadata={reportMetadata} reportMetadata={newReportMetadata}
startFromSeconds={startFromSeconds} startFromSeconds={startFromSeconds}
startToSeconds={startToSeconds} startToSeconds={startToSeconds}
endFromSeconds={endFromSeconds} endFromSeconds={endFromSeconds}
@ -1622,6 +1635,8 @@ export default function ProcessInstanceListTable({
<ProcessInstanceReportSearch <ProcessInstanceReportSearch
onChange={processInstanceReportDidChange} onChange={processInstanceReportDidChange}
selectedItem={processInstanceReportSelection} selectedItem={processInstanceReportSelection}
selectedReportId={searchParams.get('report_id')}
handleSetSelectedReportCallback={setProcessInstanceReportSelection}
/> />
</Column>, </Column>,
]; ];

View File

@ -5,7 +5,6 @@ import {
FormLabel, FormLabel,
// @ts-ignore // @ts-ignore
} from '@carbon/react'; } from '@carbon/react';
import { useSearchParams } from 'react-router-dom';
import { truncateString } from '../helpers'; import { truncateString } from '../helpers';
import { ProcessInstanceReport } from '../interfaces'; import { ProcessInstanceReport } from '../interfaces';
import HttpService from '../services/HttpService'; import HttpService from '../services/HttpService';
@ -14,32 +13,42 @@ type OwnProps = {
onChange: (..._args: any[]) => any; onChange: (..._args: any[]) => any;
selectedItem?: ProcessInstanceReport | null; selectedItem?: ProcessInstanceReport | null;
titleText?: string; titleText?: string;
selectedReportId?: string | null;
handleSetSelectedReportCallback?: Function;
}; };
export default function ProcessInstanceReportSearch({ export default function ProcessInstanceReportSearch({
selectedItem, selectedItem,
onChange, onChange,
selectedReportId,
handleSetSelectedReportCallback,
titleText = 'Process instance perspectives', titleText = 'Process instance perspectives',
}: OwnProps) { }: OwnProps) {
const [processInstanceReports, setProcessInstanceReports] = useState< const [processInstanceReports, setProcessInstanceReports] = useState<
ProcessInstanceReport[] | null ProcessInstanceReport[] | null
>(null); >(null);
const [searchParams] = useSearchParams();
const reportId = searchParams.get('report_id');
useEffect(() => { useEffect(() => {
const selectedReportIdAsNumber = Number(selectedReportId);
function setProcessInstanceReportsFromResult( function setProcessInstanceReportsFromResult(
result: ProcessInstanceReport[] result: ProcessInstanceReport[]
) { ) {
setProcessInstanceReports(result); setProcessInstanceReports(result);
if (selectedReportId && handleSetSelectedReportCallback) {
result.forEach((processInstanceReport: ProcessInstanceReport) => {
if (processInstanceReport.id === selectedReportIdAsNumber) {
handleSetSelectedReportCallback(processInstanceReport);
}
});
}
} }
HttpService.makeCallToBackend({ HttpService.makeCallToBackend({
path: `/process-instances/reports`, path: `/process-instances/reports`,
successCallback: setProcessInstanceReportsFromResult, successCallback: setProcessInstanceReportsFromResult,
}); });
}, [reportId]); }, [handleSetSelectedReportCallback, selectedReportId]);
const reportSelectionString = ( const reportSelectionString = (
processInstanceReport: ProcessInstanceReport processInstanceReport: ProcessInstanceReport

View File

@ -8,7 +8,6 @@ import ProcessGroupEdit from './ProcessGroupEdit';
import ProcessModelShow from './ProcessModelShow'; import ProcessModelShow from './ProcessModelShow';
import ProcessModelEditDiagram from './ProcessModelEditDiagram'; import ProcessModelEditDiagram from './ProcessModelEditDiagram';
import ProcessInstanceList from './ProcessInstanceList'; import ProcessInstanceList from './ProcessInstanceList';
import ProcessInstanceReportShow from './ProcessInstanceReportShow';
import ProcessModelNew from './ProcessModelNew'; import ProcessModelNew from './ProcessModelNew';
import ProcessModelEdit from './ProcessModelEdit'; import ProcessModelEdit from './ProcessModelEdit';
import ProcessInstanceShow from './ProcessInstanceShow'; import ProcessInstanceShow from './ProcessInstanceShow';
@ -88,10 +87,6 @@ export default function AdminRoutes() {
path="process-instances/reports" path="process-instances/reports"
element={<ProcessInstanceReportList />} element={<ProcessInstanceReportList />}
/> />
<Route
path="process-instances/reports/:report_identifier"
element={<ProcessInstanceReportShow />}
/>
<Route <Route
path="process-instances/reports/new" path="process-instances/reports/new"
element={<ProcessInstanceReportNew />} element={<ProcessInstanceReportNew />}

View File

@ -1,97 +0,0 @@
import { useEffect, useState } from 'react';
import { useParams, useSearchParams } from 'react-router-dom';
// @ts-ignore
import { Button, Table } from '@carbon/react';
import ProcessBreadcrumb from '../components/ProcessBreadcrumb';
import PaginationForTable from '../components/PaginationForTable';
import HttpService from '../services/HttpService';
import { getPageInfoFromSearchParams } from '../helpers';
const PER_PAGE_FOR_PROCESS_INSTANCE_REPORT = 500;
export default function ProcessInstanceReport() {
const params = useParams();
const [searchParams] = useSearchParams();
const [processInstances, setProcessInstances] = useState([]);
const [reportMetadata, setReportMetadata] = useState({});
const [pagination, setPagination] = useState(null);
useEffect(() => {
const processResult = (result: any) => {
const processInstancesFromApi = result.results;
setProcessInstances(processInstancesFromApi);
setReportMetadata(result.report_metadata);
setPagination(result.pagination);
};
function getProcessInstances() {
const { page, perPage } = getPageInfoFromSearchParams(
searchParams,
PER_PAGE_FOR_PROCESS_INSTANCE_REPORT
);
let query = `?page=${page}&per_page=${perPage}`;
searchParams.forEach((value, key) => {
if (key !== 'page' && key !== 'per_page') {
query += `&${key}=${value}`;
}
});
HttpService.makeCallToBackend({
path: `/process-instances/reports/${params.report_identifier}${query}`,
successCallback: processResult,
});
}
getProcessInstances();
}, [searchParams, params]);
const buildTable = () => {
const headers = (reportMetadata as any).columns.map((column: any) => {
return <th>{(column as any).Header}</th>;
});
const rows = processInstances.map((row) => {
const currentRow = (reportMetadata as any).columns.map((column: any) => {
return <td>{(row as any)[column.accessor]}</td>;
});
return <tr key={(row as any).id}>{currentRow}</tr>;
});
return (
<Table striped bordered>
<thead>
<tr>{headers}</tr>
</thead>
<tbody>{rows}</tbody>
</Table>
);
};
if (pagination) {
const { page, perPage } = getPageInfoFromSearchParams(
searchParams,
PER_PAGE_FOR_PROCESS_INSTANCE_REPORT
);
return (
<main>
<ProcessBreadcrumb
hotCrumbs={[['Process Groups', '/admin'], ['Process Instance']]}
/>
<h1>Process Instance Perspective: {params.report_identifier}</h1>
<Button
href={`/admin/process-instances/reports/${params.report_identifier}/edit`}
>
Edit process instance perspective
</Button>
<PaginationForTable
page={page}
perPage={perPage}
pagination={pagination}
tableToDisplay={buildTable()}
/>
</main>
);
}
return null;
}