diff --git a/spiffworkflow-backend/migrations/versions/5d8e49f9c560_.py b/spiffworkflow-backend/migrations/versions/5d8e49f9c560_.py new file mode 100644 index 000000000..654c6a07e --- /dev/null +++ b/spiffworkflow-backend/migrations/versions/5d8e49f9c560_.py @@ -0,0 +1,36 @@ +"""empty message + +Revision ID: 5d8e49f9c560 +Revises: 0b5dd14bfbac +Create Date: 2023-04-17 11:28:39.714193 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import mysql + +# revision identifiers, used by Alembic. +revision = '5d8e49f9c560' +down_revision = '0b5dd14bfbac' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('bpmn_process_definition', schema=None) as batch_op: + batch_op.alter_column('hash', existing_type=sa.String(length=255), new_column_name='single_process_hash') + batch_op.add_column(sa.Column('full_process_model_hash', sa.String(length=255), nullable=True)) + batch_op.create_unique_constraint(None, ['full_process_model_hash']) + + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('bpmn_process_definition', schema=None) as batch_op: + batch_op.drop_constraint('full_process_model_hash', type_='unique') + batch_op.drop_column('full_process_model_hash') + batch_op.alter_column('single_process_hash', existing_type=sa.String(length=255), new_column_name='hash') + + # ### end Alembic commands ### diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/models/bpmn_process_definition.py b/spiffworkflow-backend/src/spiffworkflow_backend/models/bpmn_process_definition.py index 902062357..615f529bc 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/models/bpmn_process_definition.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/models/bpmn_process_definition.py @@ -21,7 +21,11 @@ class BpmnProcessDefinitionModel(SpiffworkflowBaseDBModel): # note that a call activity is its own row in this table, with its own hash, # and therefore it only gets stored once per version, and can be reused # by multiple calling processes. - hash: str = db.Column(db.String(255), nullable=False, unique=True) + single_process_hash: str = db.Column(db.String(255), nullable=False, unique=True) + + # only the top level parent will have this set + # it includes all subprocesses and call activities + full_process_model_hash: str | None = db.Column(db.String(255), nullable=True, unique=True, default=None) bpmn_identifier: str = db.Column(db.String(255), nullable=False, index=True) bpmn_name: str = db.Column(db.String(255), nullable=True, index=True) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py index 3527e1a30..c031d3680 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py @@ -959,18 +959,31 @@ class ProcessInstanceProcessor: process_bpmn_properties: dict, bpmn_process_definition_parent: Optional[BpmnProcessDefinitionModel] = None, store_bpmn_definition_mappings: bool = False, + full_bpmn_spec_dict: Optional[dict] = None, ) -> BpmnProcessDefinitionModel: process_bpmn_identifier = process_bpmn_properties["name"] process_bpmn_name = process_bpmn_properties["description"] - new_hash_digest = sha256(json.dumps(process_bpmn_properties, sort_keys=True).encode("utf8")).hexdigest() - bpmn_process_definition: Optional[BpmnProcessDefinitionModel] = BpmnProcessDefinitionModel.query.filter_by( - hash=new_hash_digest - ).first() + + bpmn_process_definition: Optional[BpmnProcessDefinitionModel] = None + single_process_hash = sha256(json.dumps(process_bpmn_properties, sort_keys=True).encode("utf8")).hexdigest() + full_process_model_hash = None + if full_bpmn_spec_dict is not None: + full_process_model_hash = sha256( + json.dumps(full_bpmn_spec_dict, sort_keys=True).encode("utf8") + ).hexdigest() + bpmn_process_definition = BpmnProcessDefinitionModel.query.filter_by( + full_process_model_hash=full_process_model_hash + ).first() + else: + bpmn_process_definition = BpmnProcessDefinitionModel.query.filter_by( + single_process_hash=single_process_hash + ).first() if bpmn_process_definition is None: task_specs = process_bpmn_properties.pop("task_specs") bpmn_process_definition = BpmnProcessDefinitionModel( - hash=new_hash_digest, + single_process_hash=single_process_hash, + full_process_model_hash=full_process_model_hash, bpmn_identifier=process_bpmn_identifier, bpmn_name=process_bpmn_name, properties_json=process_bpmn_properties, @@ -1050,6 +1063,7 @@ class ProcessInstanceProcessor: bpmn_process_definition_parent = self._store_bpmn_process_definition( bpmn_spec_dict["spec"], store_bpmn_definition_mappings=store_bpmn_definition_mappings, + full_bpmn_spec_dict=bpmn_spec_dict, ) for process_bpmn_properties in bpmn_spec_dict["subprocess_specs"].values(): self._store_bpmn_process_definition( 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 e354d31ee..a16d9d7c7 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 @@ -622,7 +622,13 @@ class ProcessInstanceReportService: group_model_join_conditions = [GroupModel.id == HumanTaskModel.lane_assignment_id] if report_filter.user_group_identifier: group_model_join_conditions.append(GroupModel.identifier == report_filter.user_group_identifier) + process_instance_query = process_instance_query.join(HumanTaskModel) + if report_filter.has_active_status: + process_instance_query = process_instance_query.filter( + HumanTaskModel.completed.is_(False) # type: ignore + ) + process_instance_query = process_instance_query.join(GroupModel, and_(*group_model_join_conditions)) process_instance_query = process_instance_query.join( UserGroupAssignmentModel,