tasks and subprocesses can are setting the task states properly now when getting task data w/ burnettk jbirddog
This commit is contained in:
parent
7347c73d6a
commit
012d2bd367
|
@ -0,0 +1,34 @@
|
||||||
|
"""empty message
|
||||||
|
|
||||||
|
Revision ID: 63fc8d693b9f
|
||||||
|
Revises: e05ca5cdc312
|
||||||
|
Create Date: 2023-02-09 11:54:34.935801
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
from sqlalchemy.dialects import mysql
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '63fc8d693b9f'
|
||||||
|
down_revision = 'e05ca5cdc312'
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.add_column('spiff_step_details', sa.Column('start_in_seconds', sa.DECIMAL(precision=17, scale=6), nullable=False))
|
||||||
|
op.add_column('spiff_step_details', sa.Column('end_in_seconds', sa.DECIMAL(precision=17, scale=6), nullable=True))
|
||||||
|
op.drop_column('spiff_step_details', 'engine_step_end_in_seconds')
|
||||||
|
op.drop_column('spiff_step_details', 'engine_step_start_in_seconds')
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.add_column('spiff_step_details', sa.Column('engine_step_start_in_seconds', mysql.DECIMAL(precision=17, scale=6), nullable=True))
|
||||||
|
op.add_column('spiff_step_details', sa.Column('engine_step_end_in_seconds', mysql.DECIMAL(precision=17, scale=6), nullable=True))
|
||||||
|
op.drop_column('spiff_step_details', 'end_in_seconds')
|
||||||
|
op.drop_column('spiff_step_details', 'start_in_seconds')
|
||||||
|
# ### end Alembic commands ###
|
|
@ -4,6 +4,7 @@ from __future__ import annotations
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
||||||
from sqlalchemy import ForeignKey
|
from sqlalchemy import ForeignKey
|
||||||
|
from sqlalchemy.orm import relationship
|
||||||
|
|
||||||
from spiffworkflow_backend.models.db import db
|
from spiffworkflow_backend.models.db import db
|
||||||
from spiffworkflow_backend.models.db import SpiffworkflowBaseDBModel
|
from spiffworkflow_backend.models.db import SpiffworkflowBaseDBModel
|
||||||
|
@ -30,3 +31,5 @@ class HumanTaskUserModel(SpiffworkflowBaseDBModel):
|
||||||
ForeignKey(HumanTaskModel.id), nullable=False, index=True # type: ignore
|
ForeignKey(HumanTaskModel.id), nullable=False, index=True # type: ignore
|
||||||
)
|
)
|
||||||
user_id = db.Column(ForeignKey(UserModel.id), nullable=False, index=True) # type: ignore
|
user_id = db.Column(ForeignKey(UserModel.id), nullable=False, index=True) # type: ignore
|
||||||
|
|
||||||
|
human_task = relationship(HumanTaskModel)
|
||||||
|
|
|
@ -31,6 +31,5 @@ class SpiffStepDetailsModel(SpiffworkflowBaseDBModel):
|
||||||
task_state: 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)
|
bpmn_task_identifier: str = db.Column(db.String(255), nullable=False)
|
||||||
|
|
||||||
# timestamp: float = db.Column(db.DECIMAL(17, 6), nullable=False)
|
start_in_seconds: float = db.Column(db.DECIMAL(17, 6), nullable=False)
|
||||||
engine_step_start_in_seconds: float | None = db.Column(db.DECIMAL(17, 6))
|
end_in_seconds: float | None = db.Column(db.DECIMAL(17, 6))
|
||||||
engine_step_end_in_seconds: float | None = db.Column(db.DECIMAL(17, 6))
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
"""Task."""
|
"""Task."""
|
||||||
import enum
|
import enum
|
||||||
|
from SpiffWorkflow.task import TaskStateNames # type: ignore
|
||||||
from typing import Any
|
from typing import Any
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from typing import Union
|
from typing import Union
|
||||||
|
@ -212,6 +213,12 @@ class Task:
|
||||||
value for name, value in vars(cls).items() if name.startswith("FIELD_TYPE")
|
value for name, value in vars(cls).items() if name.startswith("FIELD_TYPE")
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@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
|
||||||
|
|
||||||
|
|
||||||
class OptionSchema(Schema):
|
class OptionSchema(Schema):
|
||||||
"""OptionSchema."""
|
"""OptionSchema."""
|
||||||
|
|
|
@ -11,7 +11,7 @@ from flask import jsonify
|
||||||
from flask import make_response
|
from flask import make_response
|
||||||
from flask import request
|
from flask import request
|
||||||
from flask.wrappers import Response
|
from flask.wrappers import Response
|
||||||
from SpiffWorkflow.task import TaskState # type: ignore
|
from SpiffWorkflow.task import TaskState, TaskStateNames # type: ignore
|
||||||
from sqlalchemy import and_
|
from sqlalchemy import and_
|
||||||
from sqlalchemy import or_
|
from sqlalchemy import or_
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@ from spiffworkflow_backend.models.db import db
|
||||||
from spiffworkflow_backend.models.human_task import HumanTaskModel
|
from spiffworkflow_backend.models.human_task import HumanTaskModel
|
||||||
from spiffworkflow_backend.models.human_task_user import HumanTaskUserModel
|
from spiffworkflow_backend.models.human_task_user import HumanTaskUserModel
|
||||||
from spiffworkflow_backend.models.process_instance import ProcessInstanceApiSchema
|
from spiffworkflow_backend.models.process_instance import ProcessInstanceApiSchema
|
||||||
|
from spiffworkflow_backend.models.task import Task
|
||||||
from spiffworkflow_backend.models.process_instance import (
|
from spiffworkflow_backend.models.process_instance import (
|
||||||
ProcessInstanceCannotBeDeletedError,
|
ProcessInstanceCannotBeDeletedError,
|
||||||
)
|
)
|
||||||
|
@ -568,20 +569,52 @@ def process_instance_task_list(
|
||||||
step_details = step_detail_query.all()
|
step_details = step_detail_query.all()
|
||||||
bpmn_json = json.loads(process_instance.bpmn_json or "{}")
|
bpmn_json = json.loads(process_instance.bpmn_json or "{}")
|
||||||
tasks = bpmn_json["tasks"]
|
tasks = bpmn_json["tasks"]
|
||||||
|
subprocesses = bpmn_json["subprocesses"]
|
||||||
|
|
||||||
# if step_detail is not None and process_instance.bpmn_json is not None:
|
steps_by_id = {step_detail.task_id: step_detail for step_detail in step_details}
|
||||||
|
|
||||||
|
subprocesses_to_set_to_waiting = []
|
||||||
for step_detail in step_details:
|
for step_detail in step_details:
|
||||||
if step_detail.task_id in tasks:
|
if step_detail.task_id in tasks:
|
||||||
|
# task_ids_in_use.append(step_detail.task_id)
|
||||||
task_data = (
|
task_data = (
|
||||||
step_detail.task_json["task_data"] | step_detail.task_json["python_env"]
|
step_detail.task_json["task_data"] | step_detail.task_json["python_env"]
|
||||||
)
|
)
|
||||||
if task_data is None:
|
if task_data is None:
|
||||||
task_data = {}
|
task_data = {}
|
||||||
tasks[step_detail.task_id]["data"] = task_data
|
tasks[step_detail.task_id]["data"] = task_data
|
||||||
|
tasks[step_detail.task_id]['state'] = Task.task_state_name_to_int(step_detail.task_state)
|
||||||
|
else:
|
||||||
|
for subprocess_id, subprocess_info in subprocesses.items():
|
||||||
|
if step_detail.task_id in subprocess_info['tasks']:
|
||||||
|
task_data = (
|
||||||
|
step_detail.task_json["task_data"] | step_detail.task_json["python_env"]
|
||||||
|
)
|
||||||
|
if task_data is None:
|
||||||
|
task_data = {}
|
||||||
|
subprocess_info['tasks'][step_detail.task_id]["data"] = task_data
|
||||||
|
subprocess_info['tasks'][step_detail.task_id]['state'] = Task.task_state_name_to_int(step_detail.task_state)
|
||||||
|
subprocesses_to_set_to_waiting.append(subprocess_id)
|
||||||
|
|
||||||
|
for subprocess_info in subprocesses.values():
|
||||||
|
for spiff_task_id in subprocess_info['tasks']:
|
||||||
|
if spiff_task_id not in steps_by_id:
|
||||||
|
subprocess_info['tasks'][spiff_task_id]['data'] = {}
|
||||||
|
subprocess_info['tasks'][spiff_task_id]['state'] = TaskState.FUTURE
|
||||||
|
for spiff_task_id in tasks:
|
||||||
|
if spiff_task_id not in steps_by_id:
|
||||||
|
tasks[spiff_task_id]['data'] = {}
|
||||||
|
if spiff_task_id in subprocesses_to_set_to_waiting:
|
||||||
|
tasks[spiff_task_id]['state'] = TaskState.WAITING
|
||||||
|
else:
|
||||||
|
tasks[spiff_task_id]['state'] = TaskState.FUTURE
|
||||||
|
|
||||||
process_instance.bpmn_json = json.dumps(bpmn_json)
|
process_instance.bpmn_json = json.dumps(bpmn_json)
|
||||||
|
|
||||||
processor = ProcessInstanceProcessor(process_instance)
|
processor = ProcessInstanceProcessor(process_instance)
|
||||||
|
spiff_task = processor.__class__.get_task_by_bpmn_identifier(step_details[-1].bpmn_task_identifier, processor.bpmn_process_instance)
|
||||||
|
if spiff_task is not None:
|
||||||
|
spiff_task.complete()
|
||||||
|
|
||||||
spiff_tasks = None
|
spiff_tasks = None
|
||||||
if all_tasks:
|
if all_tasks:
|
||||||
|
@ -606,6 +639,16 @@ def process_instance_task_list(
|
||||||
processor, spiff_task, calling_subprocess_task_id=calling_subprocess_task_id
|
processor, spiff_task, calling_subprocess_task_id=calling_subprocess_task_id
|
||||||
)
|
)
|
||||||
if get_task_data:
|
if get_task_data:
|
||||||
|
# if str(spiff_task.id) in steps_by_id:
|
||||||
|
# spiff_step_detail = steps_by_id[str(spiff_task.id)]
|
||||||
|
# task_data = (
|
||||||
|
# spiff_step_detail.task_json["task_data"] | spiff_step_detail.task_json["python_env"]
|
||||||
|
# )
|
||||||
|
# task.data = task_data
|
||||||
|
# task.state = spiff_step_detail.task_state
|
||||||
|
# else:
|
||||||
|
# task.data = {}
|
||||||
|
# task.state = TaskStateNames[TaskState.FUTURE]
|
||||||
task.data = spiff_task.data
|
task.data = spiff_task.data
|
||||||
tasks.append(task)
|
tasks.append(task)
|
||||||
|
|
||||||
|
|
|
@ -133,6 +133,10 @@ class ProcessInstanceLockedBySomethingElseError(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class SpiffStepDetailIsMissingError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class BoxedTaskDataBasedScriptEngineEnvironment(BoxedTaskDataEnvironment): # type: ignore
|
class BoxedTaskDataBasedScriptEngineEnvironment(BoxedTaskDataEnvironment): # type: ignore
|
||||||
def __init__(self, environment_globals: Dict[str, Any]):
|
def __init__(self, environment_globals: Dict[str, Any]):
|
||||||
"""BoxedTaskDataBasedScriptEngineEnvironment."""
|
"""BoxedTaskDataBasedScriptEngineEnvironment."""
|
||||||
|
@ -685,8 +689,8 @@ class ProcessInstanceProcessor:
|
||||||
def spiff_step_details_mapping(
|
def spiff_step_details_mapping(
|
||||||
self,
|
self,
|
||||||
spiff_task: Optional[SpiffTask] = None,
|
spiff_task: Optional[SpiffTask] = None,
|
||||||
start_in_seconds: Optional[float] = 0,
|
start_in_seconds: Optional[float] = None,
|
||||||
end_in_seconds: Optional[float] = 0,
|
end_in_seconds: Optional[float] = None,
|
||||||
) -> dict:
|
) -> dict:
|
||||||
"""SaveSpiffStepDetails."""
|
"""SaveSpiffStepDetails."""
|
||||||
# bpmn_json = self.serialize()
|
# bpmn_json = self.serialize()
|
||||||
|
@ -700,6 +704,10 @@ class ProcessInstanceProcessor:
|
||||||
if spiff_task is None:
|
if spiff_task is None:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
# it's only None when we're starting a human task (it's not complete yet)
|
||||||
|
if start_in_seconds is None:
|
||||||
|
start_in_seconds = time.time()
|
||||||
|
|
||||||
task_data = default_registry.convert(spiff_task.data)
|
task_data = default_registry.convert(spiff_task.data)
|
||||||
python_env = default_registry.convert(
|
python_env = default_registry.convert(
|
||||||
self._script_engine.environment.last_result()
|
self._script_engine.environment.last_result()
|
||||||
|
@ -716,15 +724,15 @@ class ProcessInstanceProcessor:
|
||||||
"spiff_step": self.process_instance_model.spiff_step or 1,
|
"spiff_step": self.process_instance_model.spiff_step or 1,
|
||||||
"task_json": task_json,
|
"task_json": task_json,
|
||||||
"task_id": str(spiff_task.id),
|
"task_id": str(spiff_task.id),
|
||||||
"task_state": spiff_task.state,
|
"task_state": spiff_task.get_state_name(),
|
||||||
"bpmn_task_identifier": spiff_task.task_spec.name,
|
"bpmn_task_identifier": spiff_task.task_spec.name,
|
||||||
"engine_step_start_in_seconds": start_in_seconds,
|
"start_in_seconds": start_in_seconds,
|
||||||
"engine_step_end_in_seconds": end_in_seconds,
|
"end_in_seconds": end_in_seconds,
|
||||||
}
|
}
|
||||||
|
|
||||||
def spiff_step_details(self) -> SpiffStepDetailsModel:
|
def spiff_step_details(self, spiff_task: Optional[SpiffTask] = None) -> SpiffStepDetailsModel:
|
||||||
"""SaveSpiffStepDetails."""
|
"""SaveSpiffStepDetails."""
|
||||||
details_mapping = self.spiff_step_details_mapping()
|
details_mapping = self.spiff_step_details_mapping(spiff_task=spiff_task)
|
||||||
details_model = SpiffStepDetailsModel(**details_mapping)
|
details_model = SpiffStepDetailsModel(**details_mapping)
|
||||||
return details_model
|
return details_model
|
||||||
|
|
||||||
|
@ -934,7 +942,7 @@ class ProcessInstanceProcessor:
|
||||||
potential_owner_hash = self.get_potential_owner_ids_from_task(
|
potential_owner_hash = self.get_potential_owner_ids_from_task(
|
||||||
ready_or_waiting_task
|
ready_or_waiting_task
|
||||||
)
|
)
|
||||||
extensions = ready_or_waiting_task.task_spec.extensions
|
extensions = task_spec.extensions
|
||||||
|
|
||||||
form_file_name = None
|
form_file_name = None
|
||||||
ui_form_file_name = None
|
ui_form_file_name = None
|
||||||
|
@ -965,15 +973,19 @@ class ProcessInstanceProcessor:
|
||||||
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)
|
||||||
db.session.commit()
|
|
||||||
|
|
||||||
for potential_owner_id in potential_owner_hash[
|
for potential_owner_id in potential_owner_hash[
|
||||||
"potential_owner_ids"
|
"potential_owner_ids"
|
||||||
]:
|
]:
|
||||||
human_task_user = HumanTaskUserModel(
|
human_task_user = HumanTaskUserModel(
|
||||||
user_id=potential_owner_id, human_task_id=human_task.id
|
user_id=potential_owner_id, human_task=human_task
|
||||||
)
|
)
|
||||||
db.session.add(human_task_user)
|
db.session.add(human_task_user)
|
||||||
|
|
||||||
|
self.increment_spiff_step()
|
||||||
|
spiff_step_detail_mapping = self.spiff_step_details_mapping(spiff_task=ready_or_waiting_task, start_in_seconds=time.time())
|
||||||
|
spiff_step_detail = SpiffStepDetailsModel(**spiff_step_detail_mapping)
|
||||||
|
db.session.add(spiff_step_detail)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
if len(human_tasks) > 0:
|
if len(human_tasks) > 0:
|
||||||
|
@ -1512,14 +1524,13 @@ class ProcessInstanceProcessor:
|
||||||
tasks_to_log = {
|
tasks_to_log = {
|
||||||
"BPMN Task",
|
"BPMN Task",
|
||||||
"Script Task",
|
"Script Task",
|
||||||
"Service Task"
|
"Service Task",
|
||||||
# "End Event",
|
"Default Start Event",
|
||||||
# "Default Start Event",
|
"Exclusive Gateway",
|
||||||
# "Exclusive Gateway",
|
|
||||||
# "End Join",
|
# "End Join",
|
||||||
# "End Event",
|
"End Event",
|
||||||
# "Default Throwing Event",
|
"Default Throwing Event",
|
||||||
# "Subprocess"
|
"Subprocess"
|
||||||
}
|
}
|
||||||
|
|
||||||
# making a dictionary to ensure we are not shadowing variables in the other methods
|
# making a dictionary to ensure we are not shadowing variables in the other methods
|
||||||
|
@ -1714,12 +1725,19 @@ class ProcessInstanceProcessor:
|
||||||
self, task: SpiffTask, human_task: HumanTaskModel, user: UserModel
|
self, task: SpiffTask, human_task: HumanTaskModel, user: UserModel
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Complete_task."""
|
"""Complete_task."""
|
||||||
self.increment_spiff_step()
|
|
||||||
self.bpmn_process_instance.complete_task_from_id(task.id)
|
self.bpmn_process_instance.complete_task_from_id(task.id)
|
||||||
human_task.completed_by_user_id = user.id
|
human_task.completed_by_user_id = user.id
|
||||||
human_task.completed = True
|
human_task.completed = True
|
||||||
db.session.add(human_task)
|
db.session.add(human_task)
|
||||||
details_model = self.spiff_step_details()
|
details_model = SpiffStepDetailsModel.query.filter_by(process_instance_id=self.process_instance_model.id, task_id=str(task.id), task_state="READY").order_by(SpiffStepDetailsModel.id.desc()).first()
|
||||||
|
if details_model is None:
|
||||||
|
raise SpiffStepDetailIsMissingError(
|
||||||
|
f"Cannot find a ready spiff_step_detail entry for process instance {self.process_instance_model.id} "
|
||||||
|
f"and task_id is {task.id}"
|
||||||
|
)
|
||||||
|
|
||||||
|
details_model.task_state = task.get_state_name()
|
||||||
|
details_model.end_in_seconds = time.time()
|
||||||
db.session.add(details_model)
|
db.session.add(details_model)
|
||||||
|
|
||||||
# this is the thing that actually commits the db transaction (on behalf of the other updates above as well)
|
# this is the thing that actually commits the db transaction (on behalf of the other updates above as well)
|
||||||
|
|
Loading…
Reference in New Issue