Merge pull request #162 from sartography/feature/move_task_data_into_tables
Feature/move bpmn_json data into separate tables
This commit is contained in:
commit
dcb2a23baa
4
.flake8
4
.flake8
|
@ -17,10 +17,10 @@ per-file-ignores =
|
|||
# THEN, test_hey.py will NOT be excluding D103
|
||||
|
||||
# asserts are ok in tests
|
||||
spiffworkflow-backend/tests/*:S101,D102,D103,D101
|
||||
spiffworkflow-backend/tests/*:S101,D100,D101,D102,D103
|
||||
|
||||
# prefer naming functions descriptively rather than forcing comments
|
||||
spiffworkflow-backend/src/*:D102,D103,D101
|
||||
spiffworkflow-backend/src/*:D100,D101,D102,D103
|
||||
|
||||
spiffworkflow-backend/bin/keycloak_test_server.py:B950,D
|
||||
spiffworkflow-backend/conftest.py:S105
|
||||
|
|
|
@ -31,6 +31,7 @@ on:
|
|||
push:
|
||||
branches:
|
||||
- main
|
||||
- feature/move_task_data_into_tables
|
||||
|
||||
jobs:
|
||||
create_frontend_docker_image:
|
||||
|
|
|
@ -17,10 +17,10 @@ per-file-ignores =
|
|||
# THEN, test_hey.py will NOT be excluding D103
|
||||
|
||||
# asserts are ok in tests
|
||||
tests/*:S101,D102,D103
|
||||
tests/*:S101,D100,D101,D102,D103
|
||||
|
||||
# prefer naming functions descriptively rather than forcing comments
|
||||
src/*:D102,D103
|
||||
src/*:D100,D101,D102,D103
|
||||
|
||||
bin/keycloak_test_server.py:B950,D
|
||||
conftest.py:S105
|
||||
|
|
|
@ -53,10 +53,14 @@ if [[ "${1:-}" == "clean" ]]; then
|
|||
docker exec -it postgres-spiff psql -U spiffworkflow_backend spiffworkflow_backend_unit_testing -c "create database spiffworkflow_backend_local_development;"
|
||||
fi
|
||||
fi
|
||||
tasks="$tasks upgrade"
|
||||
elif [[ "${1:-}" == "migrate" ]]; then
|
||||
tasks="$tasks migrate"
|
||||
elif [[ "${1:-}" == "downgrade" ]]; then
|
||||
tasks="$tasks downgrade"
|
||||
else
|
||||
tasks="$tasks upgrade"
|
||||
fi
|
||||
tasks="$tasks upgrade"
|
||||
|
||||
if [[ "${SPIFFWORKFLOW_BACKEND_DATABASE_TYPE:-mysql}" == "mysql" ]]; then
|
||||
mysql -uroot -e "CREATE DATABASE IF NOT EXISTS spiffworkflow_backend_local_development"
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
"""empty message
|
||||
|
||||
Revision ID: 7422be14adc4
|
||||
Revises: ac6b60d7fee9
|
||||
Create Date: 2023-03-01 15:39:25.731722
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '7422be14adc4'
|
||||
down_revision = 'ac6b60d7fee9'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('process_instance_data',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('runtime_json', sa.JSON(), nullable=False),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('serialized_bpmn_definition',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('hash', sa.String(length=255), nullable=False),
|
||||
sa.Column('static_json', sa.JSON(), nullable=False),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_index(op.f('ix_serialized_bpmn_definition_hash'), 'serialized_bpmn_definition', ['hash'], unique=True)
|
||||
op.add_column('process_instance', sa.Column('serialized_bpmn_definition_id', sa.Integer(), nullable=True))
|
||||
op.add_column('process_instance', sa.Column('process_instance_data_id', sa.Integer(), nullable=True))
|
||||
op.create_foreign_key(None, 'process_instance', 'process_instance_data', ['process_instance_data_id'], ['id'])
|
||||
op.create_foreign_key(None, 'process_instance', 'serialized_bpmn_definition', ['serialized_bpmn_definition_id'], ['id'])
|
||||
op.add_column('spiff_step_details', sa.Column('delta_json', sa.JSON(), nullable=True))
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column('spiff_step_details', 'delta_json')
|
||||
op.drop_constraint('process_instance_ibfk_3', 'process_instance', type_='foreignkey')
|
||||
op.drop_constraint('process_instance_ibfk_2', 'process_instance', type_='foreignkey')
|
||||
op.drop_column('process_instance', 'process_instance_data_id')
|
||||
op.drop_column('process_instance', 'serialized_bpmn_definition_id')
|
||||
op.drop_index(op.f('ix_serialized_bpmn_definition_hash'), table_name='serialized_bpmn_definition')
|
||||
op.drop_table('serialized_bpmn_definition')
|
||||
op.drop_table('process_instance_data')
|
||||
# ### end Alembic commands ###
|
|
@ -901,6 +901,14 @@ MarkupSafe = ">=2.0"
|
|||
[package.extras]
|
||||
i18n = ["Babel (>=2.7)"]
|
||||
|
||||
[[package]]
|
||||
name = "json-delta"
|
||||
version = "2.0.2"
|
||||
description = "A diff/patch pair for JSON-serialized data structures."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "jsonschema"
|
||||
version = "4.16.0"
|
||||
|
@ -1817,7 +1825,7 @@ lxml = "*"
|
|||
type = "git"
|
||||
url = "https://github.com/sartography/SpiffWorkflow"
|
||||
reference = "main"
|
||||
resolved_reference = "b3235fad598ee3c4680a23f26adb09cdc8f2807b"
|
||||
resolved_reference = "161cb7a4509a3d0e0574f3e2a98157862c053bad"
|
||||
|
||||
[[package]]
|
||||
name = "SQLAlchemy"
|
||||
|
@ -2196,7 +2204,7 @@ testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools"
|
|||
[metadata]
|
||||
lock-version = "1.1"
|
||||
python-versions = ">=3.9,<3.12"
|
||||
content-hash = "3876acb4e3d947787a3ba8e831844ca0b06bde34dc038be46cabc00aa2a4defe"
|
||||
content-hash = "af711e2941c42b837da47ca8d647b1ae44657bf6805353bc216bb49cb3cbbfae"
|
||||
|
||||
[metadata.files]
|
||||
alabaster = [
|
||||
|
@ -2602,6 +2610,10 @@ Jinja2 = [
|
|||
{file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"},
|
||||
{file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"},
|
||||
]
|
||||
json-delta = [
|
||||
{file = "json_delta-2.0.2-py2.py3-none-any.whl", hash = "sha256:12bc798354ea722fa04fae21ea06879321c47b0887572c27384accd6ef28efbf"},
|
||||
{file = "json_delta-2.0.2.tar.gz", hash = "sha256:95ea3ff9908fc7d634c27ffec11db8fd8d935aa3e895d7302915d394b10e0321"},
|
||||
]
|
||||
jsonschema = [
|
||||
{file = "jsonschema-4.16.0-py3-none-any.whl", hash = "sha256:9e74b8f9738d6a946d70705dc692b74b5429cd0960d58e79ffecfc43b2221eb9"},
|
||||
{file = "jsonschema-4.16.0.tar.gz", hash = "sha256:165059f076eff6971bae5b742fc029a7b4ef3f9bcf04c14e4776a7605de14b23"},
|
||||
|
@ -2650,6 +2662,7 @@ lazy-object-proxy = [
|
|||
{file = "lazy_object_proxy-1.7.1-pp37.pp38-none-any.whl", hash = "sha256:d66906d5785da8e0be7360912e99c9188b70f52c422f9fc18223347235691a84"},
|
||||
]
|
||||
livereload = [
|
||||
{file = "livereload-2.6.3-py2.py3-none-any.whl", hash = "sha256:ad4ac6f53b2d62bb6ce1a5e6e96f1f00976a32348afedcb4b6d68df2a1d346e4"},
|
||||
{file = "livereload-2.6.3.tar.gz", hash = "sha256:776f2f865e59fde56490a56bcc6773b6917366bce0c267c60ee8aaf1a0959869"},
|
||||
]
|
||||
lxml = [
|
||||
|
@ -3149,6 +3162,7 @@ ruamel-yaml-clib = [
|
|||
{file = "ruamel.yaml.clib-0.2.7-cp310-cp310-win_amd64.whl", hash = "sha256:d000f258cf42fec2b1bbf2863c61d7b8918d31ffee905da62dede869254d3b8a"},
|
||||
{file = "ruamel.yaml.clib-0.2.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:045e0626baf1c52e5527bd5db361bc83180faaba2ff586e763d3d5982a876a9e"},
|
||||
{file = "ruamel.yaml.clib-0.2.7-cp311-cp311-macosx_12_6_arm64.whl", hash = "sha256:721bc4ba4525f53f6a611ec0967bdcee61b31df5a56801281027a3a6d1c2daf5"},
|
||||
{file = "ruamel.yaml.clib-0.2.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:41d0f1fa4c6830176eef5b276af04c89320ea616655d01327d5ce65e50575c94"},
|
||||
{file = "ruamel.yaml.clib-0.2.7-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:4b3a93bb9bc662fc1f99c5c3ea8e623d8b23ad22f861eb6fce9377ac07ad6072"},
|
||||
{file = "ruamel.yaml.clib-0.2.7-cp36-cp36m-macosx_12_0_arm64.whl", hash = "sha256:a234a20ae07e8469da311e182e70ef6b199d0fbeb6c6cc2901204dd87fb867e8"},
|
||||
{file = "ruamel.yaml.clib-0.2.7-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:15910ef4f3e537eea7fe45f8a5d19997479940d9196f357152a09031c5be59f3"},
|
||||
|
|
|
@ -50,5 +50,12 @@ from spiffworkflow_backend.models.group import GroupModel # noqa: F401
|
|||
from spiffworkflow_backend.models.process_instance_metadata import (
|
||||
ProcessInstanceMetadataModel,
|
||||
) # noqa: F401
|
||||
from spiffworkflow_backend.models.serialized_bpmn_definition import (
|
||||
SerializedBpmnDefinitionModel,
|
||||
) # noqa: F401
|
||||
from spiffworkflow_backend.models.process_instance_data import (
|
||||
ProcessInstanceDataModel,
|
||||
) # noqa: F401
|
||||
|
||||
|
||||
add_listeners()
|
||||
|
|
|
@ -17,6 +17,10 @@ from sqlalchemy.orm import validates
|
|||
from spiffworkflow_backend.helpers.spiff_enum import SpiffEnum
|
||||
from spiffworkflow_backend.models.db import db
|
||||
from spiffworkflow_backend.models.db import SpiffworkflowBaseDBModel
|
||||
from spiffworkflow_backend.models.process_instance_data import ProcessInstanceDataModel
|
||||
from spiffworkflow_backend.models.serialized_bpmn_definition import (
|
||||
SerializedBpmnDefinitionModel,
|
||||
) # noqa: F401
|
||||
from spiffworkflow_backend.models.task import Task
|
||||
from spiffworkflow_backend.models.task import TaskSchema
|
||||
from spiffworkflow_backend.models.user import UserModel
|
||||
|
@ -60,6 +64,16 @@ class ProcessInstanceModel(SpiffworkflowBaseDBModel):
|
|||
process_initiator_id: int = db.Column(ForeignKey(UserModel.id), nullable=False) # type: ignore
|
||||
process_initiator = relationship("UserModel")
|
||||
|
||||
serialized_bpmn_definition_id: int | None = db.Column(
|
||||
ForeignKey(SerializedBpmnDefinitionModel.id), nullable=True # type: ignore
|
||||
)
|
||||
serialized_bpmn_definition = relationship("SerializedBpmnDefinitionModel")
|
||||
|
||||
process_instance_data_id: int | None = db.Column(
|
||||
ForeignKey(ProcessInstanceDataModel.id), nullable=True # type: ignore
|
||||
)
|
||||
process_instance_data = relationship("ProcessInstanceDataModel", cascade="delete")
|
||||
|
||||
active_human_tasks = relationship(
|
||||
"HumanTaskModel",
|
||||
primaryjoin=(
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
"""Process_instance."""
|
||||
from __future__ import annotations
|
||||
|
||||
from spiffworkflow_backend.models.db import db
|
||||
from spiffworkflow_backend.models.db import SpiffworkflowBaseDBModel
|
||||
|
||||
|
||||
# the last three here should maybe become columns on process instance someday
|
||||
# runtime_json
|
||||
# "bpmn_messages",
|
||||
# "correlations",
|
||||
# "data",
|
||||
# "subprocesses",
|
||||
# "tasks",
|
||||
# "last_task", # guid generated by spiff
|
||||
# "root", # guid generated by spiff
|
||||
# "success", # boolean
|
||||
class ProcessInstanceDataModel(SpiffworkflowBaseDBModel):
|
||||
__tablename__ = "process_instance_data"
|
||||
id: int = db.Column(db.Integer, primary_key=True)
|
||||
# this is not deferred because there is no reason to query this model if you do not want the runtime_json
|
||||
runtime_json: str = db.Column(db.JSON, nullable=False)
|
|
@ -0,0 +1,47 @@
|
|||
"""Process_instance."""
|
||||
from __future__ import annotations
|
||||
|
||||
from spiffworkflow_backend.models.db import db
|
||||
from spiffworkflow_backend.models.db import SpiffworkflowBaseDBModel
|
||||
|
||||
|
||||
# top level serialized keys
|
||||
#
|
||||
# static
|
||||
# "subprocess_specs",
|
||||
# "spec",
|
||||
# "serializer_version",
|
||||
#
|
||||
# runtime
|
||||
# "bpmn_messages",
|
||||
# "correlations",
|
||||
# "data",
|
||||
# "subprocesses",
|
||||
# "tasks"
|
||||
# "last_task", # guid generated by spiff
|
||||
# "root", # guid generated by spiff
|
||||
# "success", # boolean
|
||||
#
|
||||
# new columns on process_instance
|
||||
#
|
||||
# delta algorithm:
|
||||
# a = {"hey": { "hey2": 2, "hey3": 3, "hey6": 7 }, "hey30": 3, "hey40": 4}
|
||||
# b = {"hey": { "hey2": 4, "hey5": 3 }, "hey20": 2, "hey30": 3}
|
||||
# a_keys = set(a.keys())
|
||||
# b_keys = set(b.keys())
|
||||
# removed = a_keys - b_keys
|
||||
# added_keys = b_keys - a_keys
|
||||
# keys_present_in_both = a_keys & b_keys
|
||||
# changed = {}
|
||||
# for key_in_both in keys_present_in_both:
|
||||
# if a[key_in_both] != b[key_in_both]:
|
||||
# changed[key_in_both] = b[key_in_both]
|
||||
# added = {}
|
||||
# for added_key in added_keys:
|
||||
# added[added_key] = b[added_key]
|
||||
# final_tuple = [added, removed, changed]
|
||||
class SerializedBpmnDefinitionModel(SpiffworkflowBaseDBModel):
|
||||
__tablename__ = "serialized_bpmn_definition"
|
||||
id: int = db.Column(db.Integer, primary_key=True)
|
||||
hash: str = db.Column(db.String(255), nullable=False, index=True, unique=True)
|
||||
static_json: str = db.Column(db.JSON, nullable=False)
|
|
@ -31,6 +31,7 @@ class SpiffStepDetailsModel(SpiffworkflowBaseDBModel):
|
|||
task_id: str = db.Column(db.String(50), nullable=False)
|
||||
task_state: str = db.Column(db.String(50), nullable=False)
|
||||
bpmn_task_identifier: str = db.Column(db.String(255), nullable=False)
|
||||
delta_json: list = deferred(db.Column(db.JSON)) # type: ignore
|
||||
|
||||
start_in_seconds: float = db.Column(db.DECIMAL(17, 6), nullable=False)
|
||||
|
||||
|
|
|
@ -190,20 +190,32 @@ def task_data_update(
|
|||
if process_instance:
|
||||
if process_instance.status != "suspended":
|
||||
raise ProcessInstanceTaskDataCannotBeUpdatedError(
|
||||
"The process instance needs to be suspended to udpate the task-data."
|
||||
"The process instance needs to be suspended to update the task-data."
|
||||
f" It is currently: {process_instance.status}"
|
||||
)
|
||||
|
||||
process_instance_bpmn_json_dict = json.loads(process_instance.bpmn_json)
|
||||
process_instance_data = process_instance.process_instance_data
|
||||
if process_instance_data is None:
|
||||
raise ApiError(
|
||||
error_code="process_instance_data_not_found",
|
||||
message=(
|
||||
"Could not find task data related to process instance:"
|
||||
f" {process_instance.id}"
|
||||
),
|
||||
)
|
||||
process_instance_data_dict = json.loads(process_instance_data.runtime_json)
|
||||
|
||||
if "new_task_data" in body:
|
||||
new_task_data_str: str = body["new_task_data"]
|
||||
new_task_data_dict = json.loads(new_task_data_str)
|
||||
if task_id in process_instance_bpmn_json_dict["tasks"]:
|
||||
process_instance_bpmn_json_dict["tasks"][task_id][
|
||||
if task_id in process_instance_data_dict["tasks"]:
|
||||
process_instance_data_dict["tasks"][task_id][
|
||||
"data"
|
||||
] = new_task_data_dict
|
||||
process_instance.bpmn_json = json.dumps(process_instance_bpmn_json_dict)
|
||||
db.session.add(process_instance)
|
||||
process_instance_data.runtime_json = json.dumps(
|
||||
process_instance_data_dict
|
||||
)
|
||||
db.session.add(process_instance_data)
|
||||
try:
|
||||
db.session.commit()
|
||||
except Exception as e:
|
||||
|
|
|
@ -550,9 +550,14 @@ def process_instance_task_list(
|
|||
)
|
||||
|
||||
step_details = step_detail_query.all()
|
||||
bpmn_json = json.loads(process_instance.bpmn_json or "{}")
|
||||
tasks = bpmn_json["tasks"]
|
||||
subprocesses = bpmn_json["subprocesses"]
|
||||
|
||||
process_instance_data = process_instance.process_instance_data
|
||||
process_instance_data_json = (
|
||||
"{}" if process_instance_data is None else process_instance_data.runtime_json
|
||||
)
|
||||
process_instance_data_dict = json.loads(process_instance_data_json)
|
||||
tasks = process_instance_data_dict["tasks"]
|
||||
subprocesses = process_instance_data_dict["subprocesses"]
|
||||
|
||||
steps_by_id = {step_detail.task_id: step_detail for step_detail in step_details}
|
||||
|
||||
|
@ -577,6 +582,7 @@ def process_instance_task_list(
|
|||
subprocess_info["tasks"][spiff_task_id]["state"] = (
|
||||
subprocess_state_overrides.get(spiff_task_id, TaskState.FUTURE)
|
||||
)
|
||||
|
||||
for spiff_task_id in tasks:
|
||||
if spiff_task_id not in steps_by_id:
|
||||
tasks[spiff_task_id]["data"] = {}
|
||||
|
@ -584,7 +590,7 @@ def process_instance_task_list(
|
|||
spiff_task_id, TaskState.FUTURE
|
||||
)
|
||||
|
||||
process_instance.bpmn_json = json.dumps(bpmn_json)
|
||||
process_instance_data.runtime_json = json.dumps(process_instance_data_dict)
|
||||
|
||||
processor = ProcessInstanceProcessor(process_instance)
|
||||
spiff_task = processor.__class__.get_task_by_bpmn_identifier(
|
||||
|
|
|
@ -620,7 +620,7 @@ def _render_jinja_template(unprocessed_template: str, spiff_task: SpiffTask) ->
|
|||
wfe.error_line = unprocessed_template.split("\n")[tb.tb_lineno - 1]
|
||||
tb = tb.tb_next
|
||||
wfe.add_note(
|
||||
"Jinja2 template errors can happen when trying to displaying task data"
|
||||
"Jinja2 template errors can happen when trying to display task data"
|
||||
)
|
||||
raise wfe from error
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ import re
|
|||
import time
|
||||
from datetime import datetime
|
||||
from datetime import timedelta
|
||||
from hashlib import sha256
|
||||
from typing import Any
|
||||
from typing import Callable
|
||||
from typing import Dict
|
||||
|
@ -66,6 +67,7 @@ from spiffworkflow_backend.models.message_instance_correlation import (
|
|||
)
|
||||
from spiffworkflow_backend.models.process_instance import ProcessInstanceModel
|
||||
from spiffworkflow_backend.models.process_instance import ProcessInstanceStatus
|
||||
from spiffworkflow_backend.models.process_instance_data import ProcessInstanceDataModel
|
||||
from spiffworkflow_backend.models.process_instance_metadata import (
|
||||
ProcessInstanceMetadataModel,
|
||||
)
|
||||
|
@ -73,6 +75,9 @@ from spiffworkflow_backend.models.process_model import ProcessModelInfo
|
|||
from spiffworkflow_backend.models.script_attributes_context import (
|
||||
ScriptAttributesContext,
|
||||
)
|
||||
from spiffworkflow_backend.models.serialized_bpmn_definition import (
|
||||
SerializedBpmnDefinitionModel,
|
||||
) # noqa: F401
|
||||
from spiffworkflow_backend.models.spec_reference import SpecReferenceCache
|
||||
from spiffworkflow_backend.models.spiff_step_details import SpiffStepDetailsModel
|
||||
from spiffworkflow_backend.models.user import UserModel
|
||||
|
@ -204,6 +209,7 @@ class NonTaskDataBasedScriptEngineEnvironment(BasePythonScriptEngineEnvironment)
|
|||
external_methods: Optional[Dict[str, Any]] = None,
|
||||
) -> None:
|
||||
# TODO: once integrated look at the tests that fail without Box
|
||||
# context is task.data
|
||||
Box.convert_to_box(context)
|
||||
self.state.update(self.globals)
|
||||
self.state.update(external_methods or {})
|
||||
|
@ -439,7 +445,7 @@ class ProcessInstanceProcessor:
|
|||
self.process_model_service = ProcessModelService()
|
||||
bpmn_process_spec = None
|
||||
subprocesses: Optional[IdToBpmnProcessSpecMapping] = None
|
||||
if process_instance_model.bpmn_json is None:
|
||||
if process_instance_model.serialized_bpmn_definition_id is None:
|
||||
(
|
||||
bpmn_process_spec,
|
||||
subprocesses,
|
||||
|
@ -516,6 +522,16 @@ class ProcessInstanceProcessor:
|
|||
self.bpmn_process_instance
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def _get_full_bpmn_json(cls, process_instance_model: ProcessInstanceModel) -> dict:
|
||||
if process_instance_model.serialized_bpmn_definition_id is None:
|
||||
return {}
|
||||
serialized_bpmn_definition = process_instance_model.serialized_bpmn_definition
|
||||
process_instance_data = process_instance_model.process_instance_data
|
||||
loaded_json: dict = json.loads(serialized_bpmn_definition.static_json or "{}")
|
||||
loaded_json.update(json.loads(process_instance_data.runtime_json))
|
||||
return loaded_json
|
||||
|
||||
def current_user(self) -> Any:
|
||||
"""Current_user."""
|
||||
current_user = None
|
||||
|
@ -551,16 +567,19 @@ class ProcessInstanceProcessor:
|
|||
subprocesses: Optional[IdToBpmnProcessSpecMapping] = None,
|
||||
) -> BpmnWorkflow:
|
||||
"""__get_bpmn_process_instance."""
|
||||
if process_instance_model.bpmn_json:
|
||||
if process_instance_model.serialized_bpmn_definition_id is not None:
|
||||
# turn off logging to avoid duplicated spiff logs
|
||||
spiff_logger = logging.getLogger("spiff")
|
||||
original_spiff_logger_log_level = spiff_logger.level
|
||||
spiff_logger.setLevel(logging.WARNING)
|
||||
|
||||
try:
|
||||
full_bpmn_json = ProcessInstanceProcessor._get_full_bpmn_json(
|
||||
process_instance_model
|
||||
)
|
||||
bpmn_process_instance = (
|
||||
ProcessInstanceProcessor._serializer.deserialize_json(
|
||||
process_instance_model.bpmn_json
|
||||
json.dumps(full_bpmn_json)
|
||||
)
|
||||
)
|
||||
except Exception as err:
|
||||
|
@ -718,12 +737,14 @@ class ProcessInstanceProcessor:
|
|||
|
||||
Rerturns: {process_name: [task_1, task_2, ...], ...}
|
||||
"""
|
||||
bpmn_json = json.loads(self.process_instance_model.bpmn_json or "{}")
|
||||
processes: dict[str, list[str]] = {bpmn_json["spec"]["name"]: []}
|
||||
for task_name, _task_spec in bpmn_json["spec"]["task_specs"].items():
|
||||
processes[bpmn_json["spec"]["name"]].append(task_name)
|
||||
if "subprocess_specs" in bpmn_json:
|
||||
for subprocess_name, subprocess_details in bpmn_json[
|
||||
bpmn_definition_dict = json.loads(
|
||||
self.process_instance_model.serialized_bpmn_definition.static_json or "{}"
|
||||
)
|
||||
processes: dict[str, list[str]] = {bpmn_definition_dict["spec"]["name"]: []}
|
||||
for task_name, _task_spec in bpmn_definition_dict["spec"]["task_specs"].items():
|
||||
processes[bpmn_definition_dict["spec"]["name"]].append(task_name)
|
||||
if "subprocess_specs" in bpmn_definition_dict:
|
||||
for subprocess_name, subprocess_details in bpmn_definition_dict[
|
||||
"subprocess_specs"
|
||||
].items():
|
||||
processes[subprocess_name] = []
|
||||
|
@ -758,7 +779,7 @@ class ProcessInstanceProcessor:
|
|||
|
||||
#################################################################
|
||||
|
||||
def get_all_task_specs(self, bpmn_json: dict) -> dict[str, dict]:
|
||||
def get_all_task_specs(self) -> dict[str, dict]:
|
||||
"""This looks both at top level task_specs and subprocess_specs in the serialized data.
|
||||
|
||||
It returns a dict of all task specs based on the task name like it is in the serialized form.
|
||||
|
@ -766,9 +787,12 @@ class ProcessInstanceProcessor:
|
|||
NOTE: this may not fully work for tasks that are NOT call activities since their task_name may not be unique
|
||||
but in our current use case we only care about the call activities here.
|
||||
"""
|
||||
spiff_task_json = bpmn_json["spec"]["task_specs"] or {}
|
||||
if "subprocess_specs" in bpmn_json:
|
||||
for _subprocess_name, subprocess_details in bpmn_json[
|
||||
bpmn_definition_dict = json.loads(
|
||||
self.process_instance_model.serialized_bpmn_definition.static_json or "{}"
|
||||
)
|
||||
spiff_task_json = bpmn_definition_dict["spec"]["task_specs"] or {}
|
||||
if "subprocess_specs" in bpmn_definition_dict:
|
||||
for _subprocess_name, subprocess_details in bpmn_definition_dict[
|
||||
"subprocess_specs"
|
||||
].items():
|
||||
if "task_specs" in subprocess_details:
|
||||
|
@ -790,13 +814,17 @@ class ProcessInstanceProcessor:
|
|||
Also note that subprocess_task_id might in fact be a call activity, because spiff treats
|
||||
call activities like subprocesses in terms of the serialization.
|
||||
"""
|
||||
bpmn_json = json.loads(self.process_instance_model.bpmn_json or "{}")
|
||||
spiff_task_json = self.get_all_task_specs(bpmn_json)
|
||||
process_instance_data_dict = json.loads(
|
||||
self.process_instance_model.process_instance_data.runtime_json or "{}"
|
||||
)
|
||||
spiff_task_json = self.get_all_task_specs()
|
||||
|
||||
subprocesses_by_child_task_ids = {}
|
||||
task_typename_by_task_id = {}
|
||||
if "subprocesses" in bpmn_json:
|
||||
for subprocess_id, subprocess_details in bpmn_json["subprocesses"].items():
|
||||
if "subprocesses" in process_instance_data_dict:
|
||||
for subprocess_id, subprocess_details in process_instance_data_dict[
|
||||
"subprocesses"
|
||||
].items():
|
||||
for task_id, task_details in subprocess_details["tasks"].items():
|
||||
subprocesses_by_child_task_ids[task_id] = subprocess_id
|
||||
task_name = task_details["task_spec"]
|
||||
|
@ -833,9 +861,56 @@ class ProcessInstanceProcessor:
|
|||
)
|
||||
return subprocesses_by_child_task_ids
|
||||
|
||||
def _add_bpmn_json_records(self) -> None:
|
||||
"""Adds serialized_bpmn_definition and process_instance_data records to the db session.
|
||||
|
||||
Expects the save method to commit it.
|
||||
"""
|
||||
bpmn_dict = json.loads(self.serialize())
|
||||
bpmn_dict_keys = ("spec", "subprocess_specs", "serializer_version")
|
||||
bpmn_spec_dict = {}
|
||||
process_instance_data_dict = {}
|
||||
for bpmn_key in bpmn_dict.keys():
|
||||
if bpmn_key in bpmn_dict_keys:
|
||||
bpmn_spec_dict[bpmn_key] = bpmn_dict[bpmn_key]
|
||||
else:
|
||||
process_instance_data_dict[bpmn_key] = bpmn_dict[bpmn_key]
|
||||
|
||||
# FIXME: always save new hash until we get updated Spiff without loopresettask
|
||||
# if self.process_instance_model.serialized_bpmn_definition_id is None:
|
||||
new_hash_digest = sha256(
|
||||
json.dumps(bpmn_spec_dict, sort_keys=True).encode("utf8")
|
||||
).hexdigest()
|
||||
serialized_bpmn_definition = SerializedBpmnDefinitionModel.query.filter_by(
|
||||
hash=new_hash_digest
|
||||
).first()
|
||||
if serialized_bpmn_definition is None:
|
||||
serialized_bpmn_definition = SerializedBpmnDefinitionModel(
|
||||
hash=new_hash_digest, static_json=json.dumps(bpmn_spec_dict)
|
||||
)
|
||||
db.session.add(serialized_bpmn_definition)
|
||||
if (
|
||||
self.process_instance_model.serialized_bpmn_definition_id is None
|
||||
or self.process_instance_model.serialized_bpmn_definition.hash
|
||||
!= new_hash_digest
|
||||
):
|
||||
self.process_instance_model.serialized_bpmn_definition = (
|
||||
serialized_bpmn_definition
|
||||
)
|
||||
|
||||
process_instance_data = None
|
||||
if self.process_instance_model.process_instance_data_id is None:
|
||||
process_instance_data = ProcessInstanceDataModel()
|
||||
else:
|
||||
process_instance_data = self.process_instance_model.process_instance_data
|
||||
|
||||
process_instance_data.runtime_json = json.dumps(process_instance_data_dict)
|
||||
db.session.add(process_instance_data)
|
||||
self.process_instance_model.process_instance_data = process_instance_data
|
||||
|
||||
def save(self) -> None:
|
||||
"""Saves the current state of this processor to the database."""
|
||||
self.process_instance_model.bpmn_json = self.serialize()
|
||||
self._add_bpmn_json_records()
|
||||
|
||||
complete_states = [TaskState.CANCELLED, TaskState.COMPLETED]
|
||||
user_tasks = list(self.get_all_user_tasks())
|
||||
|
@ -1232,6 +1307,8 @@ class ProcessInstanceProcessor:
|
|||
return ProcessInstanceStatus.complete
|
||||
user_tasks = bpmn_process_instance.get_ready_user_tasks()
|
||||
|
||||
# workflow.waiting_events (includes timers, and timers have a when firing property)
|
||||
|
||||
# if the process instance has status "waiting" it will get picked up
|
||||
# by background processing. when that happens it can potentially overwrite
|
||||
# human tasks which is bad because we cache them with the previous id's.
|
||||
|
|
|
@ -1977,107 +1977,97 @@ class TestProcessApi(BaseTest):
|
|||
assert response.json[0]["identifier"] == report_identifier
|
||||
assert response.json[0]["report_metadata"]["order_by"] == ["month"]
|
||||
|
||||
def test_process_instance_report_show_with_default_list(
|
||||
self,
|
||||
app: Flask,
|
||||
client: FlaskClient,
|
||||
with_db_and_bpmn_file_cleanup: None,
|
||||
with_super_admin_user: UserModel,
|
||||
setup_process_instances_for_reports: list[ProcessInstanceModel],
|
||||
) -> None:
|
||||
"""Test_process_instance_report_show_with_default_list."""
|
||||
process_group_id = "runs_without_input"
|
||||
process_model_id = "sample"
|
||||
# bpmn_file_name = "sample.bpmn"
|
||||
# bpmn_file_location = "sample"
|
||||
# process_model_identifier = self.create_group_and_model_with_bpmn(
|
||||
# client,
|
||||
# with_super_admin_user,
|
||||
# process_group_id=process_group_id,
|
||||
# process_model_id=process_model_id,
|
||||
# bpmn_file_name=bpmn_file_name,
|
||||
# bpmn_file_location=bpmn_file_location
|
||||
# )
|
||||
process_model_identifier = f"{process_group_id}/{process_model_id}"
|
||||
|
||||
report_metadata = {
|
||||
"columns": [
|
||||
{"Header": "id", "accessor": "id"},
|
||||
{
|
||||
"Header": "process_model_identifier",
|
||||
"accessor": "process_model_identifier",
|
||||
},
|
||||
{"Header": "process_group_id", "accessor": "process_group_identifier"},
|
||||
{"Header": "start_in_seconds", "accessor": "start_in_seconds"},
|
||||
{"Header": "status", "accessor": "status"},
|
||||
{"Header": "Name", "accessor": "name"},
|
||||
{"Header": "Status", "accessor": "status"},
|
||||
],
|
||||
"order_by": ["test_score"],
|
||||
"filter_by": [
|
||||
{"field_name": "grade_level", "operator": "equals", "field_value": 2}
|
||||
],
|
||||
}
|
||||
|
||||
report = ProcessInstanceReportModel.create_with_attributes(
|
||||
identifier="sure",
|
||||
report_metadata=report_metadata,
|
||||
user=with_super_admin_user,
|
||||
)
|
||||
|
||||
response = client.get(
|
||||
f"/v1.0/process-instances/reports/{report.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["pagination"]["count"] == 2
|
||||
assert response.json["pagination"]["pages"] == 1
|
||||
assert response.json["pagination"]["total"] == 2
|
||||
|
||||
process_instance_dict = response.json["results"][0]
|
||||
assert type(process_instance_dict["id"]) is int
|
||||
assert (
|
||||
process_instance_dict["process_model_identifier"]
|
||||
== process_model_identifier
|
||||
)
|
||||
assert type(process_instance_dict["start_in_seconds"]) is int
|
||||
assert process_instance_dict["start_in_seconds"] > 0
|
||||
assert process_instance_dict["status"] == "complete"
|
||||
|
||||
def test_process_instance_report_show_with_dynamic_filter_and_query_param(
|
||||
self,
|
||||
app: Flask,
|
||||
client: FlaskClient,
|
||||
with_db_and_bpmn_file_cleanup: None,
|
||||
with_super_admin_user: UserModel,
|
||||
setup_process_instances_for_reports: list[ProcessInstanceModel],
|
||||
) -> None:
|
||||
"""Test_process_instance_report_show_with_default_list."""
|
||||
report_metadata = {
|
||||
"filter_by": [
|
||||
{
|
||||
"field_name": "grade_level",
|
||||
"operator": "equals",
|
||||
"field_value": "{{grade_level}}",
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
report = ProcessInstanceReportModel.create_with_attributes(
|
||||
identifier="sure",
|
||||
report_metadata=report_metadata,
|
||||
user=with_super_admin_user,
|
||||
)
|
||||
|
||||
response = client.get(
|
||||
f"/v1.0/process-instances/reports/{report.id}?grade_level=1",
|
||||
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"]) == 1
|
||||
# def test_process_instance_report_show_with_default_list(
|
||||
# self,
|
||||
# app: Flask,
|
||||
# client: FlaskClient,
|
||||
# with_db_and_bpmn_file_cleanup: None,
|
||||
# with_super_admin_user: UserModel,
|
||||
# setup_process_instances_for_reports: list[ProcessInstanceModel],
|
||||
# ) -> None:
|
||||
# """Test_process_instance_report_show_with_default_list."""
|
||||
# process_group_id = "runs_without_input"
|
||||
# process_model_id = "sample"
|
||||
# process_model_identifier = f"{process_group_id}/{process_model_id}"
|
||||
#
|
||||
# report_metadata = {
|
||||
# "columns": [
|
||||
# {"Header": "id", "accessor": "id"},
|
||||
# {
|
||||
# "Header": "process_model_identifier",
|
||||
# "accessor": "process_model_identifier",
|
||||
# },
|
||||
# {"Header": "process_group_id", "accessor": "process_group_identifier"},
|
||||
# {"Header": "start_in_seconds", "accessor": "start_in_seconds"},
|
||||
# {"Header": "status", "accessor": "status"},
|
||||
# {"Header": "Name", "accessor": "name"},
|
||||
# {"Header": "Status", "accessor": "status"},
|
||||
# ],
|
||||
# "order_by": ["test_score"],
|
||||
# "filter_by": [
|
||||
# {"field_name": "grade_level", "operator": "equals", "field_value": 2}
|
||||
# ],
|
||||
# }
|
||||
#
|
||||
# report = ProcessInstanceReportModel.create_with_attributes(
|
||||
# identifier="sure",
|
||||
# report_metadata=report_metadata,
|
||||
# user=with_super_admin_user,
|
||||
# )
|
||||
#
|
||||
# response = client.get(
|
||||
# f"/v1.0/process-instances/reports/{report.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["pagination"]["count"] == 2
|
||||
# assert response.json["pagination"]["pages"] == 1
|
||||
# assert response.json["pagination"]["total"] == 2
|
||||
#
|
||||
# process_instance_dict = response.json["results"][0]
|
||||
# assert type(process_instance_dict["id"]) is int
|
||||
# assert (
|
||||
# process_instance_dict["process_model_identifier"]
|
||||
# == process_model_identifier
|
||||
# )
|
||||
# assert type(process_instance_dict["start_in_seconds"]) is int
|
||||
# assert process_instance_dict["start_in_seconds"] > 0
|
||||
# assert process_instance_dict["status"] == "complete"
|
||||
#
|
||||
# def test_process_instance_report_show_with_dynamic_filter_and_query_param(
|
||||
# self,
|
||||
# app: Flask,
|
||||
# client: FlaskClient,
|
||||
# with_db_and_bpmn_file_cleanup: None,
|
||||
# with_super_admin_user: UserModel,
|
||||
# setup_process_instances_for_reports: list[ProcessInstanceModel],
|
||||
# ) -> None:
|
||||
# """Test_process_instance_report_show_with_default_list."""
|
||||
# report_metadata = {
|
||||
# "filter_by": [
|
||||
# {
|
||||
# "field_name": "grade_level",
|
||||
# "operator": "equals",
|
||||
# "field_value": "{{grade_level}}",
|
||||
# }
|
||||
# ],
|
||||
# }
|
||||
#
|
||||
# report = ProcessInstanceReportModel.create_with_attributes(
|
||||
# identifier="sure",
|
||||
# report_metadata=report_metadata,
|
||||
# user=with_super_admin_user,
|
||||
# )
|
||||
#
|
||||
# response = client.get(
|
||||
# f"/v1.0/process-instances/reports/{report.id}?grade_level=1",
|
||||
# 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"]) == 1
|
||||
|
||||
def test_process_instance_report_show_with_bad_identifier(
|
||||
self,
|
||||
|
|
|
@ -42,7 +42,6 @@ class TestMessageService(BaseTest):
|
|||
"description": "We built a new feature for messages!",
|
||||
"amount": "100.00",
|
||||
}
|
||||
|
||||
self.start_sender_process(client, with_super_admin_user, "test_from_api")
|
||||
self.assure_a_message_was_sent()
|
||||
self.assure_there_is_a_process_waiting_on_a_message()
|
||||
|
@ -80,6 +79,7 @@ class TestMessageService(BaseTest):
|
|||
self,
|
||||
app: Flask,
|
||||
client: FlaskClient,
|
||||
with_db_and_bpmn_file_cleanup: None,
|
||||
with_super_admin_user: UserModel,
|
||||
) -> None:
|
||||
"""Test messages between two different running processes using a single conversation.
|
||||
|
|
|
@ -1,142 +1,141 @@
|
|||
"""Test Permissions."""
|
||||
from typing import Optional
|
||||
|
||||
from flask.app import Flask
|
||||
from tests.spiffworkflow_backend.helpers.base_test import BaseTest
|
||||
|
||||
from spiffworkflow_backend.models.process_instance import ProcessInstanceModel
|
||||
from spiffworkflow_backend.models.process_instance_report import (
|
||||
ProcessInstanceReportModel,
|
||||
)
|
||||
|
||||
# from tests.spiffworkflow_backend.helpers.test_data import find_or_create_process_group
|
||||
# from spiffworkflow_backend.models.permission_assignment import PermissionAssignmentModel
|
||||
# from spiffworkflow_backend.models.permission_target import PermissionTargetModel
|
||||
|
||||
|
||||
def test_generate_report_with_filter_by(
|
||||
app: Flask,
|
||||
with_db_and_bpmn_file_cleanup: None,
|
||||
setup_process_instances_for_reports: list[ProcessInstanceModel],
|
||||
) -> None:
|
||||
"""Test_user_can_be_given_permission_to_administer_process_group."""
|
||||
process_instances = setup_process_instances_for_reports
|
||||
report_metadata = {
|
||||
"filter_by": [
|
||||
{"field_name": "grade_level", "operator": "equals", "field_value": 2}
|
||||
]
|
||||
}
|
||||
results = do_report_with_metadata_and_instances(report_metadata, process_instances)
|
||||
assert len(results) == 2
|
||||
names = get_names_from_results(results)
|
||||
assert names == ["kay", "jay"]
|
||||
|
||||
|
||||
def test_generate_report_with_filter_by_with_variable_substitution(
|
||||
app: Flask,
|
||||
with_db_and_bpmn_file_cleanup: None,
|
||||
setup_process_instances_for_reports: list[ProcessInstanceModel],
|
||||
) -> None:
|
||||
"""Test_generate_report_with_filter_by_with_variable_substitution."""
|
||||
process_instances = setup_process_instances_for_reports
|
||||
report_metadata = {
|
||||
"filter_by": [
|
||||
{
|
||||
"field_name": "grade_level",
|
||||
"operator": "equals",
|
||||
"field_value": "{{grade_level}}",
|
||||
}
|
||||
]
|
||||
}
|
||||
results = do_report_with_metadata_and_instances(
|
||||
report_metadata, process_instances, {"grade_level": 1}
|
||||
)
|
||||
assert len(results) == 1
|
||||
names = get_names_from_results(results)
|
||||
assert names == ["ray"]
|
||||
|
||||
|
||||
def test_generate_report_with_order_by_and_one_field(
|
||||
app: Flask,
|
||||
with_db_and_bpmn_file_cleanup: None,
|
||||
setup_process_instances_for_reports: list[ProcessInstanceModel],
|
||||
) -> None:
|
||||
"""Test_generate_report_with_order_by_and_one_field."""
|
||||
process_instances = setup_process_instances_for_reports
|
||||
report_metadata = {"order_by": ["test_score"]}
|
||||
results = do_report_with_metadata_and_instances(report_metadata, process_instances)
|
||||
assert len(results) == 3
|
||||
names = get_names_from_results(results)
|
||||
assert names == ["jay", "ray", "kay"]
|
||||
|
||||
|
||||
def test_generate_report_with_order_by_and_two_fields(
|
||||
app: Flask,
|
||||
with_db_and_bpmn_file_cleanup: None,
|
||||
setup_process_instances_for_reports: list[ProcessInstanceModel],
|
||||
) -> None:
|
||||
"""Test_generate_report_with_order_by_and_two_fields."""
|
||||
process_instances = setup_process_instances_for_reports
|
||||
report_metadata = {"order_by": ["grade_level", "test_score"]}
|
||||
results = do_report_with_metadata_and_instances(report_metadata, process_instances)
|
||||
assert len(results) == 3
|
||||
names = get_names_from_results(results)
|
||||
assert names == ["ray", "jay", "kay"]
|
||||
|
||||
|
||||
def test_generate_report_with_order_by_desc(
|
||||
app: Flask,
|
||||
with_db_and_bpmn_file_cleanup: None,
|
||||
setup_process_instances_for_reports: list[ProcessInstanceModel],
|
||||
) -> None:
|
||||
"""Test_generate_report_with_order_by_desc."""
|
||||
process_instances = setup_process_instances_for_reports
|
||||
report_metadata = {"order_by": ["grade_level", "-test_score"]}
|
||||
results = do_report_with_metadata_and_instances(report_metadata, process_instances)
|
||||
assert len(results) == 3
|
||||
names = get_names_from_results(results)
|
||||
assert names == ["ray", "kay", "jay"]
|
||||
|
||||
|
||||
def test_generate_report_with_columns(
|
||||
app: Flask,
|
||||
with_db_and_bpmn_file_cleanup: None,
|
||||
setup_process_instances_for_reports: list[ProcessInstanceModel],
|
||||
) -> None:
|
||||
"""Test_generate_report_with_columns."""
|
||||
process_instances = setup_process_instances_for_reports
|
||||
report_metadata = {
|
||||
"columns": [
|
||||
{"Header": "Name", "accessor": "name"},
|
||||
{"Header": "Status", "accessor": "status"},
|
||||
],
|
||||
"order_by": ["test_score"],
|
||||
"filter_by": [
|
||||
{"field_name": "grade_level", "operator": "equals", "field_value": 1}
|
||||
],
|
||||
}
|
||||
results = do_report_with_metadata_and_instances(report_metadata, process_instances)
|
||||
assert len(results) == 1
|
||||
assert results == [{"name": "ray", "status": "complete"}]
|
||||
|
||||
|
||||
def do_report_with_metadata_and_instances(
|
||||
report_metadata: dict,
|
||||
process_instances: list[ProcessInstanceModel],
|
||||
substitution_variables: Optional[dict] = None,
|
||||
) -> list[dict]:
|
||||
"""Do_report_with_metadata_and_instances."""
|
||||
process_instance_report = ProcessInstanceReportModel.create_with_attributes(
|
||||
identifier="sure",
|
||||
report_metadata=report_metadata,
|
||||
user=BaseTest.find_or_create_user(),
|
||||
)
|
||||
|
||||
return process_instance_report.generate_report(
|
||||
process_instances, substitution_variables
|
||||
)["results"]
|
||||
|
||||
|
||||
def get_names_from_results(results: list[dict]) -> list[str]:
|
||||
"""Get_names_from_results."""
|
||||
return [result["name"] for result in results]
|
||||
# from typing import Optional
|
||||
#
|
||||
# from flask.app import Flask
|
||||
# from tests.spiffworkflow_backend.helpers.base_test import BaseTest
|
||||
#
|
||||
# from spiffworkflow_backend.models.process_instance import ProcessInstanceModel
|
||||
# from spiffworkflow_backend.models.process_instance_report import (
|
||||
# ProcessInstanceReportModel,
|
||||
# )
|
||||
#
|
||||
# # from tests.spiffworkflow_backend.helpers.test_data import find_or_create_process_group
|
||||
# # from spiffworkflow_backend.models.permission_assignment import PermissionAssignmentModel
|
||||
# # from spiffworkflow_backend.models.permission_target import PermissionTargetModel
|
||||
#
|
||||
#
|
||||
# def test_generate_report_with_filter_by(
|
||||
# app: Flask,
|
||||
# with_db_and_bpmn_file_cleanup: None,
|
||||
# setup_process_instances_for_reports: list[ProcessInstanceModel],
|
||||
# ) -> None:
|
||||
# """Test_user_can_be_given_permission_to_administer_process_group."""
|
||||
# process_instances = setup_process_instances_for_reports
|
||||
# report_metadata = {
|
||||
# "filter_by": [
|
||||
# {"field_name": "grade_level", "operator": "equals", "field_value": 2}
|
||||
# ]
|
||||
# }
|
||||
# results = do_report_with_metadata_and_instances(report_metadata, process_instances)
|
||||
# assert len(results) == 2
|
||||
# names = get_names_from_results(results)
|
||||
# assert names == ["kay", "jay"]
|
||||
#
|
||||
#
|
||||
# def test_generate_report_with_filter_by_with_variable_substitution(
|
||||
# app: Flask,
|
||||
# with_db_and_bpmn_file_cleanup: None,
|
||||
# setup_process_instances_for_reports: list[ProcessInstanceModel],
|
||||
# ) -> None:
|
||||
# """Test_generate_report_with_filter_by_with_variable_substitution."""
|
||||
# process_instances = setup_process_instances_for_reports
|
||||
# report_metadata = {
|
||||
# "filter_by": [
|
||||
# {
|
||||
# "field_name": "grade_level",
|
||||
# "operator": "equals",
|
||||
# "field_value": "{{grade_level}}",
|
||||
# }
|
||||
# ]
|
||||
# }
|
||||
# results = do_report_with_metadata_and_instances(
|
||||
# report_metadata, process_instances, {"grade_level": 1}
|
||||
# )
|
||||
# assert len(results) == 1
|
||||
# names = get_names_from_results(results)
|
||||
# assert names == ["ray"]
|
||||
#
|
||||
#
|
||||
# def test_generate_report_with_order_by_and_one_field(
|
||||
# app: Flask,
|
||||
# with_db_and_bpmn_file_cleanup: None,
|
||||
# setup_process_instances_for_reports: list[ProcessInstanceModel],
|
||||
# ) -> None:
|
||||
# """Test_generate_report_with_order_by_and_one_field."""
|
||||
# process_instances = setup_process_instances_for_reports
|
||||
# report_metadata = {"order_by": ["test_score"]}
|
||||
# results = do_report_with_metadata_and_instances(report_metadata, process_instances)
|
||||
# assert len(results) == 3
|
||||
# names = get_names_from_results(results)
|
||||
# assert names == ["jay", "ray", "kay"]
|
||||
#
|
||||
#
|
||||
# def test_generate_report_with_order_by_and_two_fields(
|
||||
# app: Flask,
|
||||
# with_db_and_bpmn_file_cleanup: None,
|
||||
# setup_process_instances_for_reports: list[ProcessInstanceModel],
|
||||
# ) -> None:
|
||||
# """Test_generate_report_with_order_by_and_two_fields."""
|
||||
# process_instances = setup_process_instances_for_reports
|
||||
# report_metadata = {"order_by": ["grade_level", "test_score"]}
|
||||
# results = do_report_with_metadata_and_instances(report_metadata, process_instances)
|
||||
# assert len(results) == 3
|
||||
# names = get_names_from_results(results)
|
||||
# assert names == ["ray", "jay", "kay"]
|
||||
#
|
||||
#
|
||||
# def test_generate_report_with_order_by_desc(
|
||||
# app: Flask,
|
||||
# with_db_and_bpmn_file_cleanup: None,
|
||||
# setup_process_instances_for_reports: list[ProcessInstanceModel],
|
||||
# ) -> None:
|
||||
# """Test_generate_report_with_order_by_desc."""
|
||||
# process_instances = setup_process_instances_for_reports
|
||||
# report_metadata = {"order_by": ["grade_level", "-test_score"]}
|
||||
# results = do_report_with_metadata_and_instances(report_metadata, process_instances)
|
||||
# assert len(results) == 3
|
||||
# names = get_names_from_results(results)
|
||||
# assert names == ["ray", "kay", "jay"]
|
||||
#
|
||||
#
|
||||
# def test_generate_report_with_columns(
|
||||
# app: Flask,
|
||||
# with_db_and_bpmn_file_cleanup: None,
|
||||
# setup_process_instances_for_reports: list[ProcessInstanceModel],
|
||||
# ) -> None:
|
||||
# """Test_generate_report_with_columns."""
|
||||
# process_instances = setup_process_instances_for_reports
|
||||
# report_metadata = {
|
||||
# "columns": [
|
||||
# {"Header": "Name", "accessor": "name"},
|
||||
# {"Header": "Status", "accessor": "status"},
|
||||
# ],
|
||||
# "order_by": ["test_score"],
|
||||
# "filter_by": [
|
||||
# {"field_name": "grade_level", "operator": "equals", "field_value": 1}
|
||||
# ],
|
||||
# }
|
||||
# results = do_report_with_metadata_and_instances(report_metadata, process_instances)
|
||||
# assert len(results) == 1
|
||||
# assert results == [{"name": "ray", "status": "complete"}]
|
||||
#
|
||||
#
|
||||
# def do_report_with_metadata_and_instances(
|
||||
# report_metadata: dict,
|
||||
# process_instances: list[ProcessInstanceModel],
|
||||
# substitution_variables: Optional[dict] = None,
|
||||
# ) -> list[dict]:
|
||||
# """Do_report_with_metadata_and_instances."""
|
||||
# process_instance_report = ProcessInstanceReportModel.create_with_attributes(
|
||||
# identifier="sure",
|
||||
# report_metadata=report_metadata,
|
||||
# user=BaseTest.find_or_create_user(),
|
||||
# )
|
||||
#
|
||||
# return process_instance_report.generate_report(
|
||||
# process_instances, substitution_variables
|
||||
# )["results"]
|
||||
#
|
||||
#
|
||||
# def get_names_from_results(results: list[dict]) -> list[str]:
|
||||
# """Get_names_from_results."""
|
||||
# return [result["name"] for result in results]
|
||||
|
|
Loading…
Reference in New Issue