diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py index b2b07ae5..753b6c3c 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py @@ -12,7 +12,6 @@ from typing import Union import connexion # type: ignore import flask.wrappers import jinja2 -from spiffworkflow_backend.models.process_instance_metadata import ProcessInstanceMetadataModel import werkzeug from flask import Blueprint from flask import current_app @@ -28,10 +27,12 @@ from lxml import etree # type: ignore from lxml.builder import ElementMaker # type: ignore from SpiffWorkflow.task import Task as SpiffTask # type: ignore from SpiffWorkflow.task import TaskState -from sqlalchemy import and_, func +from sqlalchemy import and_ from sqlalchemy import asc from sqlalchemy import desc -from sqlalchemy.orm import aliased, joinedload +from sqlalchemy import func +from sqlalchemy.orm import aliased +from sqlalchemy.orm import joinedload from spiffworkflow_backend.exceptions.process_entity_not_found_error import ( ProcessEntityNotFoundError, @@ -53,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 ProcessInstanceModelSchema 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 ( ProcessInstanceReportModel, ) @@ -814,9 +818,9 @@ def process_instance_list( # process_model_identifier = un_modify_modified_process_model_id(modified_process_model_identifier) process_instance_query = ProcessInstanceModel.query # Always join that hot user table for good performance at serialization time. - # process_instance_query = process_instance_query.options( - # joinedload(ProcessInstanceModel.process_initiator, ProcessInstanceModel.process_initiator_id == UserModel.id) - # ) + process_instance_query = process_instance_query.options( + joinedload(ProcessInstanceModel.process_initiator) + ) if report_filter.process_model_identifier is not None: process_model = get_process_model( @@ -929,14 +933,22 @@ def process_instance_list( 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: + 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.options(joinedload(instance_metadata_alias, ProcessInstanceModel.id == instance_metadata_alias.process_instance_id, innerjoin=False)).filter(instance_metadata_alias.key == column['accessor']) - .add_columns(func.max(instance_metadata_alias.value).label(column['accessor'])) + process_instance_query.outerjoin( + instance_metadata_alias, + ProcessInstanceModel.id == instance_metadata_alias.process_instance_id, + ) + .filter(instance_metadata_alias.key == column["accessor"]) + .add_columns( + func.max(instance_metadata_alias.value).label(column["accessor"]) + ) ) process_instances = ( @@ -947,7 +959,9 @@ def process_instance_list( .paginate(page=page, per_page=per_page, error_out=False) ) - results = ProcessInstanceReportService.add_metadata_columns_to_process_instance(process_instances.items, process_instance_report.report_metadata['columns']) + results = ProcessInstanceReportService.add_metadata_columns_to_process_instance( + process_instances.items, process_instance_report.report_metadata["columns"] + ) report_metadata = process_instance_report.report_metadata response_json = { diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_report_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_report_service.py index 6c579826..20563be3 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_report_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_report_service.py @@ -1,9 +1,11 @@ """Process_instance_report_service.""" from dataclasses import dataclass -from flask_bpmn.models.db import db from typing import Optional -from spiffworkflow_backend.models.process_instance import ProcessInstanceModel +import sqlalchemy +from flask_bpmn.models.db import db + +from spiffworkflow_backend.models.process_instance import ProcessInstanceModel from spiffworkflow_backend.models.process_instance_report import ( ProcessInstanceReportModel, ) @@ -245,18 +247,26 @@ class ProcessInstanceReportService: return report_filter @classmethod - def add_metadata_columns_to_process_instance(cls, process_instance_sqlalchemy_rows, metadata_columns: list[dict]) -> list[dict]: + 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.""" stock_columns = cls.get_column_names_for_model(ProcessInstanceModel) results = [] for process_instance in process_instance_sqlalchemy_rows: - process_instance_dict = process_instance['ProcessInstanceModel'].serialized + process_instance_dict = process_instance["ProcessInstanceModel"].serialized for metadata_column in metadata_columns: - if metadata_column['accessor'] not in stock_columns: - process_instance_dict[metadata_column['accessor']] = process_instance[metadata_column['accessor']] + if metadata_column["accessor"] not in stock_columns: + 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]: + 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] diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_api.py b/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_api.py index fb33d246..e22ec77b 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_api.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_api.py @@ -4,13 +4,11 @@ import json import os import time from typing import Any -from conftest import with_super_admin_user import pytest from flask.app import Flask from flask.testing import FlaskClient from flask_bpmn.models.db import db -from spiffworkflow_backend.models.process_instance_metadata import ProcessInstanceMetadataModel from tests.spiffworkflow_backend.helpers.base_test import BaseTest from tests.spiffworkflow_backend.helpers.test_data import load_test_spec @@ -22,6 +20,9 @@ from spiffworkflow_backend.models.group import GroupModel from spiffworkflow_backend.models.process_group import ProcessGroup from spiffworkflow_backend.models.process_instance import ProcessInstanceModel 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 ( ProcessInstanceReportModel, ) @@ -2554,10 +2555,11 @@ class TestProcessApi(BaseTest): 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_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 @@ -2570,7 +2572,6 @@ class TestProcessApi(BaseTest): ).all() assert len(process_instance_metadata) == 2 - report_metadata = { "columns": [ {"Header": "ID", "accessor": "id"},