mirror of
https://github.com/sartography/spiff-arena.git
synced 2025-01-14 11:34:29 +00:00
Merge remote-tracking branch 'origin/feature/add_process_model_metadata_to_reporting' into new_report
This commit is contained in:
commit
d1506887b4
1908
spiffworkflow-backend/:
Normal file
1908
spiffworkflow-backend/:
Normal file
File diff suppressed because it is too large
Load Diff
@ -204,18 +204,8 @@ class ProcessInstanceReportModel(SpiffworkflowBaseDBModel):
|
|||||||
user: UserModel,
|
user: UserModel,
|
||||||
) -> ProcessInstanceReportModel:
|
) -> ProcessInstanceReportModel:
|
||||||
"""Create_with_attributes."""
|
"""Create_with_attributes."""
|
||||||
# <<<<<<< HEAD
|
|
||||||
# process_model = ProcessModelService.get_process_model(
|
|
||||||
# process_model_id=f"{process_model_identifier}"
|
|
||||||
# )
|
|
||||||
# process_instance_report = cls(
|
|
||||||
# identifier=identifier,
|
|
||||||
# process_group_identifier="process_model.process_group_id",
|
|
||||||
# process_model_identifier=process_model.id,
|
|
||||||
# =======
|
|
||||||
process_instance_report = cls(
|
process_instance_report = cls(
|
||||||
identifier=identifier,
|
identifier=identifier,
|
||||||
# >>>>>>> main
|
|
||||||
created_by_id=user.id,
|
created_by_id=user.id,
|
||||||
report_metadata=report_metadata,
|
report_metadata=report_metadata,
|
||||||
)
|
)
|
||||||
|
@ -30,6 +30,8 @@ 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 func
|
||||||
|
from sqlalchemy.orm import aliased
|
||||||
from sqlalchemy.orm import joinedload
|
from sqlalchemy.orm import joinedload
|
||||||
|
|
||||||
from spiffworkflow_backend.exceptions.process_entity_not_found_error import (
|
from spiffworkflow_backend.exceptions.process_entity_not_found_error import (
|
||||||
@ -52,6 +54,9 @@ from spiffworkflow_backend.models.process_instance import ProcessInstanceApiSche
|
|||||||
from spiffworkflow_backend.models.process_instance import ProcessInstanceModel
|
from spiffworkflow_backend.models.process_instance import ProcessInstanceModel
|
||||||
from spiffworkflow_backend.models.process_instance import ProcessInstanceModelSchema
|
from spiffworkflow_backend.models.process_instance import ProcessInstanceModelSchema
|
||||||
from spiffworkflow_backend.models.process_instance import ProcessInstanceStatus
|
from spiffworkflow_backend.models.process_instance import ProcessInstanceStatus
|
||||||
|
from spiffworkflow_backend.models.process_instance_metadata import (
|
||||||
|
ProcessInstanceMetadataModel,
|
||||||
|
)
|
||||||
from spiffworkflow_backend.models.process_instance_report import (
|
from spiffworkflow_backend.models.process_instance_report import (
|
||||||
ProcessInstanceReportModel,
|
ProcessInstanceReportModel,
|
||||||
)
|
)
|
||||||
@ -928,19 +933,32 @@ def process_instance_list(
|
|||||||
UserGroupAssignmentModel.user_id == g.user.id
|
UserGroupAssignmentModel.user_id == g.user.id
|
||||||
)
|
)
|
||||||
|
|
||||||
|
stock_columns = ProcessInstanceReportService.get_column_names_for_model(
|
||||||
|
ProcessInstanceModel
|
||||||
|
)
|
||||||
|
for column in process_instance_report.report_metadata["columns"]:
|
||||||
|
if column["accessor"] in stock_columns:
|
||||||
|
continue
|
||||||
|
instance_metadata_alias = aliased(ProcessInstanceMetadataModel)
|
||||||
|
process_instance_query = process_instance_query.outerjoin(
|
||||||
|
instance_metadata_alias,
|
||||||
|
and_(
|
||||||
|
ProcessInstanceModel.id == instance_metadata_alias.process_instance_id,
|
||||||
|
instance_metadata_alias.key == column["accessor"],
|
||||||
|
),
|
||||||
|
).add_columns(func.max(instance_metadata_alias.value).label(column["accessor"]))
|
||||||
|
|
||||||
process_instances = (
|
process_instances = (
|
||||||
process_instance_query.group_by(ProcessInstanceModel.id)
|
process_instance_query.group_by(ProcessInstanceModel.id)
|
||||||
|
.add_columns(ProcessInstanceModel.id)
|
||||||
.order_by(
|
.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 = ProcessInstanceReportService.add_metadata_columns_to_process_instance(
|
||||||
map(
|
process_instances.items, process_instance_report.report_metadata["columns"]
|
||||||
ProcessInstanceService.serialize_flat_with_task_data,
|
|
||||||
process_instances.items,
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
report_metadata = process_instance_report.report_metadata
|
report_metadata = process_instance_report.report_metadata
|
||||||
|
|
||||||
|
@ -235,8 +235,9 @@ class AuthenticationService:
|
|||||||
refresh_token_object: RefreshTokenModel = RefreshTokenModel.query.filter(
|
refresh_token_object: RefreshTokenModel = RefreshTokenModel.query.filter(
|
||||||
RefreshTokenModel.user_id == user_id
|
RefreshTokenModel.user_id == user_id
|
||||||
).first()
|
).first()
|
||||||
assert refresh_token_object # noqa: S101
|
if refresh_token_object:
|
||||||
return refresh_token_object.token
|
return refresh_token_object.token
|
||||||
|
return None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_auth_token_from_refresh_token(cls, refresh_token: str) -> dict:
|
def get_auth_token_from_refresh_token(cls, refresh_token: str) -> dict:
|
||||||
|
@ -2,6 +2,9 @@
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
|
import sqlalchemy
|
||||||
|
from flask_bpmn.models.db import db
|
||||||
|
|
||||||
from spiffworkflow_backend.models.process_instance_report import (
|
from spiffworkflow_backend.models.process_instance_report import (
|
||||||
ProcessInstanceReportModel,
|
ProcessInstanceReportModel,
|
||||||
)
|
)
|
||||||
@ -241,3 +244,27 @@ class ProcessInstanceReportService:
|
|||||||
)
|
)
|
||||||
|
|
||||||
return report_filter
|
return report_filter
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def add_metadata_columns_to_process_instance(
|
||||||
|
cls,
|
||||||
|
process_instance_sqlalchemy_rows: list[sqlalchemy.engine.row.Row], # type: ignore
|
||||||
|
metadata_columns: list[dict],
|
||||||
|
) -> list[dict]:
|
||||||
|
"""Add_metadata_columns_to_process_instance."""
|
||||||
|
results = []
|
||||||
|
for process_instance in process_instance_sqlalchemy_rows:
|
||||||
|
process_instance_dict = process_instance["ProcessInstanceModel"].serialized
|
||||||
|
for metadata_column in metadata_columns:
|
||||||
|
if metadata_column["accessor"] not in process_instance_dict:
|
||||||
|
process_instance_dict[
|
||||||
|
metadata_column["accessor"]
|
||||||
|
] = process_instance[metadata_column["accessor"]]
|
||||||
|
|
||||||
|
results.append(process_instance_dict)
|
||||||
|
return results
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_column_names_for_model(cls, model: db.Model) -> list[str]: # type: ignore
|
||||||
|
"""Get_column_names_for_model."""
|
||||||
|
return [i.name for i in model.__table__.columns]
|
||||||
|
@ -0,0 +1,40 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" id="Definitions_96f6665" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="3.0.0-dev">
|
||||||
|
<bpmn:process id="Process_zqd49ox" isExecutable="true">
|
||||||
|
<bpmn:startEvent id="StartEvent_1">
|
||||||
|
<bpmn:outgoing>Flow_0fmt4q1</bpmn:outgoing>
|
||||||
|
</bpmn:startEvent>
|
||||||
|
<bpmn:sequenceFlow id="Flow_0fmt4q1" sourceRef="StartEvent_1" targetRef="save_metadata" />
|
||||||
|
<bpmn:scriptTask id="save_metadata" name="Save Metadata">
|
||||||
|
<bpmn:incoming>Flow_0fmt4q1</bpmn:incoming>
|
||||||
|
<bpmn:outgoing>Flow_0hhrkce</bpmn:outgoing>
|
||||||
|
<bpmn:script>save_process_instance_metadata({"key1": "value1", "key2": "value2"})</bpmn:script>
|
||||||
|
</bpmn:scriptTask>
|
||||||
|
<bpmn:endEvent id="Event_10onn1h">
|
||||||
|
<bpmn:incoming>Flow_0hhrkce</bpmn:incoming>
|
||||||
|
</bpmn:endEvent>
|
||||||
|
<bpmn:sequenceFlow id="Flow_0hhrkce" sourceRef="save_metadata" targetRef="Event_10onn1h" />
|
||||||
|
</bpmn:process>
|
||||||
|
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
|
||||||
|
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_zqd49ox">
|
||||||
|
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
|
||||||
|
<dc:Bounds x="179" y="159" width="36" height="36" />
|
||||||
|
</bpmndi:BPMNShape>
|
||||||
|
<bpmndi:BPMNShape id="Activity_0e0gdj6_di" bpmnElement="save_metadata">
|
||||||
|
<dc:Bounds x="270" y="137" width="100" height="80" />
|
||||||
|
<bpmndi:BPMNLabel />
|
||||||
|
</bpmndi:BPMNShape>
|
||||||
|
<bpmndi:BPMNShape id="Event_10onn1h_di" bpmnElement="Event_10onn1h">
|
||||||
|
<dc:Bounds x="432" y="159" width="36" height="36" />
|
||||||
|
</bpmndi:BPMNShape>
|
||||||
|
<bpmndi:BPMNEdge id="Flow_0fmt4q1_di" bpmnElement="Flow_0fmt4q1">
|
||||||
|
<di:waypoint x="215" y="177" />
|
||||||
|
<di:waypoint x="270" y="177" />
|
||||||
|
</bpmndi:BPMNEdge>
|
||||||
|
<bpmndi:BPMNEdge id="Flow_0hhrkce_di" bpmnElement="Flow_0hhrkce">
|
||||||
|
<di:waypoint x="370" y="177" />
|
||||||
|
<di:waypoint x="432" y="177" />
|
||||||
|
</bpmndi:BPMNEdge>
|
||||||
|
</bpmndi:BPMNPlane>
|
||||||
|
</bpmndi:BPMNDiagram>
|
||||||
|
</bpmn:definitions>
|
@ -20,6 +20,9 @@ from spiffworkflow_backend.models.group import GroupModel
|
|||||||
from spiffworkflow_backend.models.process_group import ProcessGroup
|
from spiffworkflow_backend.models.process_group import ProcessGroup
|
||||||
from spiffworkflow_backend.models.process_instance import ProcessInstanceModel
|
from spiffworkflow_backend.models.process_instance import ProcessInstanceModel
|
||||||
from spiffworkflow_backend.models.process_instance import ProcessInstanceStatus
|
from spiffworkflow_backend.models.process_instance import ProcessInstanceStatus
|
||||||
|
from spiffworkflow_backend.models.process_instance_metadata import (
|
||||||
|
ProcessInstanceMetadataModel,
|
||||||
|
)
|
||||||
from spiffworkflow_backend.models.process_instance_report import (
|
from spiffworkflow_backend.models.process_instance_report import (
|
||||||
ProcessInstanceReportModel,
|
ProcessInstanceReportModel,
|
||||||
)
|
)
|
||||||
@ -2544,3 +2547,60 @@ class TestProcessApi(BaseTest):
|
|||||||
# make sure the new subgroup does exist
|
# make sure the new subgroup does exist
|
||||||
new_process_group = ProcessModelService.get_process_group(new_sub_path)
|
new_process_group = ProcessModelService.get_process_group(new_sub_path)
|
||||||
assert new_process_group.id == new_sub_path
|
assert new_process_group.id == new_sub_path
|
||||||
|
|
||||||
|
def test_can_get_process_instance_list_with_report_metadata(
|
||||||
|
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."""
|
||||||
|
process_model = load_test_spec(
|
||||||
|
process_model_id="test-process-instance-metadata-report",
|
||||||
|
bpmn_file_name="process_instance_metadata.bpmn",
|
||||||
|
process_model_source_directory="test-process-instance-metadata-report",
|
||||||
|
)
|
||||||
|
process_instance = self.create_process_instance_from_process_model(
|
||||||
|
process_model=process_model, user=with_super_admin_user
|
||||||
|
)
|
||||||
|
|
||||||
|
processor = ProcessInstanceProcessor(process_instance)
|
||||||
|
processor.do_engine_steps(save=True)
|
||||||
|
process_instance_metadata = ProcessInstanceMetadataModel.query.filter_by(
|
||||||
|
process_instance_id=process_instance.id
|
||||||
|
).all()
|
||||||
|
assert len(process_instance_metadata) == 2
|
||||||
|
|
||||||
|
report_metadata = {
|
||||||
|
"columns": [
|
||||||
|
{"Header": "ID", "accessor": "id"},
|
||||||
|
{"Header": "Status", "accessor": "status"},
|
||||||
|
{"Header": "Key One", "accessor": "key1"},
|
||||||
|
# {"Header": "Key Two", "accessor": "key2"},
|
||||||
|
],
|
||||||
|
"order_by": ["status"],
|
||||||
|
"filter_by": [],
|
||||||
|
}
|
||||||
|
process_instance_report = ProcessInstanceReportModel.create_with_attributes(
|
||||||
|
identifier="sure",
|
||||||
|
report_metadata=report_metadata,
|
||||||
|
user=with_super_admin_user,
|
||||||
|
)
|
||||||
|
|
||||||
|
response = client.get(
|
||||||
|
f"/v1.0/process-instances?report_identifier={process_instance_report.identifier}",
|
||||||
|
headers=self.logged_in_headers(with_super_admin_user),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response.json is not None
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
assert len(response.json["results"]) == 1
|
||||||
|
assert response.json["results"][0]["status"] == "complete"
|
||||||
|
assert response.json["results"][0]["id"] == process_instance.id
|
||||||
|
assert response.json["results"][0]["key1"] == "value1"
|
||||||
|
# assert response.json["results"][0]["key2"] == "value2"
|
||||||
|
assert response.json["pagination"]["count"] == 1
|
||||||
|
assert response.json["pagination"]["pages"] == 1
|
||||||
|
assert response.json["pagination"]["total"] == 1
|
||||||
|
Loading…
x
Reference in New Issue
Block a user