added support to order reports by given column and metadata headers w/ burnettk

This commit is contained in:
jasquat 2022-12-05 10:59:27 -05:00
parent f920edbb56
commit 424eb2412e
4 changed files with 120 additions and 10 deletions

View File

@ -75,6 +75,10 @@ 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)
@classmethod
def default_order_by(cls) -> list[str]:
return ["-start_in_seconds", "-id"]
@classmethod @classmethod
def add_fixtures(cls) -> None: def add_fixtures(cls) -> None:
"""Add_fixtures.""" """Add_fixtures."""

View File

@ -3,6 +3,7 @@ import json
import random import random
import string import string
import uuid import uuid
import re
from typing import Any from typing import Any
from typing import Dict from typing import Dict
from typing import Optional from typing import Optional
@ -944,6 +945,7 @@ def process_instance_list(
UserGroupAssignmentModel.user_id == g.user.id UserGroupAssignmentModel.user_id == g.user.id
) )
instance_metadata_aliases = {}
stock_columns = ProcessInstanceReportService.get_column_names_for_model( stock_columns = ProcessInstanceReportService.get_column_names_for_model(
ProcessInstanceModel ProcessInstanceModel
) )
@ -951,15 +953,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)
instance_metadata_aliases[column['accessor']] = instance_metadata_alias
filter_for_column = next( filter_for_column = None
( if 'filter_by' in process_instance_report.report_metadata:
f filter_for_column = next(
for f in process_instance_report.report_metadata["filter_by"] (
if f["field_name"] == column["accessor"] f
), for f in process_instance_report.report_metadata["filter_by"]
None, if f["field_name"] == column["accessor"]
) ),
None,
)
isouter = True isouter = True
conditions = [ conditions = [
ProcessInstanceModel.id == instance_metadata_alias.process_instance_id, ProcessInstanceModel.id == instance_metadata_alias.process_instance_id,
@ -974,11 +979,28 @@ def process_instance_list(
instance_metadata_alias, and_(*conditions), isouter=isouter instance_metadata_alias, and_(*conditions), isouter=isouter
).add_columns(func.max(instance_metadata_alias.value).label(column["accessor"])) ).add_columns(func.max(instance_metadata_alias.value).label(column["accessor"]))
order_by_query_array = []
order_by_array = process_instance_report.report_metadata['order_by']
if len(order_by_array) < 1:
order_by_array = ProcessInstanceReportModel.default_order_by()
for order_by_option in order_by_array:
attribute = re.sub('^-', '', order_by_option)
if attribute in stock_columns:
if order_by_option.startswith('-'):
order_by_query_array.append(getattr(ProcessInstanceModel, attribute).desc())
else:
order_by_query_array.append(getattr(ProcessInstanceModel, attribute).asc())
elif attribute in instance_metadata_aliases:
if order_by_option.startswith('-'):
order_by_query_array.append(instance_metadata_aliases[attribute].value.desc())
else:
order_by_query_array.append(instance_metadata_aliases[attribute].value.asc())
process_instances = ( process_instances = (
process_instance_query.group_by(ProcessInstanceModel.id) process_instance_query.group_by(ProcessInstanceModel.id)
.add_columns(ProcessInstanceModel.id) .add_columns(ProcessInstanceModel.id)
.order_by( .order_by(
ProcessInstanceModel.start_in_seconds.desc(), ProcessInstanceModel.id.desc() # type: ignore *order_by_query_array
) )
.paginate(page=page, per_page=per_page, error_out=False) .paginate(page=page, per_page=per_page, error_out=False)
) )

View File

@ -14,7 +14,8 @@
<bpmn:outgoing>Flow_18gs4jt</bpmn:outgoing> <bpmn:outgoing>Flow_18gs4jt</bpmn:outgoing>
<bpmn:script>outer = {} <bpmn:script>outer = {}
invoice_number = 123 invoice_number = 123
outer["inner"] = 'sweet1'</bpmn:script> outer["inner"] = 'sweet1'
outer['time'] = time.time_ns()</bpmn:script>
</bpmn:scriptTask> </bpmn:scriptTask>
<bpmn:sequenceFlow id="Flow_1flxgry" sourceRef="Activity_1bvyv67" targetRef="Event_1tk4dsv" /> <bpmn:sequenceFlow id="Flow_1flxgry" sourceRef="Activity_1bvyv67" targetRef="Event_1tk4dsv" />
<bpmn:scriptTask id="Activity_1bvyv67" name="First setting of data"> <bpmn:scriptTask id="Activity_1bvyv67" name="First setting of data">

View File

@ -2657,3 +2657,86 @@ class TestProcessApi(BaseTest):
{"Header": "key2", "accessor": "key2", "filterable": True}, {"Header": "key2", "accessor": "key2", "filterable": True},
{"Header": "key3", "accessor": "key3", "filterable": True}, {"Header": "key3", "accessor": "key3", "filterable": True},
] ]
def test_process_instance_list_can_order_by_metadata(
self,
app: Flask,
client: FlaskClient,
with_db_and_bpmn_file_cleanup: None,
with_super_admin_user: UserModel,
) -> None:
self.create_process_group(
client, with_super_admin_user, "test_group", "test_group"
)
process_model = load_test_spec(
"test_group/hello_world",
process_model_source_directory="nested-task-data-structure",
)
ProcessModelService.update_process_model(
process_model,
{
"metadata_extraction_paths": [
{"key": "time_ns", "path": "outer.time"},
]
},
)
process_instance_one = self.create_process_instance_from_process_model(
process_model
)
processor = ProcessInstanceProcessor(process_instance_one)
processor.do_engine_steps(save=True)
assert process_instance_one.status == "complete"
process_instance_two = self.create_process_instance_from_process_model(
process_model
)
processor = ProcessInstanceProcessor(process_instance_two)
processor.do_engine_steps(save=True)
assert process_instance_two.status == "complete"
report_metadata = {
"columns": [
{"Header": "id", "accessor": "id"},
{"Header": "Time", "accessor": "time_ns"},
],
"order_by": ["time_ns"],
}
report_one = ProcessInstanceReportModel.create_with_attributes(
identifier="report_one",
report_metadata=report_metadata,
user=with_super_admin_user,
)
response = client.get(
f"/v1.0/process-instances?report_id={report_one.id}",
headers=self.logged_in_headers(with_super_admin_user),
)
assert response.status_code == 200
assert response.json is not None
assert len(response.json["results"]) == 2
assert response.json['results'][0]['id'] == process_instance_one.id
assert response.json['results'][1]['id'] == process_instance_two.id
report_metadata = {
"columns": [
{"Header": "id", "accessor": "id"},
{"Header": "Time", "accessor": "time_ns"},
],
"order_by": ["-time_ns"],
}
report_two = ProcessInstanceReportModel.create_with_attributes(
identifier="report_two",
report_metadata=report_metadata,
user=with_super_admin_user,
)
response = client.get(
f"/v1.0/process-instances?report_id={report_two.id}",
headers=self.logged_in_headers(with_super_admin_user),
)
assert response.status_code == 200
assert response.json is not None
assert len(response.json["results"]) == 2
assert response.json['results'][1]['id'] == process_instance_one.id
assert response.json['results'][0]['id'] == process_instance_two.id