switch to spiff_properties branch of spiff, remove api blueprint, camunda to spiff parser/serializer
This commit is contained in:
parent
3d19d7c51b
commit
100f6454f8
|
@ -618,23 +618,18 @@ develop = false
|
|||
[package.dependencies]
|
||||
click = "^8.0.1"
|
||||
flask = "*"
|
||||
flask-admin = "*"
|
||||
flask-bcrypt = "*"
|
||||
flask-cors = "*"
|
||||
flask-mail = "*"
|
||||
flask-marshmallow = "*"
|
||||
flask-migrate = "*"
|
||||
flask-restful = "*"
|
||||
sentry-sdk = "1.7.0"
|
||||
sentry-sdk = "1.7.1"
|
||||
sphinx-autoapi = "^1.8.4"
|
||||
spiffworkflow = {git = "https://github.com/sartography/SpiffWorkflow", rev = "feature/parse_spiffworkflow_extensions"}
|
||||
spiffworkflow = "*"
|
||||
werkzeug = "*"
|
||||
|
||||
[package.source]
|
||||
type = "git"
|
||||
url = "https://github.com/sartography/flask-bpmn"
|
||||
reference = "main"
|
||||
resolved_reference = "c454e729c634c7c86ff30cf4d388480647d18d7b"
|
||||
reference = "feature/with-spiff-properties"
|
||||
resolved_reference = "c7497aabb039420d9e7c68a50d7a4b3e74100e81"
|
||||
|
||||
[[package]]
|
||||
name = "flask-cors"
|
||||
|
@ -1561,7 +1556,7 @@ requests = "*"
|
|||
|
||||
[[package]]
|
||||
name = "sentry-sdk"
|
||||
version = "1.7.0"
|
||||
version = "1.7.1"
|
||||
description = "Python client for Sentry (https://sentry.io)"
|
||||
category = "main"
|
||||
optional = false
|
||||
|
@ -1806,8 +1801,8 @@ pytz = "*"
|
|||
[package.source]
|
||||
type = "git"
|
||||
url = "https://github.com/sartography/SpiffWorkflow"
|
||||
reference = "feature/parse_spiffworkflow_extensions"
|
||||
resolved_reference = "67054883d4040d6755bf0555f072ff85aa42093c"
|
||||
reference = "feature/spiff_properties"
|
||||
resolved_reference = "e108aa12da008bdd8d0319e182d28fbd3afb4c67"
|
||||
|
||||
[[package]]
|
||||
name = "sqlalchemy"
|
||||
|
@ -2098,7 +2093,7 @@ testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-
|
|||
[metadata]
|
||||
lock-version = "1.1"
|
||||
python-versions = "^3.9"
|
||||
content-hash = "c20a647c5a3e12bb5e1e9280316566acaa548e6722b7e4e13b610f25877bd4d0"
|
||||
content-hash = "83f09337ac9218c85a6095a8dcf82eff5153dda76453f77ca97dadfb9ed58b8a"
|
||||
|
||||
[metadata.files]
|
||||
alabaster = [
|
||||
|
|
|
@ -27,13 +27,13 @@ flask-marshmallow = "*"
|
|||
flask-migrate = "*"
|
||||
flask-restful = "*"
|
||||
werkzeug = "*"
|
||||
spiffworkflow = {git = "https://github.com/sartography/SpiffWorkflow", rev = "feature/parse_spiffworkflow_extensions"}
|
||||
# spiffworkflow = {develop = true, path = "/home/jason/projects/github/sartography/SpiffWorkflow"}
|
||||
sentry-sdk = "1.7.0"
|
||||
spiffworkflow = {git = "https://github.com/sartography/SpiffWorkflow", rev = "feature/spiff_properties"}
|
||||
# spiffworkflow = {develop = true, path = "/Users/kevin/projects/github/sartography/SpiffWorkflow"}
|
||||
sentry-sdk = "1.7.1"
|
||||
sphinx-autoapi = "^1.8.4"
|
||||
# flask-bpmn = {develop = true, path = "/home/jason/projects/github/sartography/flask-bpmn"}
|
||||
# flask-bpmn = {develop = true, path = "/Users/kevin/projects/github/sartography/flask-bpmn"}
|
||||
flask-bpmn = {git = "https://github.com/sartography/flask-bpmn", rev = "main"}
|
||||
flask-bpmn = {git = "https://github.com/sartography/flask-bpmn", rev = "feature/with-spiff-properties"}
|
||||
mysql-connector-python = "^8.0.29"
|
||||
pytest-flask = "^1.2.0"
|
||||
pytest-flask-sqlalchemy = "^1.1.0"
|
||||
|
|
|
@ -14,7 +14,6 @@ from flask_mail import Mail # type: ignore
|
|||
import spiffworkflow_backend.load_database_models # noqa: F401
|
||||
from spiffworkflow_backend.config import setup_config
|
||||
from spiffworkflow_backend.routes.admin_blueprint.admin_blueprint import admin_blueprint
|
||||
from spiffworkflow_backend.routes.api_blueprint import api_blueprint
|
||||
from spiffworkflow_backend.routes.process_api_blueprint import process_api_blueprint
|
||||
from spiffworkflow_backend.routes.user_blueprint import user_blueprint
|
||||
|
||||
|
@ -55,7 +54,6 @@ def create_app() -> flask.app.Flask:
|
|||
migrate.init_app(app, db)
|
||||
|
||||
app.register_blueprint(user_blueprint)
|
||||
app.register_blueprint(api_blueprint)
|
||||
app.register_blueprint(process_api_blueprint)
|
||||
app.register_blueprint(api_error_blueprint)
|
||||
app.register_blueprint(admin_blueprint, url_prefix="/admin")
|
||||
|
|
|
@ -165,7 +165,6 @@ class ProcessInstanceApi:
|
|||
next_task: Task | None,
|
||||
process_model_identifier: str,
|
||||
process_group_identifier: str,
|
||||
total_tasks: int,
|
||||
completed_tasks: int,
|
||||
updated_at_in_seconds: int,
|
||||
is_review: bool,
|
||||
|
@ -178,7 +177,6 @@ class ProcessInstanceApi:
|
|||
# self.navigation = navigation fixme: would be a hotness.
|
||||
self.process_model_identifier = process_model_identifier
|
||||
self.process_group_identifier = process_group_identifier
|
||||
self.total_tasks = total_tasks
|
||||
self.completed_tasks = completed_tasks
|
||||
self.updated_at_in_seconds = updated_at_in_seconds
|
||||
self.title = title
|
||||
|
@ -199,7 +197,6 @@ class ProcessInstanceApiSchema(Schema):
|
|||
"navigation",
|
||||
"process_model_identifier",
|
||||
"process_group_identifier",
|
||||
"total_tasks",
|
||||
"completed_tasks",
|
||||
"updated_at_in_seconds",
|
||||
"is_review",
|
||||
|
@ -228,7 +225,6 @@ class ProcessInstanceApiSchema(Schema):
|
|||
"navigation",
|
||||
"process_model_identifier",
|
||||
"process_group_identifier",
|
||||
"total_tasks",
|
||||
"completed_tasks",
|
||||
"updated_at_in_seconds",
|
||||
"is_review",
|
||||
|
@ -251,7 +247,6 @@ class ProcessInstanceMetadata:
|
|||
spec_version: str | None = None
|
||||
state: str | None = None
|
||||
status: str | None = None
|
||||
total_tasks: int | None = None
|
||||
completed_tasks: int | None = None
|
||||
is_review: bool | None = None
|
||||
state_message: str | None = None
|
||||
|
@ -270,7 +265,6 @@ class ProcessInstanceMetadata:
|
|||
process_group_id=process_model.process_group_id,
|
||||
state_message=process_instance.state_message,
|
||||
status=process_instance.status,
|
||||
total_tasks=process_instance.total_tasks,
|
||||
completed_tasks=process_instance.completed_tasks,
|
||||
is_review=process_model.is_review,
|
||||
process_model_identifier=process_instance.process_model_identifier,
|
||||
|
@ -292,7 +286,6 @@ class ProcessInstanceMetadataSchema(Schema):
|
|||
"display_name",
|
||||
"description",
|
||||
"state",
|
||||
"total_tasks",
|
||||
"completed_tasks",
|
||||
"process_group_id",
|
||||
"is_review",
|
||||
|
|
|
@ -1,59 +0,0 @@
|
|||
"""Api."""
|
||||
import json
|
||||
import os
|
||||
|
||||
from flask import Blueprint
|
||||
from flask import current_app
|
||||
from flask import request
|
||||
from flask import Response
|
||||
from SpiffWorkflow.bpmn.serializer.workflow import BpmnWorkflowSerializer # type: ignore
|
||||
from SpiffWorkflow.camunda.serializer.task_spec_converters import UserTaskConverter # type: ignore
|
||||
from SpiffWorkflow.dmn.serializer.task_spec_converters import BusinessRuleTaskConverter # type: ignore
|
||||
|
||||
from spiffworkflow_backend.models.process_instance import ProcessInstanceModel
|
||||
from spiffworkflow_backend.spiff_workflow_connector import parse
|
||||
from spiffworkflow_backend.spiff_workflow_connector import run
|
||||
|
||||
|
||||
wf_spec_converter = BpmnWorkflowSerializer.configure_workflow_spec_converter(
|
||||
[UserTaskConverter, BusinessRuleTaskConverter]
|
||||
)
|
||||
serializer = BpmnWorkflowSerializer(wf_spec_converter)
|
||||
|
||||
api_blueprint = Blueprint("api", __name__)
|
||||
|
||||
|
||||
@api_blueprint.route("/run_process", methods=["POST"])
|
||||
def run_process() -> Response:
|
||||
"""Run_process."""
|
||||
content = request.json
|
||||
if content is None:
|
||||
return Response(
|
||||
json.dumps({"error": "Could not find json request"}),
|
||||
status=400,
|
||||
mimetype="application/json",
|
||||
)
|
||||
|
||||
bpmn_spec_dir = current_app.config["BPMN_SPEC_ABSOLUTE_DIR"]
|
||||
process = "order_product"
|
||||
dmn = [
|
||||
os.path.join(bpmn_spec_dir, "product_prices.dmn"),
|
||||
os.path.join(bpmn_spec_dir, "shipping_costs.dmn"),
|
||||
]
|
||||
bpmn = [
|
||||
os.path.join(bpmn_spec_dir, "multiinstance.bpmn"),
|
||||
os.path.join(bpmn_spec_dir, "call_activity_multi.bpmn"),
|
||||
]
|
||||
|
||||
workflow = None
|
||||
process_instance = ProcessInstanceModel.query.filter().first()
|
||||
if process_instance is None:
|
||||
workflow = parse(process, bpmn, dmn)
|
||||
else:
|
||||
workflow = serializer.deserialize_json(process_instance.bpmn_json)
|
||||
|
||||
response = run(workflow, content.get("task_identifier"), content.get("answer"))
|
||||
|
||||
return Response(
|
||||
json.dumps({"response": response}), status=200, mimetype="application/json"
|
||||
)
|
|
@ -14,6 +14,7 @@ from lxml import etree # type: ignore
|
|||
from SpiffWorkflow import Task as SpiffTask # type: ignore
|
||||
from SpiffWorkflow import TaskState
|
||||
from SpiffWorkflow import WorkflowException
|
||||
from SpiffWorkflow.bpmn.exceptions import WorkflowTaskExecException # type: ignore
|
||||
from SpiffWorkflow.bpmn.parser.ValidationException import ValidationException # type: ignore
|
||||
from SpiffWorkflow.bpmn.PythonScriptEngine import Box # type: ignore
|
||||
from SpiffWorkflow.bpmn.PythonScriptEngine import PythonScriptEngine
|
||||
|
@ -23,13 +24,12 @@ from SpiffWorkflow.bpmn.specs.BpmnProcessSpec import BpmnProcessSpec # type: ig
|
|||
from SpiffWorkflow.bpmn.specs.events import CancelEventDefinition # type: ignore
|
||||
from SpiffWorkflow.bpmn.specs.events import EndEvent
|
||||
from SpiffWorkflow.bpmn.workflow import BpmnWorkflow # type: ignore
|
||||
from SpiffWorkflow.camunda.parser.CamundaParser import CamundaParser # type: ignore
|
||||
from SpiffWorkflow.camunda.serializer import UserTaskConverter # type: ignore
|
||||
from SpiffWorkflow.dmn.parser.BpmnDmnParser import BpmnDmnParser # type: ignore
|
||||
from SpiffWorkflow.dmn.serializer import BusinessRuleTaskConverter # type: ignore
|
||||
from SpiffWorkflow.exceptions import WorkflowTaskExecException # type: ignore
|
||||
from SpiffWorkflow.serializer.exceptions import MissingSpecError # type: ignore
|
||||
from SpiffWorkflow.specs import WorkflowSpec # type: ignore
|
||||
from SpiffWorkflow.spiff.parser.process import SpiffBpmnParser # type: ignore
|
||||
from SpiffWorkflow.spiff.serializer import UserTaskConverter # type: ignore
|
||||
from SpiffWorkflow.util.deep_merge import DeepMerge # type: ignore
|
||||
|
||||
from spiffworkflow_backend.models.active_task import ActiveTaskModel
|
||||
|
@ -90,10 +90,10 @@ class CustomBpmnScriptEngine(PythonScriptEngine): # type: ignore
|
|||
|
||||
|
||||
class MyCustomParser(BpmnDmnParser): # type: ignore
|
||||
"""A BPMN and DMN parser that can also parse Camunda forms."""
|
||||
"""A BPMN and DMN parser that can also parse spiffworkflow-specific extensions."""
|
||||
|
||||
OVERRIDE_PARSER_CLASSES = BpmnDmnParser.OVERRIDE_PARSER_CLASSES
|
||||
OVERRIDE_PARSER_CLASSES.update(CamundaParser.OVERRIDE_PARSER_CLASSES)
|
||||
OVERRIDE_PARSER_CLASSES.update(SpiffBpmnParser.OVERRIDE_PARSER_CLASSES)
|
||||
|
||||
|
||||
class ProcessInstanceProcessor:
|
||||
|
@ -355,8 +355,13 @@ class ProcessInstanceProcessor:
|
|||
extensions = ready_or_waiting_task.task_spec.extensions
|
||||
|
||||
form_file_name = None
|
||||
if "formKey" in extensions:
|
||||
form_file_name = extensions["formKey"]
|
||||
if "properties" in extensions:
|
||||
properties = extensions["properties"]
|
||||
if "formJsonSchemaFilename" in properties:
|
||||
form_file_name = properties["formJsonSchemaFilename"]
|
||||
# FIXME:
|
||||
# if "formUiSchemaFilename" in properties:
|
||||
# form_file_name = properties["formUiSchemaFilename"]
|
||||
|
||||
active_task = ActiveTaskModel(
|
||||
spiffworkflow_task_id=str(ready_or_waiting_task.id),
|
||||
|
|
|
@ -67,7 +67,7 @@ class ProcessInstanceService:
|
|||
|
||||
If requested, and possible, next_task is set to the current_task.
|
||||
"""
|
||||
navigation = processor.bpmn_process_instance.get_deep_nav_list()
|
||||
# navigation = processor.bpmn_process_instance.get_deep_nav_list()
|
||||
# ProcessInstanceService.update_navigation(navigation, processor)
|
||||
process_model_service = ProcessModelService()
|
||||
process_model = process_model_service.get_process_model(
|
||||
|
@ -82,7 +82,7 @@ class ProcessInstanceService:
|
|||
# navigation=navigation,
|
||||
process_model_identifier=processor.process_model_identifier,
|
||||
process_group_identifier=processor.process_group_identifier,
|
||||
total_tasks=len(navigation),
|
||||
# total_tasks=len(navigation),
|
||||
completed_tasks=processor.process_instance_model.completed_tasks,
|
||||
updated_at_in_seconds=processor.process_instance_model.updated_at_in_seconds,
|
||||
is_review=is_review_value,
|
||||
|
|
|
@ -1,214 +0,0 @@
|
|||
"""Spiff Workflow Connector."""
|
||||
from typing import Any
|
||||
from typing import Dict
|
||||
from typing import List
|
||||
from typing import Optional
|
||||
from typing import Union
|
||||
|
||||
from flask_bpmn.models.db import db
|
||||
from SpiffWorkflow.bpmn.serializer.workflow import BpmnWorkflowSerializer # type: ignore
|
||||
from SpiffWorkflow.bpmn.specs.events.event_types import CatchingEvent # type: ignore
|
||||
from SpiffWorkflow.bpmn.specs.events.event_types import ThrowingEvent
|
||||
from SpiffWorkflow.bpmn.specs.ManualTask import ManualTask # type: ignore
|
||||
from SpiffWorkflow.bpmn.specs.ScriptTask import ScriptTask # type: ignore
|
||||
from SpiffWorkflow.bpmn.workflow import BpmnWorkflow # type: ignore
|
||||
from SpiffWorkflow.camunda.parser.CamundaParser import CamundaParser # type: ignore
|
||||
from SpiffWorkflow.camunda.serializer.task_spec_converters import UserTaskConverter # type: ignore
|
||||
from SpiffWorkflow.camunda.specs.UserTask import EnumFormField # type: ignore
|
||||
from SpiffWorkflow.camunda.specs.UserTask import UserTask
|
||||
from SpiffWorkflow.dmn.parser.BpmnDmnParser import BpmnDmnParser # type: ignore
|
||||
from SpiffWorkflow.dmn.serializer.task_spec_converters import BusinessRuleTaskConverter # type: ignore
|
||||
from SpiffWorkflow.task import Task # type: ignore
|
||||
from SpiffWorkflow.task import TaskState
|
||||
from typing_extensions import TypedDict
|
||||
|
||||
from spiffworkflow_backend.models.process_instance import ProcessInstanceModel
|
||||
from spiffworkflow_backend.models.user import UserModel
|
||||
|
||||
|
||||
wf_spec_converter = BpmnWorkflowSerializer.configure_workflow_spec_converter(
|
||||
[UserTaskConverter, BusinessRuleTaskConverter]
|
||||
)
|
||||
serializer = BpmnWorkflowSerializer(wf_spec_converter)
|
||||
|
||||
|
||||
class Parser(BpmnDmnParser): # type: ignore
|
||||
"""Parser."""
|
||||
|
||||
OVERRIDE_PARSER_CLASSES = BpmnDmnParser.OVERRIDE_PARSER_CLASSES
|
||||
OVERRIDE_PARSER_CLASSES.update(CamundaParser.OVERRIDE_PARSER_CLASSES)
|
||||
|
||||
|
||||
class ProcessStatus(TypedDict, total=False):
|
||||
"""ProcessStatus."""
|
||||
|
||||
last_task: str
|
||||
upcoming_tasks: List[str]
|
||||
next_activity: Dict[str, str]
|
||||
|
||||
|
||||
def parse(process: str, bpmn_files: List[str], dmn_files: List[str]) -> BpmnWorkflow:
|
||||
"""Parse."""
|
||||
parser = Parser()
|
||||
parser.add_bpmn_files(bpmn_files)
|
||||
if dmn_files:
|
||||
parser.add_dmn_files(dmn_files)
|
||||
return BpmnWorkflow(parser.get_spec(process))
|
||||
|
||||
|
||||
def format_task(task: Task, include_state: bool = True) -> str:
|
||||
"""Format_task."""
|
||||
if hasattr(task.task_spec, "lane") and task.task_spec.lane is not None:
|
||||
lane = f"[{task.task_spec.lane}]"
|
||||
else:
|
||||
lane = ""
|
||||
state = f"[{task.get_state_name()}]" if include_state else ""
|
||||
return f"{lane} {task.task_spec.description} ({task.task_spec.name}) {state}"
|
||||
|
||||
|
||||
def process_field(
|
||||
field: Any, answer: Union[dict, None], required_user_input_fields: Dict[str, str]
|
||||
) -> Union[str, int, None]:
|
||||
"""Handles the complexities of figuring out what to do about each necessary user field."""
|
||||
response = None
|
||||
if isinstance(field, EnumFormField):
|
||||
option_map = {opt.name: opt.id for opt in field.options}
|
||||
options = "(" + ", ".join(option_map) + ")"
|
||||
if answer is None:
|
||||
required_user_input_fields[field.label] = options
|
||||
else:
|
||||
response = option_map[answer[field.label]]
|
||||
elif field.type == "string":
|
||||
if answer is None:
|
||||
required_user_input_fields[field.label] = "STRING"
|
||||
else:
|
||||
response = answer[field.label]
|
||||
else:
|
||||
if answer is None:
|
||||
required_user_input_fields[field.label] = "(1..)"
|
||||
else:
|
||||
if field.type == "long":
|
||||
response = int(answer[field.label])
|
||||
|
||||
return response
|
||||
|
||||
|
||||
def complete_user_task(
|
||||
task: Task, answer: Optional[Dict[str, str]] = None
|
||||
) -> Dict[Any, Any]:
|
||||
"""Complete_user_task."""
|
||||
if task.data is None:
|
||||
task.data = {}
|
||||
|
||||
required_user_input_fields: Dict[str, str] = {}
|
||||
for field in task.task_spec.form.fields:
|
||||
response = process_field(field, answer, required_user_input_fields)
|
||||
if answer:
|
||||
task.update_data_var(field.id, response)
|
||||
return required_user_input_fields
|
||||
|
||||
|
||||
def get_state(workflow: BpmnWorkflow) -> ProcessStatus:
|
||||
"""Print_state."""
|
||||
task = workflow.last_task
|
||||
|
||||
return_json: ProcessStatus = {"last_task": format_task(task), "upcoming_tasks": []}
|
||||
|
||||
display_types = (UserTask, ManualTask, ScriptTask, ThrowingEvent, CatchingEvent)
|
||||
all_tasks = [
|
||||
task
|
||||
for task in workflow.get_tasks()
|
||||
if isinstance(task.task_spec, display_types)
|
||||
]
|
||||
upcoming_tasks = [
|
||||
task for task in all_tasks if task.state in [TaskState.READY, TaskState.WAITING]
|
||||
]
|
||||
|
||||
for _idx, task in enumerate(upcoming_tasks):
|
||||
return_json["upcoming_tasks"].append(format_task(task))
|
||||
|
||||
return return_json
|
||||
|
||||
|
||||
def create_user() -> UserModel:
|
||||
"""Create_user."""
|
||||
user = UserModel(username="user1")
|
||||
db.session.add(user)
|
||||
db.session.commit()
|
||||
|
||||
return user
|
||||
|
||||
|
||||
def create_process_instance() -> ProcessInstanceModel:
|
||||
"""Create_process_instance."""
|
||||
user = UserModel.query.filter().first()
|
||||
if user is None:
|
||||
user = create_user()
|
||||
|
||||
process_instance = ProcessInstanceModel(
|
||||
process_model_identifier="process_model1", process_initiator_id=user.id
|
||||
)
|
||||
db.session.add(process_instance)
|
||||
db.session.commit()
|
||||
|
||||
return process_instance
|
||||
|
||||
|
||||
def run(
|
||||
workflow: BpmnWorkflow,
|
||||
task_identifier: Optional[str] = None,
|
||||
answer: Optional[Dict[str, str]] = None,
|
||||
) -> Union[ProcessStatus, Dict[str, str]]:
|
||||
"""Run."""
|
||||
workflow.do_engine_steps()
|
||||
tasks_status = ProcessStatus()
|
||||
|
||||
if workflow.is_completed():
|
||||
return tasks_status
|
||||
|
||||
ready_tasks = workflow.get_ready_user_tasks()
|
||||
options = {}
|
||||
formatted_options = {}
|
||||
|
||||
for idx, task in enumerate(ready_tasks):
|
||||
option = format_task(task, False)
|
||||
options[str(idx + 1)] = task
|
||||
formatted_options[str(idx + 1)] = option
|
||||
|
||||
if task_identifier is None:
|
||||
return formatted_options
|
||||
|
||||
next_task = options[task_identifier]
|
||||
if isinstance(next_task.task_spec, UserTask):
|
||||
if answer is None:
|
||||
return complete_user_task(next_task)
|
||||
else:
|
||||
complete_user_task(next_task, answer)
|
||||
next_task.complete()
|
||||
elif isinstance(next_task.task_spec, ManualTask):
|
||||
next_task.complete()
|
||||
else:
|
||||
next_task.complete()
|
||||
|
||||
workflow.refresh_waiting_tasks()
|
||||
workflow.do_engine_steps()
|
||||
tasks_status = get_state(workflow)
|
||||
|
||||
ready_tasks = workflow.get_ready_user_tasks()
|
||||
formatted_options = {}
|
||||
for idx, task in enumerate(ready_tasks):
|
||||
option = format_task(task, False)
|
||||
formatted_options[str(idx + 1)] = option
|
||||
|
||||
state = serializer.serialize_json(workflow)
|
||||
process_instance = ProcessInstanceModel.query.filter().first()
|
||||
|
||||
if process_instance is None:
|
||||
process_instance = create_process_instance()
|
||||
process_instance.bpmn_json = state
|
||||
db.session.add(process_instance)
|
||||
db.session.commit()
|
||||
|
||||
tasks_status["next_activity"] = formatted_options
|
||||
|
||||
return tasks_status
|
Loading…
Reference in New Issue