Improvement/flexible task iteration (#507)
* updated to use new spiff branch and fixed broken tests w/ burnettk essweine * updated spiffworkflow with new main build w/ burnettk essweine * initial updates to new spiff branch w/ burnettk essweine * more updates for new spiff w/ burnettk essweine * fixed some linting issues w/ burnettk essweine * fixed some failing tests w/ burnettk * updated spiffworkflow for cancel fix w/ burnettk * Improvement/flexible task iteration 2 (#504) * wip * consistent failure, mostly * removing test code and tests * removing unused test bpmn files * removing unused test bpmn files * minor cleanup of commented code * spaces and unused imports * go back to spiff on main --------- Co-authored-by: burnettk <burnettk@users.noreply.github.com> * lint * updated test to reflect storing predicted tasks w/ burnettk * add some orders so postgres does not do whatever it wants, and clear log * fix lint --------- Co-authored-by: jasquat <jasquat@users.noreply.github.com> Co-authored-by: danfunk <daniel.h.funk@gmail.com> Co-authored-by: burnettk <burnettk@users.noreply.github.com>
This commit is contained in:
parent
c986dbb9e5
commit
1826cc4b6c
|
@ -70,11 +70,19 @@ if [[ "$subcommand" != "pre" ]] || [[ -n "$(git status --porcelain "spiffworkflo
|
||||||
run_pre_commmit || run_pre_commmit
|
run_pre_commmit || run_pre_commmit
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
function clear_log_file() {
|
||||||
|
unit_testing_log_file="./log/unit_testing.log"
|
||||||
|
if [[ -f "$unit_testing_log_file" ]]; then
|
||||||
|
> "$unit_testing_log_file"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
for python_project in "${python_projects[@]}"; do
|
for python_project in "${python_projects[@]}"; do
|
||||||
if [[ "$subcommand" != "pre" ]] || [[ -n "$(git status --porcelain "$python_project")" ]]; then
|
if [[ "$subcommand" != "pre" ]] || [[ -n "$(git status --porcelain "$python_project")" ]]; then
|
||||||
pushd "$python_project"
|
pushd "$python_project"
|
||||||
poetry install
|
poetry install
|
||||||
poetry run mypy $(get_python_dirs)
|
poetry run mypy $(get_python_dirs)
|
||||||
|
clear_log_file
|
||||||
./bin/tests-par
|
./bin/tests-par
|
||||||
popd
|
popd
|
||||||
fi
|
fi
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand.
|
# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand.
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "alembic"
|
name = "alembic"
|
||||||
|
@ -2366,7 +2366,7 @@ lxml = "*"
|
||||||
type = "git"
|
type = "git"
|
||||||
url = "https://github.com/sartography/SpiffWorkflow"
|
url = "https://github.com/sartography/SpiffWorkflow"
|
||||||
reference = "main"
|
reference = "main"
|
||||||
resolved_reference = "f113aba0f59f18e9dce7263289c441a28a83b4c6"
|
resolved_reference = "90159bd23c3c74fcf480414473e851460a01e92c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sqlalchemy"
|
name = "sqlalchemy"
|
||||||
|
@ -2419,7 +2419,7 @@ files = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
greenlet = {version = "!=0.4.17", markers = "platform_machine == \"win32\" or platform_machine == \"WIN32\" or platform_machine == \"AMD64\" or platform_machine == \"amd64\" or platform_machine == \"x86_64\" or platform_machine == \"ppc64le\" or platform_machine == \"aarch64\""}
|
greenlet = {version = "!=0.4.17", markers = "platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\""}
|
||||||
typing-extensions = ">=4.2.0"
|
typing-extensions = ">=4.2.0"
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
|
|
|
@ -8,7 +8,7 @@ from typing import Any
|
||||||
import marshmallow
|
import marshmallow
|
||||||
from marshmallow import Schema
|
from marshmallow import Schema
|
||||||
from marshmallow_enum import EnumField # type: ignore
|
from marshmallow_enum import EnumField # type: ignore
|
||||||
from SpiffWorkflow.task import TaskStateNames # type: ignore
|
from SpiffWorkflow.util.task import TaskState # type: ignore
|
||||||
from sqlalchemy import ForeignKey
|
from sqlalchemy import ForeignKey
|
||||||
from sqlalchemy.orm import relationship
|
from sqlalchemy.orm import relationship
|
||||||
|
|
||||||
|
@ -232,9 +232,8 @@ class Task:
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def task_state_name_to_int(cls, task_state_name: str) -> int:
|
def task_state_name_to_int(cls, task_state_name: str) -> int:
|
||||||
task_state_integers = {v: k for k, v in TaskStateNames.items()}
|
state_value: int = TaskState.get_value(task_state_name)
|
||||||
task_state_int: int = task_state_integers[task_state_name]
|
return state_value
|
||||||
return task_state_int
|
|
||||||
|
|
||||||
|
|
||||||
class OptionSchema(Schema):
|
class OptionSchema(Schema):
|
||||||
|
|
|
@ -18,7 +18,7 @@ from MySQLdb import OperationalError # type: ignore
|
||||||
from SpiffWorkflow.bpmn.exceptions import WorkflowTaskException # type: ignore
|
from SpiffWorkflow.bpmn.exceptions import WorkflowTaskException # type: ignore
|
||||||
from SpiffWorkflow.bpmn.workflow import BpmnWorkflow # type: ignore
|
from SpiffWorkflow.bpmn.workflow import BpmnWorkflow # type: ignore
|
||||||
from SpiffWorkflow.task import Task as SpiffTask # type: ignore
|
from SpiffWorkflow.task import Task as SpiffTask # type: ignore
|
||||||
from SpiffWorkflow.task import TaskState
|
from SpiffWorkflow.util.task import TaskState # type: ignore
|
||||||
from sqlalchemy import and_
|
from sqlalchemy import and_
|
||||||
from sqlalchemy import asc
|
from sqlalchemy import asc
|
||||||
from sqlalchemy import desc
|
from sqlalchemy import desc
|
||||||
|
@ -493,7 +493,7 @@ def _interstitial_stream(
|
||||||
) -> Generator[str, str | None, None]:
|
) -> Generator[str, str | None, None]:
|
||||||
def get_reportable_tasks() -> Any:
|
def get_reportable_tasks() -> Any:
|
||||||
return processor.bpmn_process_instance.get_tasks(
|
return processor.bpmn_process_instance.get_tasks(
|
||||||
TaskState.WAITING | TaskState.STARTED | TaskState.READY | TaskState.ERROR
|
state=TaskState.WAITING | TaskState.STARTED | TaskState.READY | TaskState.ERROR
|
||||||
)
|
)
|
||||||
|
|
||||||
def render_instructions(spiff_task: SpiffTask) -> str:
|
def render_instructions(spiff_task: SpiffTask) -> str:
|
||||||
|
@ -607,7 +607,7 @@ def _interstitial_stream(
|
||||||
|
|
||||||
|
|
||||||
def _get_ready_engine_step_count(bpmn_process_instance: BpmnWorkflow) -> int:
|
def _get_ready_engine_step_count(bpmn_process_instance: BpmnWorkflow) -> int:
|
||||||
return len([t for t in bpmn_process_instance.get_tasks(TaskState.READY) if not t.task_spec.manual])
|
return len([t for t in bpmn_process_instance.get_tasks(state=TaskState.READY) if not t.task_spec.manual])
|
||||||
|
|
||||||
|
|
||||||
def _dequeued_interstitial_stream(
|
def _dequeued_interstitial_stream(
|
||||||
|
|
|
@ -41,8 +41,9 @@ from SpiffWorkflow.serializer.exceptions import MissingSpecError # type: ignore
|
||||||
from SpiffWorkflow.spiff.parser.process import SpiffBpmnParser # type: ignore
|
from SpiffWorkflow.spiff.parser.process import SpiffBpmnParser # type: ignore
|
||||||
from SpiffWorkflow.spiff.serializer.config import SPIFF_SPEC_CONFIG # type: ignore
|
from SpiffWorkflow.spiff.serializer.config import SPIFF_SPEC_CONFIG # type: ignore
|
||||||
from SpiffWorkflow.task import Task as SpiffTask # type: ignore
|
from SpiffWorkflow.task import Task as SpiffTask # type: ignore
|
||||||
from SpiffWorkflow.task import TaskState
|
|
||||||
from SpiffWorkflow.util.deep_merge import DeepMerge # type: ignore
|
from SpiffWorkflow.util.deep_merge import DeepMerge # type: ignore
|
||||||
|
from SpiffWorkflow.util.task import TaskIterator # type: ignore
|
||||||
|
from SpiffWorkflow.util.task import TaskState
|
||||||
from spiffworkflow_backend.data_stores.json import JSONDataStore
|
from spiffworkflow_backend.data_stores.json import JSONDataStore
|
||||||
from spiffworkflow_backend.data_stores.json import JSONFileDataStore
|
from spiffworkflow_backend.data_stores.json import JSONFileDataStore
|
||||||
from spiffworkflow_backend.data_stores.typeahead import TypeaheadDataStore
|
from spiffworkflow_backend.data_stores.typeahead import TypeaheadDataStore
|
||||||
|
@ -790,8 +791,6 @@ class ProcessInstanceProcessor:
|
||||||
)
|
)
|
||||||
bpmn_process_instance.data[ProcessInstanceProcessor.VALIDATION_PROCESS_KEY] = validate_only
|
bpmn_process_instance.data[ProcessInstanceProcessor.VALIDATION_PROCESS_KEY] = validate_only
|
||||||
|
|
||||||
# run _predict to ensure tasks are predicted to add back in LIKELY and MAYBE tasks
|
|
||||||
bpmn_process_instance._predict()
|
|
||||||
return (
|
return (
|
||||||
bpmn_process_instance,
|
bpmn_process_instance,
|
||||||
full_bpmn_process_dict,
|
full_bpmn_process_dict,
|
||||||
|
@ -1044,16 +1043,6 @@ class ProcessInstanceProcessor:
|
||||||
|
|
||||||
db.session.add(self.process_instance_model)
|
db.session.add(self.process_instance_model)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
known_task_ids = [str(t.id) for t in self.bpmn_process_instance.get_tasks()]
|
|
||||||
TaskModel.query.filter(TaskModel.process_instance_id == self.process_instance_model.id).filter(
|
|
||||||
TaskModel.guid.notin_(known_task_ids) # type: ignore
|
|
||||||
).delete()
|
|
||||||
HumanTaskModel.query.filter(HumanTaskModel.process_instance_id == self.process_instance_model.id).filter(
|
|
||||||
HumanTaskModel.task_id.notin_(known_task_ids) # type: ignore
|
|
||||||
).delete()
|
|
||||||
db.session.commit()
|
|
||||||
|
|
||||||
human_tasks = HumanTaskModel.query.filter_by(
|
human_tasks = HumanTaskModel.query.filter_by(
|
||||||
process_instance_id=self.process_instance_model.id, completed=False
|
process_instance_id=self.process_instance_model.id, completed=False
|
||||||
).all()
|
).all()
|
||||||
|
@ -1111,7 +1100,7 @@ class ProcessInstanceProcessor:
|
||||||
task_name=ready_or_waiting_task.task_spec.bpmn_id,
|
task_name=ready_or_waiting_task.task_spec.bpmn_id,
|
||||||
task_title=ready_or_waiting_task.task_spec.bpmn_name,
|
task_title=ready_or_waiting_task.task_spec.bpmn_name,
|
||||||
task_type=ready_or_waiting_task.task_spec.__class__.__name__,
|
task_type=ready_or_waiting_task.task_spec.__class__.__name__,
|
||||||
task_status=ready_or_waiting_task.get_state_name(),
|
task_status=TaskState.get_name(ready_or_waiting_task.state),
|
||||||
lane_assignment_id=potential_owner_hash["lane_assignment_id"],
|
lane_assignment_id=potential_owner_hash["lane_assignment_id"],
|
||||||
)
|
)
|
||||||
db.session.add(human_task)
|
db.session.add(human_task)
|
||||||
|
@ -1365,14 +1354,14 @@ class ProcessInstanceProcessor:
|
||||||
def status_of(bpmn_process_instance: BpmnWorkflow) -> ProcessInstanceStatus:
|
def status_of(bpmn_process_instance: BpmnWorkflow) -> ProcessInstanceStatus:
|
||||||
if bpmn_process_instance.is_completed():
|
if bpmn_process_instance.is_completed():
|
||||||
return ProcessInstanceStatus.complete
|
return ProcessInstanceStatus.complete
|
||||||
user_tasks = bpmn_process_instance.get_ready_user_tasks()
|
user_tasks = bpmn_process_instance.get_tasks(state=TaskState.READY, manual=True)
|
||||||
|
|
||||||
# workflow.waiting_events (includes timers, and timers have a when firing property)
|
# workflow.waiting_events (includes timers, and timers have a when firing property)
|
||||||
|
|
||||||
# if the process instance has status "waiting" it will get picked up
|
# if the process instance has status "waiting" it will get picked up
|
||||||
# by background processing. when that happens it can potentially overwrite
|
# by background processing. when that happens it can potentially overwrite
|
||||||
# human tasks which is bad because we cache them with the previous id's.
|
# human tasks which is bad because we cache them with the previous id's.
|
||||||
# waiting_tasks = bpmn_process_instance.get_tasks(TaskState.WAITING)
|
# waiting_tasks = bpmn_process_instance.get_tasks(state=TaskState.WAITING)
|
||||||
# waiting_tasks = bpmn_process_instance.get_waiting()
|
# waiting_tasks = bpmn_process_instance.get_waiting()
|
||||||
# if len(waiting_tasks) > 0:
|
# if len(waiting_tasks) > 0:
|
||||||
# return ProcessInstanceStatus.waiting
|
# return ProcessInstanceStatus.waiting
|
||||||
|
@ -1409,7 +1398,7 @@ class ProcessInstanceProcessor:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def lazy_load_subprocess_specs(self) -> None:
|
def lazy_load_subprocess_specs(self) -> None:
|
||||||
tasks = self.bpmn_process_instance.get_tasks(TaskState.DEFINITE_MASK)
|
tasks = self.bpmn_process_instance.get_tasks(state=TaskState.DEFINITE_MASK)
|
||||||
loaded_specs = set(self.bpmn_process_instance.subprocess_specs.keys())
|
loaded_specs = set(self.bpmn_process_instance.subprocess_specs.keys())
|
||||||
for task in tasks:
|
for task in tasks:
|
||||||
if task.task_spec.description != "Call Activity":
|
if task.task_spec.description != "Call Activity":
|
||||||
|
@ -1489,7 +1478,7 @@ class ProcessInstanceProcessor:
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_tasks_with_data(cls, bpmn_process_instance: BpmnWorkflow) -> list[SpiffTask]:
|
def get_tasks_with_data(cls, bpmn_process_instance: BpmnWorkflow) -> list[SpiffTask]:
|
||||||
return [task for task in bpmn_process_instance.get_tasks(TaskState.FINISHED_MASK) if len(task.data) > 0]
|
return [task for task in bpmn_process_instance.get_tasks(state=TaskState.FINISHED_MASK) if len(task.data) > 0]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_task_data_size(cls, bpmn_process_instance: BpmnWorkflow) -> int:
|
def get_task_data_size(cls, bpmn_process_instance: BpmnWorkflow) -> int:
|
||||||
|
@ -1531,7 +1520,7 @@ class ProcessInstanceProcessor:
|
||||||
return self._serializer.workflow_to_dict(self.bpmn_process_instance) # type: ignore
|
return self._serializer.workflow_to_dict(self.bpmn_process_instance) # type: ignore
|
||||||
|
|
||||||
def next_user_tasks(self) -> list[SpiffTask]:
|
def next_user_tasks(self) -> list[SpiffTask]:
|
||||||
return self.bpmn_process_instance.get_ready_user_tasks() # type: ignore
|
return self.bpmn_process_instance.get_tasks(state=TaskState.READY, manual=True) # type: ignore
|
||||||
|
|
||||||
def next_task(self) -> SpiffTask:
|
def next_task(self) -> SpiffTask:
|
||||||
"""Returns the next task that should be completed even if there are parallel tasks and multiple options are available.
|
"""Returns the next task that should be completed even if there are parallel tasks and multiple options are available.
|
||||||
|
@ -1545,7 +1534,7 @@ class ProcessInstanceProcessor:
|
||||||
|
|
||||||
endtasks = []
|
endtasks = []
|
||||||
if self.bpmn_process_instance.is_completed():
|
if self.bpmn_process_instance.is_completed():
|
||||||
for spiff_task in SpiffTask.Iterator(self.bpmn_process_instance.task_tree, TaskState.ANY_MASK):
|
for spiff_task in TaskIterator(self.bpmn_process_instance.task_tree, TaskState.ANY_MASK):
|
||||||
# Assure that we find the end event for this process_instance, and not for any sub-process_instances.
|
# Assure that we find the end event for this process_instance, and not for any sub-process_instances.
|
||||||
if TaskService.is_main_process_end_event(spiff_task):
|
if TaskService.is_main_process_end_event(spiff_task):
|
||||||
endtasks.append(spiff_task)
|
endtasks.append(spiff_task)
|
||||||
|
@ -1557,17 +1546,17 @@ class ProcessInstanceProcessor:
|
||||||
# a parallel gateway with multiple tasks, so prefer ones that share a parent.
|
# a parallel gateway with multiple tasks, so prefer ones that share a parent.
|
||||||
|
|
||||||
# Get a list of all ready tasks
|
# Get a list of all ready tasks
|
||||||
ready_tasks = self.bpmn_process_instance.get_tasks(TaskState.READY)
|
ready_tasks = self.bpmn_process_instance.get_tasks(state=TaskState.READY)
|
||||||
|
|
||||||
if len(ready_tasks) == 0:
|
if len(ready_tasks) == 0:
|
||||||
# If no ready tasks exist, check for a waiting task.
|
# If no ready tasks exist, check for a waiting task.
|
||||||
waiting_tasks = self.bpmn_process_instance.get_tasks(TaskState.WAITING)
|
waiting_tasks = self.bpmn_process_instance.get_tasks(state=TaskState.WAITING)
|
||||||
if len(waiting_tasks) > 0:
|
if len(waiting_tasks) > 0:
|
||||||
return waiting_tasks[0]
|
return waiting_tasks[0]
|
||||||
|
|
||||||
# If there are no ready tasks, and not waiting tasks, return the latest error.
|
# If there are no ready tasks, and not waiting tasks, return the latest error.
|
||||||
error_task = None
|
error_task = None
|
||||||
for task in SpiffTask.Iterator(self.bpmn_process_instance.task_tree, TaskState.ERROR):
|
for task in TaskIterator(self.bpmn_process_instance.task_tree, TaskState.ERROR):
|
||||||
error_task = task
|
error_task = task
|
||||||
return error_task
|
return error_task
|
||||||
|
|
||||||
|
@ -1582,7 +1571,7 @@ class ProcessInstanceProcessor:
|
||||||
last_user_task = completed_user_tasks[0]
|
last_user_task = completed_user_tasks[0]
|
||||||
if len(ready_tasks) > 0:
|
if len(ready_tasks) > 0:
|
||||||
for task in ready_tasks:
|
for task in ready_tasks:
|
||||||
if task._is_descendant_of(last_user_task):
|
if task.is_descendant_of(last_user_task):
|
||||||
return task
|
return task
|
||||||
for task in ready_tasks:
|
for task in ready_tasks:
|
||||||
if self.bpmn_process_instance.last_task and task.parent == last_user_task.parent:
|
if self.bpmn_process_instance.last_task and task.parent == last_user_task.parent:
|
||||||
|
@ -1593,12 +1582,12 @@ class ProcessInstanceProcessor:
|
||||||
# If there are no ready tasks, but the thing isn't complete yet, find the first non-complete task
|
# If there are no ready tasks, but the thing isn't complete yet, find the first non-complete task
|
||||||
# and return that
|
# and return that
|
||||||
next_task_to_return = None
|
next_task_to_return = None
|
||||||
for task in SpiffTask.Iterator(self.bpmn_process_instance.task_tree, TaskState.NOT_FINISHED_MASK):
|
for task in TaskIterator(self.bpmn_process_instance.task_tree, TaskState.NOT_FINISHED_MASK):
|
||||||
next_task_to_return = task
|
next_task_to_return = task
|
||||||
return next_task_to_return
|
return next_task_to_return
|
||||||
|
|
||||||
def completed_user_tasks(self) -> list[SpiffTask]:
|
def completed_user_tasks(self) -> list[SpiffTask]:
|
||||||
user_tasks = self.bpmn_process_instance.get_tasks(TaskState.COMPLETED)
|
user_tasks = self.bpmn_process_instance.get_tasks(state=TaskState.COMPLETED)
|
||||||
user_tasks.reverse()
|
user_tasks.reverse()
|
||||||
user_tasks = list(
|
user_tasks = list(
|
||||||
filter(
|
filter(
|
||||||
|
@ -1638,7 +1627,7 @@ class ProcessInstanceProcessor:
|
||||||
|
|
||||||
human_task.completed_by_user_id = user.id
|
human_task.completed_by_user_id = user.id
|
||||||
human_task.completed = True
|
human_task.completed = True
|
||||||
human_task.task_status = spiff_task.get_state_name()
|
human_task.task_status = TaskState.get_name(spiff_task.state)
|
||||||
db.session.add(human_task)
|
db.session.add(human_task)
|
||||||
|
|
||||||
task_service = TaskService(
|
task_service = TaskService(
|
||||||
|
@ -1701,11 +1690,11 @@ class ProcessInstanceProcessor:
|
||||||
return self.process_instance_model.id
|
return self.process_instance_model.id
|
||||||
|
|
||||||
def get_ready_user_tasks(self) -> list[SpiffTask]:
|
def get_ready_user_tasks(self) -> list[SpiffTask]:
|
||||||
return self.bpmn_process_instance.get_ready_user_tasks() # type: ignore
|
return self.bpmn_process_instance.get_tasks(state=TaskState.READY, manual=True) # type: ignore
|
||||||
|
|
||||||
def get_current_user_tasks(self) -> list[SpiffTask]:
|
def get_current_user_tasks(self) -> list[SpiffTask]:
|
||||||
"""Return a list of all user tasks that are READY or COMPLETE and are parallel to the READY Task."""
|
"""Return a list of all user tasks that are READY or COMPLETE and are parallel to the READY Task."""
|
||||||
ready_tasks = self.bpmn_process_instance.get_ready_user_tasks()
|
ready_tasks = self.bpmn_process_instance.get_tasks(state=TaskState.READY, manual=True)
|
||||||
additional_tasks = []
|
additional_tasks = []
|
||||||
if len(ready_tasks) > 0:
|
if len(ready_tasks) > 0:
|
||||||
for child in ready_tasks[0].parent.children:
|
for child in ready_tasks[0].parent.children:
|
||||||
|
@ -1714,19 +1703,19 @@ class ProcessInstanceProcessor:
|
||||||
return ready_tasks + additional_tasks # type: ignore
|
return ready_tasks + additional_tasks # type: ignore
|
||||||
|
|
||||||
def get_all_user_tasks(self) -> list[SpiffTask]:
|
def get_all_user_tasks(self) -> list[SpiffTask]:
|
||||||
all_tasks = self.bpmn_process_instance.get_tasks(TaskState.ANY_MASK)
|
all_tasks = self.bpmn_process_instance.get_tasks(state=TaskState.ANY_MASK)
|
||||||
return [t for t in all_tasks if t.task_spec.manual]
|
return [t for t in all_tasks if t.task_spec.manual]
|
||||||
|
|
||||||
def get_all_completed_tasks(self) -> list[SpiffTask]:
|
def get_all_completed_tasks(self) -> list[SpiffTask]:
|
||||||
all_tasks = self.bpmn_process_instance.get_tasks(TaskState.ANY_MASK)
|
all_tasks = self.bpmn_process_instance.get_tasks(state=TaskState.ANY_MASK)
|
||||||
return [t for t in all_tasks if t.task_spec.manual and t.state in [TaskState.COMPLETED, TaskState.CANCELLED]]
|
return [t for t in all_tasks if t.task_spec.manual and t.state in [TaskState.COMPLETED, TaskState.CANCELLED]]
|
||||||
|
|
||||||
def get_all_waiting_tasks(self) -> list[SpiffTask]:
|
def get_all_waiting_tasks(self) -> list[SpiffTask]:
|
||||||
all_tasks = self.bpmn_process_instance.get_tasks(TaskState.ANY_MASK)
|
all_tasks = self.bpmn_process_instance.get_tasks(state=TaskState.ANY_MASK)
|
||||||
return [t for t in all_tasks if t.state in [TaskState.WAITING]]
|
return [t for t in all_tasks if t.state in [TaskState.WAITING]]
|
||||||
|
|
||||||
def get_all_ready_or_waiting_tasks(self) -> list[SpiffTask]:
|
def get_all_ready_or_waiting_tasks(self) -> list[SpiffTask]:
|
||||||
all_tasks = self.bpmn_process_instance.get_tasks(TaskState.ANY_MASK)
|
all_tasks = self.bpmn_process_instance.get_tasks(state=TaskState.ANY_MASK)
|
||||||
return [t for t in all_tasks if t.state in [TaskState.WAITING, TaskState.READY]]
|
return [t for t in all_tasks if t.state in [TaskState.WAITING, TaskState.READY]]
|
||||||
|
|
||||||
def get_task_by_guid(self, task_guid: str) -> SpiffTask | None:
|
def get_task_by_guid(self, task_guid: str) -> SpiffTask | None:
|
||||||
|
@ -1736,7 +1725,7 @@ class ProcessInstanceProcessor:
|
||||||
def get_task_by_bpmn_identifier(
|
def get_task_by_bpmn_identifier(
|
||||||
cls, bpmn_task_identifier: str, bpmn_process_instance: BpmnWorkflow
|
cls, bpmn_task_identifier: str, bpmn_process_instance: BpmnWorkflow
|
||||||
) -> SpiffTask | None:
|
) -> SpiffTask | None:
|
||||||
all_tasks = bpmn_process_instance.get_tasks(TaskState.ANY_MASK)
|
all_tasks = bpmn_process_instance.get_tasks(state=TaskState.ANY_MASK)
|
||||||
for task in all_tasks:
|
for task in all_tasks:
|
||||||
if task.task_spec.name == bpmn_task_identifier:
|
if task.task_spec.name == bpmn_task_identifier:
|
||||||
return task
|
return task
|
||||||
|
|
|
@ -14,6 +14,7 @@ from SpiffWorkflow.bpmn.event import PendingBpmnEvent # type: ignore
|
||||||
from SpiffWorkflow.bpmn.specs.control import BoundaryEventSplit # type: ignore
|
from SpiffWorkflow.bpmn.specs.control import BoundaryEventSplit # type: ignore
|
||||||
from SpiffWorkflow.bpmn.specs.event_definitions.timer import TimerEventDefinition # type: ignore
|
from SpiffWorkflow.bpmn.specs.event_definitions.timer import TimerEventDefinition # type: ignore
|
||||||
from SpiffWorkflow.task import Task as SpiffTask # type: ignore
|
from SpiffWorkflow.task import Task as SpiffTask # type: ignore
|
||||||
|
from SpiffWorkflow.util.task import TaskState # type: ignore
|
||||||
from spiffworkflow_backend import db
|
from spiffworkflow_backend import db
|
||||||
from spiffworkflow_backend.exceptions.api_error import ApiError
|
from spiffworkflow_backend.exceptions.api_error import ApiError
|
||||||
from spiffworkflow_backend.models.group import GroupModel
|
from spiffworkflow_backend.models.group import GroupModel
|
||||||
|
@ -199,7 +200,7 @@ class ProcessInstanceService:
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def ready_user_task_has_associated_timer(cls, processor: ProcessInstanceProcessor) -> bool:
|
def ready_user_task_has_associated_timer(cls, processor: ProcessInstanceProcessor) -> bool:
|
||||||
for ready_user_task in processor.bpmn_process_instance.get_ready_user_tasks():
|
for ready_user_task in processor.bpmn_process_instance.get_tasks(state=TaskState.READY, manual=True):
|
||||||
if isinstance(ready_user_task.parent.task_spec, BoundaryEventSplit):
|
if isinstance(ready_user_task.parent.task_spec, BoundaryEventSplit):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
@ -596,7 +597,7 @@ class ProcessInstanceService:
|
||||||
spiff_task.task_spec.bpmn_id,
|
spiff_task.task_spec.bpmn_id,
|
||||||
spiff_task.task_spec.bpmn_name,
|
spiff_task.task_spec.bpmn_name,
|
||||||
task_type,
|
task_type,
|
||||||
spiff_task.get_state_name(),
|
TaskState.get_name(spiff_task.state),
|
||||||
can_complete=can_complete,
|
can_complete=can_complete,
|
||||||
lane=lane,
|
lane=lane,
|
||||||
process_identifier=spiff_task.task_spec._wf_spec.name,
|
process_identifier=spiff_task.task_spec._wf_spec.name,
|
||||||
|
|
|
@ -10,7 +10,7 @@ from lxml import etree # type: ignore
|
||||||
from SpiffWorkflow.bpmn.exceptions import WorkflowTaskException # type: ignore
|
from SpiffWorkflow.bpmn.exceptions import WorkflowTaskException # type: ignore
|
||||||
from SpiffWorkflow.bpmn.workflow import BpmnWorkflow # type: ignore
|
from SpiffWorkflow.bpmn.workflow import BpmnWorkflow # type: ignore
|
||||||
from SpiffWorkflow.task import Task as SpiffTask # type: ignore
|
from SpiffWorkflow.task import Task as SpiffTask # type: ignore
|
||||||
from SpiffWorkflow.task import TaskState
|
from SpiffWorkflow.util.task import TaskState # type: ignore
|
||||||
from spiffworkflow_backend.services.custom_parser import MyCustomParser
|
from spiffworkflow_backend.services.custom_parser import MyCustomParser
|
||||||
|
|
||||||
|
|
||||||
|
@ -126,7 +126,7 @@ class ProcessModelTestRunnerMostlyPureSpiffDelegate(ProcessModelTestRunnerDelega
|
||||||
spiff_task.run()
|
spiff_task.run()
|
||||||
|
|
||||||
def get_next_task(self, bpmn_process_instance: BpmnWorkflow) -> SpiffTask | None:
|
def get_next_task(self, bpmn_process_instance: BpmnWorkflow) -> SpiffTask | None:
|
||||||
ready_tasks = list(bpmn_process_instance.get_tasks(TaskState.READY))
|
ready_tasks = list(bpmn_process_instance.get_tasks(state=TaskState.READY))
|
||||||
if len(ready_tasks) > 0:
|
if len(ready_tasks) > 0:
|
||||||
return ready_tasks[0]
|
return ready_tasks[0]
|
||||||
return None
|
return None
|
||||||
|
|
|
@ -9,7 +9,7 @@ from SpiffWorkflow.bpmn.serializer.workflow import BpmnWorkflow # type: ignore
|
||||||
from SpiffWorkflow.bpmn.serializer.workflow import BpmnWorkflowSerializer
|
from SpiffWorkflow.bpmn.serializer.workflow import BpmnWorkflowSerializer
|
||||||
from SpiffWorkflow.exceptions import WorkflowException # type: ignore
|
from SpiffWorkflow.exceptions import WorkflowException # type: ignore
|
||||||
from SpiffWorkflow.task import Task as SpiffTask # type: ignore
|
from SpiffWorkflow.task import Task as SpiffTask # type: ignore
|
||||||
from SpiffWorkflow.task import TaskStateNames
|
from SpiffWorkflow.util.task import TaskState # type: ignore
|
||||||
from spiffworkflow_backend.models.bpmn_process import BpmnProcessModel
|
from spiffworkflow_backend.models.bpmn_process import BpmnProcessModel
|
||||||
from spiffworkflow_backend.models.bpmn_process import BpmnProcessNotFoundError
|
from spiffworkflow_backend.models.bpmn_process import BpmnProcessNotFoundError
|
||||||
from spiffworkflow_backend.models.bpmn_process_definition import BpmnProcessDefinitionModel
|
from spiffworkflow_backend.models.bpmn_process_definition import BpmnProcessDefinitionModel
|
||||||
|
@ -136,6 +136,9 @@ class TaskService:
|
||||||
spiff_task: SpiffTask,
|
spiff_task: SpiffTask,
|
||||||
) -> None:
|
) -> None:
|
||||||
for child_spiff_task in spiff_task.children:
|
for child_spiff_task in spiff_task.children:
|
||||||
|
if child_spiff_task.has_state(TaskState.PREDICTED_MASK):
|
||||||
|
self.__class__.remove_spiff_task_from_parent(child_spiff_task, self.task_models)
|
||||||
|
continue
|
||||||
self.update_task_model_with_spiff_task(
|
self.update_task_model_with_spiff_task(
|
||||||
spiff_task=child_spiff_task,
|
spiff_task=child_spiff_task,
|
||||||
)
|
)
|
||||||
|
@ -265,7 +268,7 @@ class TaskService:
|
||||||
spiff_task_data = new_properties_json.pop("data")
|
spiff_task_data = new_properties_json.pop("data")
|
||||||
python_env_data_dict = self.__class__._get_python_env_data_dict_from_spiff_task(spiff_task, self.serializer)
|
python_env_data_dict = self.__class__._get_python_env_data_dict_from_spiff_task(spiff_task, self.serializer)
|
||||||
task_model.properties_json = new_properties_json
|
task_model.properties_json = new_properties_json
|
||||||
task_model.state = TaskStateNames[new_properties_json["state"]]
|
task_model.state = TaskState.get_name(new_properties_json["state"])
|
||||||
json_data_dict = self.__class__.update_task_data_on_task_model_and_return_dict_if_updated(
|
json_data_dict = self.__class__.update_task_data_on_task_model_and_return_dict_if_updated(
|
||||||
task_model, spiff_task_data, "json_data_hash"
|
task_model, spiff_task_data, "json_data_hash"
|
||||||
)
|
)
|
||||||
|
@ -430,6 +433,9 @@ class TaskService:
|
||||||
# we are going to avoid saving likely and maybe tasks to the db.
|
# we are going to avoid saving likely and maybe tasks to the db.
|
||||||
# that means we need to remove them from their parents' lists of children as well.
|
# that means we need to remove them from their parents' lists of children as well.
|
||||||
spiff_task = spiff_workflow.get_task_from_id(UUID(task_id))
|
spiff_task = spiff_workflow.get_task_from_id(UUID(task_id))
|
||||||
|
if spiff_task.has_state(TaskState.PREDICTED_MASK):
|
||||||
|
self.__class__.remove_spiff_task_from_parent(spiff_task, self.task_models)
|
||||||
|
continue
|
||||||
|
|
||||||
task_model = TaskModel.query.filter_by(guid=task_id).first()
|
task_model = TaskModel.query.filter_by(guid=task_id).first()
|
||||||
if task_model is None:
|
if task_model is None:
|
||||||
|
|
|
@ -16,7 +16,7 @@ from SpiffWorkflow.bpmn.specs.event_definitions.message import MessageEventDefin
|
||||||
from SpiffWorkflow.bpmn.workflow import BpmnWorkflow # type: ignore
|
from SpiffWorkflow.bpmn.workflow import BpmnWorkflow # type: ignore
|
||||||
from SpiffWorkflow.exceptions import SpiffWorkflowException # type: ignore
|
from SpiffWorkflow.exceptions import SpiffWorkflowException # type: ignore
|
||||||
from SpiffWorkflow.task import Task as SpiffTask # type: ignore
|
from SpiffWorkflow.task import Task as SpiffTask # type: ignore
|
||||||
from SpiffWorkflow.task import TaskState
|
from SpiffWorkflow.util.task import TaskState # type: ignore
|
||||||
from spiffworkflow_backend.exceptions.api_error import ApiError
|
from spiffworkflow_backend.exceptions.api_error import ApiError
|
||||||
from spiffworkflow_backend.models.db import db
|
from spiffworkflow_backend.models.db import db
|
||||||
from spiffworkflow_backend.models.message_instance import MessageInstanceModel
|
from spiffworkflow_backend.models.message_instance import MessageInstanceModel
|
||||||
|
@ -166,7 +166,7 @@ class ExecutionStrategy:
|
||||||
self.delegate.save(bpmn_process_instance)
|
self.delegate.save(bpmn_process_instance)
|
||||||
|
|
||||||
def get_ready_engine_steps(self, bpmn_process_instance: BpmnWorkflow) -> list[SpiffTask]:
|
def get_ready_engine_steps(self, bpmn_process_instance: BpmnWorkflow) -> list[SpiffTask]:
|
||||||
tasks = [t for t in bpmn_process_instance.get_tasks(TaskState.READY) if not t.task_spec.manual]
|
tasks = [t for t in bpmn_process_instance.get_tasks(state=TaskState.READY) if not t.task_spec.manual]
|
||||||
|
|
||||||
if len(tasks) > 0:
|
if len(tasks) > 0:
|
||||||
self.subprocess_spec_loader()
|
self.subprocess_spec_loader()
|
||||||
|
@ -249,7 +249,7 @@ class TaskModelSavingDelegate(EngineStepDelegate):
|
||||||
# but it didn't quite work in all cases, so we deleted it. you can find it in commit
|
# but it didn't quite work in all cases, so we deleted it. you can find it in commit
|
||||||
# 1ead87b4b496525df8cc0e27836c3e987d593dc0 if you are curious.
|
# 1ead87b4b496525df8cc0e27836c3e987d593dc0 if you are curious.
|
||||||
for waiting_spiff_task in bpmn_process_instance.get_tasks(
|
for waiting_spiff_task in bpmn_process_instance.get_tasks(
|
||||||
TaskState.WAITING
|
state=TaskState.WAITING
|
||||||
| TaskState.CANCELLED
|
| TaskState.CANCELLED
|
||||||
| TaskState.READY
|
| TaskState.READY
|
||||||
| TaskState.MAYBE
|
| TaskState.MAYBE
|
||||||
|
|
|
@ -2,7 +2,7 @@ from datetime import datetime
|
||||||
|
|
||||||
from SpiffWorkflow.bpmn.workflow import BpmnWorkflow # type: ignore
|
from SpiffWorkflow.bpmn.workflow import BpmnWorkflow # type: ignore
|
||||||
from SpiffWorkflow.task import Task as SpiffTask # type: ignore
|
from SpiffWorkflow.task import Task as SpiffTask # type: ignore
|
||||||
from SpiffWorkflow.task import TaskState
|
from SpiffWorkflow.util.task import TaskState # type: ignore
|
||||||
from spiffworkflow_backend.specs.start_event import StartConfiguration
|
from spiffworkflow_backend.specs.start_event import StartConfiguration
|
||||||
from spiffworkflow_backend.specs.start_event import StartEvent
|
from spiffworkflow_backend.specs.start_event import StartEvent
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ from spiffworkflow_backend.specs.start_event import StartEvent
|
||||||
class WorkflowService:
|
class WorkflowService:
|
||||||
@classmethod
|
@classmethod
|
||||||
def future_start_events(cls, workflow: BpmnWorkflow) -> list[SpiffTask]:
|
def future_start_events(cls, workflow: BpmnWorkflow) -> list[SpiffTask]:
|
||||||
return [t for t in workflow.get_tasks(TaskState.FUTURE) if isinstance(t.task_spec, StartEvent)]
|
return [t for t in workflow.get_tasks(state=TaskState.FUTURE) if isinstance(t.task_spec, StartEvent)]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def next_start_event_configuration(cls, workflow: BpmnWorkflow, now_in_utc: datetime) -> StartConfiguration | None:
|
def next_start_event_configuration(cls, workflow: BpmnWorkflow, now_in_utc: datetime) -> StartConfiguration | None:
|
||||||
|
|
|
@ -9,7 +9,7 @@ from typing import Any
|
||||||
import pytest
|
import pytest
|
||||||
from flask.app import Flask
|
from flask.app import Flask
|
||||||
from flask.testing import FlaskClient
|
from flask.testing import FlaskClient
|
||||||
from SpiffWorkflow.task import TaskState # type: ignore
|
from SpiffWorkflow.util.task import TaskState # type: ignore
|
||||||
from spiffworkflow_backend.exceptions.process_entity_not_found_error import ProcessEntityNotFoundError
|
from spiffworkflow_backend.exceptions.process_entity_not_found_error import ProcessEntityNotFoundError
|
||||||
from spiffworkflow_backend.models.db import db
|
from spiffworkflow_backend.models.db import db
|
||||||
from spiffworkflow_backend.models.process_group import ProcessGroup
|
from spiffworkflow_backend.models.process_group import ProcessGroup
|
||||||
|
@ -1617,7 +1617,7 @@ class TestProcessApi(BaseTest):
|
||||||
assert len(ready_tasks) == 1
|
assert len(ready_tasks) == 1
|
||||||
ready_task = ready_tasks[0]
|
ready_task = ready_tasks[0]
|
||||||
|
|
||||||
# check all_tasks here to ensure we actually deleted items when cancelling the instance
|
# check all_tasks here to ensure we actually deleted item when cancelling the instance
|
||||||
all_tasks = TaskModel.query.filter_by(process_instance_id=process_instance_id).all()
|
all_tasks = TaskModel.query.filter_by(process_instance_id=process_instance_id).all()
|
||||||
assert len(all_tasks) == 8
|
assert len(all_tasks) == 8
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,7 @@ class TestDotNotation(BaseTest):
|
||||||
processor.do_engine_steps(save=True)
|
processor.do_engine_steps(save=True)
|
||||||
human_task = process_instance.human_tasks[0]
|
human_task = process_instance.human_tasks[0]
|
||||||
|
|
||||||
user_task = processor.get_ready_user_tasks()[0]
|
user_task = processor.get_all_ready_or_waiting_tasks()[0]
|
||||||
form_data = {
|
form_data = {
|
||||||
"invoice.contibutorName": "Elizabeth",
|
"invoice.contibutorName": "Elizabeth",
|
||||||
"invoice.contributorId": 100,
|
"invoice.contributorId": 100,
|
||||||
|
|
|
@ -52,7 +52,7 @@ class TestJinjaService(BaseTest):
|
||||||
processor = ProcessInstanceProcessor(process_instance)
|
processor = ProcessInstanceProcessor(process_instance)
|
||||||
processor.do_engine_steps(save=True)
|
processor.do_engine_steps(save=True)
|
||||||
|
|
||||||
JinjaService.render_instructions_for_end_user(processor.get_ready_user_tasks()[0])
|
JinjaService.render_instructions_for_end_user(processor.get_all_ready_or_waiting_tasks()[0])
|
||||||
"\n".join(
|
"\n".join(
|
||||||
[
|
[
|
||||||
r"* From Filter: Sanitized \| from \| filter",
|
r"* From Filter: Sanitized \| from \| filter",
|
||||||
|
|
|
@ -5,7 +5,7 @@ from flask import g
|
||||||
from flask.app import Flask
|
from flask.app import Flask
|
||||||
from flask.testing import FlaskClient
|
from flask.testing import FlaskClient
|
||||||
from SpiffWorkflow.task import Task as SpiffTask # type: ignore
|
from SpiffWorkflow.task import Task as SpiffTask # type: ignore
|
||||||
from SpiffWorkflow.task import TaskState
|
from SpiffWorkflow.util.task import TaskState # type: ignore
|
||||||
from spiffworkflow_backend.models.bpmn_process import BpmnProcessModel
|
from spiffworkflow_backend.models.bpmn_process import BpmnProcessModel
|
||||||
from spiffworkflow_backend.models.db import db
|
from spiffworkflow_backend.models.db import db
|
||||||
from spiffworkflow_backend.models.group import GroupModel
|
from spiffworkflow_backend.models.group import GroupModel
|
||||||
|
@ -500,7 +500,7 @@ class TestProcessInstanceProcessor(BaseTest):
|
||||||
assert gateway_task is not None
|
assert gateway_task is not None
|
||||||
assert gateway_task.state == TaskState.READY
|
assert gateway_task.state == TaskState.READY
|
||||||
|
|
||||||
gateway_task = processor.bpmn_process_instance.get_tasks(TaskState.READY)[0]
|
gateway_task = processor.bpmn_process_instance.get_tasks(state=TaskState.READY)[0]
|
||||||
processor.manual_complete_task(str(gateway_task.id), execute=True, user=process_instance.process_initiator)
|
processor.manual_complete_task(str(gateway_task.id), execute=True, user=process_instance.process_initiator)
|
||||||
processor.save()
|
processor.save()
|
||||||
processor = ProcessInstanceProcessor(process_instance)
|
processor = ProcessInstanceProcessor(process_instance)
|
||||||
|
@ -659,7 +659,9 @@ class TestProcessInstanceProcessor(BaseTest):
|
||||||
.filter(TaskDefinitionModel.bpmn_identifier == spiff_task.task_spec.name)
|
.filter(TaskDefinitionModel.bpmn_identifier == spiff_task.task_spec.name)
|
||||||
.count()
|
.count()
|
||||||
)
|
)
|
||||||
assert task_models_with_bpmn_identifier_count < 3, count_failure_message
|
|
||||||
|
# some tasks will have 2 COMPLETED and 1 LIKELY/MAYBE
|
||||||
|
assert task_models_with_bpmn_identifier_count < 4, count_failure_message
|
||||||
task_model = TaskModel.query.filter_by(guid=str(spiff_task.id)).first()
|
task_model = TaskModel.query.filter_by(guid=str(spiff_task.id)).first()
|
||||||
|
|
||||||
assert task_model.start_in_seconds is not None
|
assert task_model.start_in_seconds is not None
|
||||||
|
@ -737,8 +739,7 @@ class TestProcessInstanceProcessor(BaseTest):
|
||||||
.filter(TaskModel.state.in_(["LIKELY", "MAYBE"])) # type: ignore
|
.filter(TaskModel.state.in_(["LIKELY", "MAYBE"])) # type: ignore
|
||||||
.count()
|
.count()
|
||||||
)
|
)
|
||||||
assert task_models_that_are_predicted_count == 0
|
assert task_models_that_are_predicted_count == 4
|
||||||
|
|
||||||
assert processor_final.get_data() == data_set_7
|
assert processor_final.get_data() == data_set_7
|
||||||
|
|
||||||
def test_does_not_recreate_human_tasks_on_multiple_saves(
|
def test_does_not_recreate_human_tasks_on_multiple_saves(
|
||||||
|
|
|
@ -41,6 +41,7 @@ class TestTaskService(BaseTest):
|
||||||
bpmn_process_level_2b = (
|
bpmn_process_level_2b = (
|
||||||
BpmnProcessModel.query.join(BpmnProcessDefinitionModel)
|
BpmnProcessModel.query.join(BpmnProcessDefinitionModel)
|
||||||
.filter(BpmnProcessDefinitionModel.bpmn_identifier == "Level2b")
|
.filter(BpmnProcessDefinitionModel.bpmn_identifier == "Level2b")
|
||||||
|
.order_by(BpmnProcessModel.id)
|
||||||
.first()
|
.first()
|
||||||
)
|
)
|
||||||
assert bpmn_process_level_2b is not None
|
assert bpmn_process_level_2b is not None
|
||||||
|
@ -50,6 +51,7 @@ class TestTaskService(BaseTest):
|
||||||
bpmn_process_level_3 = (
|
bpmn_process_level_3 = (
|
||||||
BpmnProcessModel.query.join(BpmnProcessDefinitionModel)
|
BpmnProcessModel.query.join(BpmnProcessDefinitionModel)
|
||||||
.filter(BpmnProcessDefinitionModel.bpmn_identifier == "Level3")
|
.filter(BpmnProcessDefinitionModel.bpmn_identifier == "Level3")
|
||||||
|
.order_by(BpmnProcessModel.id)
|
||||||
.first()
|
.first()
|
||||||
)
|
)
|
||||||
assert bpmn_process_level_3 is not None
|
assert bpmn_process_level_3 is not None
|
||||||
|
@ -86,6 +88,7 @@ class TestTaskService(BaseTest):
|
||||||
task_model_level_2b = (
|
task_model_level_2b = (
|
||||||
TaskModel.query.join(TaskDefinitionModel)
|
TaskModel.query.join(TaskDefinitionModel)
|
||||||
.filter(TaskDefinitionModel.bpmn_identifier == "level_2b_subprocess_script_task")
|
.filter(TaskDefinitionModel.bpmn_identifier == "level_2b_subprocess_script_task")
|
||||||
|
.order_by(TaskModel.id)
|
||||||
.first()
|
.first()
|
||||||
)
|
)
|
||||||
assert task_model_level_2b is not None
|
assert task_model_level_2b is not None
|
||||||
|
@ -100,6 +103,7 @@ class TestTaskService(BaseTest):
|
||||||
task_model_level_3 = (
|
task_model_level_3 = (
|
||||||
TaskModel.query.join(TaskDefinitionModel)
|
TaskModel.query.join(TaskDefinitionModel)
|
||||||
.filter(TaskDefinitionModel.bpmn_identifier == "level_3_script_task")
|
.filter(TaskDefinitionModel.bpmn_identifier == "level_3_script_task")
|
||||||
|
.order_by(TaskModel.id)
|
||||||
.first()
|
.first()
|
||||||
)
|
)
|
||||||
assert task_model_level_3 is not None
|
assert task_model_level_3 is not None
|
||||||
|
|
|
@ -16,7 +16,10 @@ test('renders hotCrumbs', () => {
|
||||||
render(
|
render(
|
||||||
<BrowserRouter>
|
<BrowserRouter>
|
||||||
<ProcessBreadcrumb
|
<ProcessBreadcrumb
|
||||||
hotCrumbs={[['Process Groups', '/process-groups'], [`Process Group: hey`]]}
|
hotCrumbs={[
|
||||||
|
['Process Groups', '/process-groups'],
|
||||||
|
[`Process Group: hey`],
|
||||||
|
]}
|
||||||
/>
|
/>
|
||||||
</BrowserRouter>
|
</BrowserRouter>
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in New Issue