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:
jasquat 2023-09-21 17:47:18 -04:00 committed by GitHub
parent c986dbb9e5
commit 1826cc4b6c
16 changed files with 76 additions and 65 deletions

View File

@ -70,11 +70,19 @@ if [[ "$subcommand" != "pre" ]] || [[ -n "$(git status --porcelain "spiffworkflo
run_pre_commmit || run_pre_commmit
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
if [[ "$subcommand" != "pre" ]] || [[ -n "$(git status --porcelain "$python_project")" ]]; then
pushd "$python_project"
poetry install
poetry run mypy $(get_python_dirs)
clear_log_file
./bin/tests-par
popd
fi

View File

@ -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]]
name = "alembic"
@ -2366,7 +2366,7 @@ lxml = "*"
type = "git"
url = "https://github.com/sartography/SpiffWorkflow"
reference = "main"
resolved_reference = "f113aba0f59f18e9dce7263289c441a28a83b4c6"
resolved_reference = "90159bd23c3c74fcf480414473e851460a01e92c"
[[package]]
name = "sqlalchemy"
@ -2419,7 +2419,7 @@ files = [
]
[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"
[package.extras]

View File

@ -8,7 +8,7 @@ from typing import Any
import marshmallow
from marshmallow import Schema
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.orm import relationship
@ -232,9 +232,8 @@ class Task:
@classmethod
def task_state_name_to_int(cls, task_state_name: str) -> int:
task_state_integers = {v: k for k, v in TaskStateNames.items()}
task_state_int: int = task_state_integers[task_state_name]
return task_state_int
state_value: int = TaskState.get_value(task_state_name)
return state_value
class OptionSchema(Schema):

View File

@ -18,7 +18,7 @@ from MySQLdb import OperationalError # type: ignore
from SpiffWorkflow.bpmn.exceptions import WorkflowTaskException # type: ignore
from SpiffWorkflow.bpmn.workflow import BpmnWorkflow # 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 asc
from sqlalchemy import desc
@ -493,7 +493,7 @@ def _interstitial_stream(
) -> Generator[str, str | None, None]:
def get_reportable_tasks() -> Any:
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:
@ -607,7 +607,7 @@ def _interstitial_stream(
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(

View File

@ -41,8 +41,9 @@ from SpiffWorkflow.serializer.exceptions import MissingSpecError # type: ignore
from SpiffWorkflow.spiff.parser.process import SpiffBpmnParser # 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 TaskState
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 JSONFileDataStore
from spiffworkflow_backend.data_stores.typeahead import TypeaheadDataStore
@ -790,8 +791,6 @@ class ProcessInstanceProcessor:
)
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 (
bpmn_process_instance,
full_bpmn_process_dict,
@ -1044,16 +1043,6 @@ class ProcessInstanceProcessor:
db.session.add(self.process_instance_model)
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(
process_instance_id=self.process_instance_model.id, completed=False
).all()
@ -1111,7 +1100,7 @@ class ProcessInstanceProcessor:
task_name=ready_or_waiting_task.task_spec.bpmn_id,
task_title=ready_or_waiting_task.task_spec.bpmn_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"],
)
db.session.add(human_task)
@ -1365,14 +1354,14 @@ class ProcessInstanceProcessor:
def status_of(bpmn_process_instance: BpmnWorkflow) -> ProcessInstanceStatus:
if bpmn_process_instance.is_completed():
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)
# 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.
# 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()
# if len(waiting_tasks) > 0:
# return ProcessInstanceStatus.waiting
@ -1409,7 +1398,7 @@ class ProcessInstanceProcessor:
return 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())
for task in tasks:
if task.task_spec.description != "Call Activity":
@ -1489,7 +1478,7 @@ class ProcessInstanceProcessor:
@classmethod
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
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
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:
"""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 = []
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.
if TaskService.is_main_process_end_event(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.
# 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 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:
return waiting_tasks[0]
# If there are no ready tasks, and not waiting tasks, return the latest error.
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
return error_task
@ -1582,7 +1571,7 @@ class ProcessInstanceProcessor:
last_user_task = completed_user_tasks[0]
if len(ready_tasks) > 0:
for task in ready_tasks:
if task._is_descendant_of(last_user_task):
if task.is_descendant_of(last_user_task):
return task
for task in ready_tasks:
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
# and return that
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
return next_task_to_return
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 = list(
filter(
@ -1638,7 +1627,7 @@ class ProcessInstanceProcessor:
human_task.completed_by_user_id = user.id
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)
task_service = TaskService(
@ -1701,11 +1690,11 @@ class ProcessInstanceProcessor:
return self.process_instance_model.id
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]:
"""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 = []
if len(ready_tasks) > 0:
for child in ready_tasks[0].parent.children:
@ -1714,19 +1703,19 @@ class ProcessInstanceProcessor:
return ready_tasks + additional_tasks # type: ignore
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]
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]]
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]]
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]]
def get_task_by_guid(self, task_guid: str) -> SpiffTask | None:
@ -1736,7 +1725,7 @@ class ProcessInstanceProcessor:
def get_task_by_bpmn_identifier(
cls, bpmn_task_identifier: str, bpmn_process_instance: BpmnWorkflow
) -> 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:
if task.task_spec.name == bpmn_task_identifier:
return task

View File

@ -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.event_definitions.timer import TimerEventDefinition # 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.exceptions.api_error import ApiError
from spiffworkflow_backend.models.group import GroupModel
@ -199,7 +200,7 @@ class ProcessInstanceService:
@classmethod
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):
return True
return False
@ -596,7 +597,7 @@ class ProcessInstanceService:
spiff_task.task_spec.bpmn_id,
spiff_task.task_spec.bpmn_name,
task_type,
spiff_task.get_state_name(),
TaskState.get_name(spiff_task.state),
can_complete=can_complete,
lane=lane,
process_identifier=spiff_task.task_spec._wf_spec.name,

View File

@ -10,7 +10,7 @@ from lxml import etree # type: ignore
from SpiffWorkflow.bpmn.exceptions import WorkflowTaskException # type: ignore
from SpiffWorkflow.bpmn.workflow import BpmnWorkflow # 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
@ -126,7 +126,7 @@ class ProcessModelTestRunnerMostlyPureSpiffDelegate(ProcessModelTestRunnerDelega
spiff_task.run()
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:
return ready_tasks[0]
return None

View File

@ -9,7 +9,7 @@ from SpiffWorkflow.bpmn.serializer.workflow import BpmnWorkflow # type: ignore
from SpiffWorkflow.bpmn.serializer.workflow import BpmnWorkflowSerializer
from SpiffWorkflow.exceptions import WorkflowException # 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 BpmnProcessNotFoundError
from spiffworkflow_backend.models.bpmn_process_definition import BpmnProcessDefinitionModel
@ -136,6 +136,9 @@ class TaskService:
spiff_task: SpiffTask,
) -> None:
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(
spiff_task=child_spiff_task,
)
@ -265,7 +268,7 @@ class TaskService:
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)
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(
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.
# 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))
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()
if task_model is None:

View File

@ -16,7 +16,7 @@ from SpiffWorkflow.bpmn.specs.event_definitions.message import MessageEventDefin
from SpiffWorkflow.bpmn.workflow import BpmnWorkflow # type: ignore
from SpiffWorkflow.exceptions import SpiffWorkflowException # 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.models.db import db
from spiffworkflow_backend.models.message_instance import MessageInstanceModel
@ -166,7 +166,7 @@ class ExecutionStrategy:
self.delegate.save(bpmn_process_instance)
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:
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
# 1ead87b4b496525df8cc0e27836c3e987d593dc0 if you are curious.
for waiting_spiff_task in bpmn_process_instance.get_tasks(
TaskState.WAITING
state=TaskState.WAITING
| TaskState.CANCELLED
| TaskState.READY
| TaskState.MAYBE

View File

@ -2,7 +2,7 @@ from datetime import datetime
from SpiffWorkflow.bpmn.workflow import BpmnWorkflow # 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 StartEvent
@ -10,7 +10,7 @@ from spiffworkflow_backend.specs.start_event import StartEvent
class WorkflowService:
@classmethod
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
def next_start_event_configuration(cls, workflow: BpmnWorkflow, now_in_utc: datetime) -> StartConfiguration | None:

View File

@ -9,7 +9,7 @@ from typing import Any
import pytest
from flask.app import Flask
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.models.db import db
from spiffworkflow_backend.models.process_group import ProcessGroup
@ -1617,7 +1617,7 @@ class TestProcessApi(BaseTest):
assert len(ready_tasks) == 1
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()
assert len(all_tasks) == 8

View File

@ -28,7 +28,7 @@ class TestDotNotation(BaseTest):
processor.do_engine_steps(save=True)
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 = {
"invoice.contibutorName": "Elizabeth",
"invoice.contributorId": 100,

View File

@ -52,7 +52,7 @@ class TestJinjaService(BaseTest):
processor = ProcessInstanceProcessor(process_instance)
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(
[
r"* From Filter: Sanitized \| from \| filter",

View File

@ -5,7 +5,7 @@ from flask import g
from flask.app import Flask
from flask.testing import FlaskClient
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.db import db
from spiffworkflow_backend.models.group import GroupModel
@ -500,7 +500,7 @@ class TestProcessInstanceProcessor(BaseTest):
assert gateway_task is not None
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.save()
processor = ProcessInstanceProcessor(process_instance)
@ -659,7 +659,9 @@ class TestProcessInstanceProcessor(BaseTest):
.filter(TaskDefinitionModel.bpmn_identifier == spiff_task.task_spec.name)
.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()
assert task_model.start_in_seconds is not None
@ -737,8 +739,7 @@ class TestProcessInstanceProcessor(BaseTest):
.filter(TaskModel.state.in_(["LIKELY", "MAYBE"])) # type: ignore
.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
def test_does_not_recreate_human_tasks_on_multiple_saves(

View File

@ -41,6 +41,7 @@ class TestTaskService(BaseTest):
bpmn_process_level_2b = (
BpmnProcessModel.query.join(BpmnProcessDefinitionModel)
.filter(BpmnProcessDefinitionModel.bpmn_identifier == "Level2b")
.order_by(BpmnProcessModel.id)
.first()
)
assert bpmn_process_level_2b is not None
@ -50,6 +51,7 @@ class TestTaskService(BaseTest):
bpmn_process_level_3 = (
BpmnProcessModel.query.join(BpmnProcessDefinitionModel)
.filter(BpmnProcessDefinitionModel.bpmn_identifier == "Level3")
.order_by(BpmnProcessModel.id)
.first()
)
assert bpmn_process_level_3 is not None
@ -86,6 +88,7 @@ class TestTaskService(BaseTest):
task_model_level_2b = (
TaskModel.query.join(TaskDefinitionModel)
.filter(TaskDefinitionModel.bpmn_identifier == "level_2b_subprocess_script_task")
.order_by(TaskModel.id)
.first()
)
assert task_model_level_2b is not None
@ -100,6 +103,7 @@ class TestTaskService(BaseTest):
task_model_level_3 = (
TaskModel.query.join(TaskDefinitionModel)
.filter(TaskDefinitionModel.bpmn_identifier == "level_3_script_task")
.order_by(TaskModel.id)
.first()
)
assert task_model_level_3 is not None

View File

@ -16,7 +16,10 @@ test('renders hotCrumbs', () => {
render(
<BrowserRouter>
<ProcessBreadcrumb
hotCrumbs={[['Process Groups', '/process-groups'], [`Process Group: hey`]]}
hotCrumbs={[
['Process Groups', '/process-groups'],
[`Process Group: hey`],
]}
/>
</BrowserRouter>
);