run pyl.
This commit is contained in:
parent
19e3b7da1e
commit
c7457daa31
|
@ -18,13 +18,13 @@ def setup_database_uri(app: Flask) -> None:
|
||||||
if app.config.get("SPIFFWORKFLOW_BACKEND_DATABASE_URI") is None:
|
if app.config.get("SPIFFWORKFLOW_BACKEND_DATABASE_URI") is None:
|
||||||
database_name = f"spiffworkflow_backend_{app.config['ENV_IDENTIFIER']}"
|
database_name = f"spiffworkflow_backend_{app.config['ENV_IDENTIFIER']}"
|
||||||
if app.config.get("SPIFFWORKFLOW_BACKEND_DATABASE_TYPE") == "sqlite":
|
if app.config.get("SPIFFWORKFLOW_BACKEND_DATABASE_TYPE") == "sqlite":
|
||||||
app.config["SQLALCHEMY_DATABASE_URI"] = (
|
app.config[
|
||||||
f"sqlite:///{app.instance_path}/db_{app.config['ENV_IDENTIFIER']}.sqlite3"
|
"SQLALCHEMY_DATABASE_URI"
|
||||||
)
|
] = f"sqlite:///{app.instance_path}/db_{app.config['ENV_IDENTIFIER']}.sqlite3"
|
||||||
elif app.config.get("SPIFFWORKFLOW_BACKEND_DATABASE_TYPE") == "postgres":
|
elif app.config.get("SPIFFWORKFLOW_BACKEND_DATABASE_TYPE") == "postgres":
|
||||||
app.config["SQLALCHEMY_DATABASE_URI"] = (
|
app.config[
|
||||||
f"postgresql://spiffworkflow_backend:spiffworkflow_backend@localhost:5432/{database_name}"
|
"SQLALCHEMY_DATABASE_URI"
|
||||||
)
|
] = f"postgresql://spiffworkflow_backend:spiffworkflow_backend@localhost:5432/{database_name}"
|
||||||
else:
|
else:
|
||||||
# use pswd to trick flake8 with hardcoded passwords
|
# use pswd to trick flake8 with hardcoded passwords
|
||||||
db_pswd = app.config.get("SPIFFWORKFLOW_BACKEND_DATABASE_PASSWORD")
|
db_pswd = app.config.get("SPIFFWORKFLOW_BACKEND_DATABASE_PASSWORD")
|
||||||
|
|
|
@ -127,9 +127,9 @@ class ProcessInstanceModel(SpiffworkflowBaseDBModel):
|
||||||
def serialized_with_metadata(self) -> dict[str, Any]:
|
def serialized_with_metadata(self) -> dict[str, Any]:
|
||||||
process_instance_attributes = self.serialized
|
process_instance_attributes = self.serialized
|
||||||
process_instance_attributes["process_metadata"] = self.process_metadata
|
process_instance_attributes["process_metadata"] = self.process_metadata
|
||||||
process_instance_attributes["process_model_with_diagram_identifier"] = (
|
process_instance_attributes[
|
||||||
self.process_model_with_diagram_identifier
|
"process_model_with_diagram_identifier"
|
||||||
)
|
] = self.process_model_with_diagram_identifier
|
||||||
return process_instance_attributes
|
return process_instance_attributes
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|
|
@ -5,6 +5,7 @@ import uuid
|
||||||
from sys import exc_info
|
from sys import exc_info
|
||||||
from typing import Any
|
from typing import Any
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
|
from typing import Generator
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from typing import TypedDict
|
from typing import TypedDict
|
||||||
from typing import Union
|
from typing import Union
|
||||||
|
@ -353,7 +354,7 @@ def task_show(process_instance_id: int, task_guid: str = "next") -> flask.wrappe
|
||||||
return make_response(jsonify(task), 200)
|
return make_response(jsonify(task), 200)
|
||||||
|
|
||||||
|
|
||||||
def _render_instructions_for_end_user(spiff_task: SpiffTask, task: Task):
|
def _render_instructions_for_end_user(spiff_task: SpiffTask, task: Task) -> str:
|
||||||
"""Assure any instructions for end user are processed for jinja syntax."""
|
"""Assure any instructions for end user are processed for jinja syntax."""
|
||||||
if task.properties and "instructionsForEndUser" in task.properties:
|
if task.properties and "instructionsForEndUser" in task.properties:
|
||||||
if task.properties["instructionsForEndUser"]:
|
if task.properties["instructionsForEndUser"]:
|
||||||
|
@ -391,7 +392,7 @@ def process_data_show(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _interstitial_stream(process_instance_id: int):
|
def _interstitial_stream(process_instance_id: int) -> Generator[str, str, None]:
|
||||||
process_instance = _find_process_instance_by_id_or_raise(process_instance_id)
|
process_instance = _find_process_instance_by_id_or_raise(process_instance_id)
|
||||||
processor = ProcessInstanceProcessor(process_instance)
|
processor = ProcessInstanceProcessor(process_instance)
|
||||||
reported_ids = [] # bit of an issue with end tasks showing as getting completed twice.
|
reported_ids = [] # bit of an issue with end tasks showing as getting completed twice.
|
||||||
|
@ -415,9 +416,8 @@ def _interstitial_stream(process_instance_id: int):
|
||||||
processor.save() # Fixme - maybe find a way not to do this on every method?
|
processor.save() # Fixme - maybe find a way not to do this on every method?
|
||||||
|
|
||||||
|
|
||||||
def interstitial(process_instance_id: int):
|
def interstitial(process_instance_id: int) -> Response:
|
||||||
"""A Server Side Events Stream for watching the execution of engine tasks in a
|
"""A Server Side Events Stream for watching the execution of engine tasks."""
|
||||||
process instance."""
|
|
||||||
return Response(stream_with_context(_interstitial_stream(process_instance_id)), mimetype="text/event-stream")
|
return Response(stream_with_context(_interstitial_stream(process_instance_id)), mimetype="text/event-stream")
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -433,9 +433,9 @@ class ProcessInstanceProcessor:
|
||||||
tld.process_instance_id = process_instance_model.id
|
tld.process_instance_id = process_instance_model.id
|
||||||
|
|
||||||
# we want this to be the fully qualified path to the process model including all group subcomponents
|
# we want this to be the fully qualified path to the process model including all group subcomponents
|
||||||
current_app.config["THREAD_LOCAL_DATA"].process_model_identifier = (
|
current_app.config[
|
||||||
f"{process_instance_model.process_model_identifier}"
|
"THREAD_LOCAL_DATA"
|
||||||
)
|
].process_model_identifier = f"{process_instance_model.process_model_identifier}"
|
||||||
|
|
||||||
self.process_instance_model = process_instance_model
|
self.process_instance_model = process_instance_model
|
||||||
self.process_model_service = ProcessModelService()
|
self.process_model_service = ProcessModelService()
|
||||||
|
@ -595,9 +595,9 @@ class ProcessInstanceProcessor:
|
||||||
bpmn_subprocess_definition.bpmn_identifier
|
bpmn_subprocess_definition.bpmn_identifier
|
||||||
] = bpmn_process_definition_dict
|
] = bpmn_process_definition_dict
|
||||||
spiff_bpmn_process_dict["subprocess_specs"][bpmn_subprocess_definition.bpmn_identifier]["task_specs"] = {}
|
spiff_bpmn_process_dict["subprocess_specs"][bpmn_subprocess_definition.bpmn_identifier]["task_specs"] = {}
|
||||||
bpmn_subprocess_definition_bpmn_identifiers[bpmn_subprocess_definition.id] = (
|
bpmn_subprocess_definition_bpmn_identifiers[
|
||||||
bpmn_subprocess_definition.bpmn_identifier
|
bpmn_subprocess_definition.id
|
||||||
)
|
] = bpmn_subprocess_definition.bpmn_identifier
|
||||||
|
|
||||||
task_definitions = TaskDefinitionModel.query.filter(
|
task_definitions = TaskDefinitionModel.query.filter(
|
||||||
TaskDefinitionModel.bpmn_process_definition_id.in_( # type: ignore
|
TaskDefinitionModel.bpmn_process_definition_id.in_( # type: ignore
|
||||||
|
|
|
@ -267,8 +267,7 @@ class RunUntilServiceTaskExecutionStrategy(ExecutionStrategy):
|
||||||
|
|
||||||
|
|
||||||
class RunUntilUserTaskOrMessageExecutionStrategy(ExecutionStrategy):
|
class RunUntilUserTaskOrMessageExecutionStrategy(ExecutionStrategy):
|
||||||
"""When you want to run tasks until you hit something to report to the end user, or
|
"""When you want to run tasks until you hit something to report to the end user."""
|
||||||
until there are no other engine steps to complete."""
|
|
||||||
|
|
||||||
def get_engine_steps(self, bpmn_process_instance: BpmnWorkflow) -> List[SpiffTask]:
|
def get_engine_steps(self, bpmn_process_instance: BpmnWorkflow) -> List[SpiffTask]:
|
||||||
return list(
|
return list(
|
||||||
|
|
|
@ -1654,7 +1654,7 @@ class TestProcessApi(BaseTest):
|
||||||
# a list. It tests all of our code. No reason to test Flasks SSE support.
|
# a list. It tests all of our code. No reason to test Flasks SSE support.
|
||||||
results = list(_interstitial_stream(process_instance_id))
|
results = list(_interstitial_stream(process_instance_id))
|
||||||
# strip the "data:" prefix and convert remaining string to dict.
|
# strip the "data:" prefix and convert remaining string to dict.
|
||||||
json_results = list(map(lambda x: json.loads(x[5:]), results))
|
json_results = list(map(lambda x: json.loads(x[5:]), results)) # type: ignore
|
||||||
# There should be 2 results back -
|
# There should be 2 results back -
|
||||||
# the first script task should not be returned (it contains no end user instructions)
|
# the first script task should not be returned (it contains no end user instructions)
|
||||||
# The second script task should produce rendered jinja text
|
# The second script task should produce rendered jinja text
|
||||||
|
@ -1675,10 +1675,10 @@ class TestProcessApi(BaseTest):
|
||||||
|
|
||||||
# we should now be on a task that does not belong to the original user, and the interstitial page should know this.
|
# we should now be on a task that does not belong to the original user, and the interstitial page should know this.
|
||||||
results = list(_interstitial_stream(process_instance_id))
|
results = list(_interstitial_stream(process_instance_id))
|
||||||
json_results = list(map(lambda x: json.loads(x[5:]), results))
|
json_results = list(map(lambda x: json.loads(x[5:]), results)) # type: ignore
|
||||||
assert len(results) == 1
|
assert len(results) == 1
|
||||||
assert json_results[0]["state"] == "READY"
|
assert json_results[0]["state"] == "READY"
|
||||||
assert json_results[0]["can_complete"] == False
|
assert json_results[0]["can_complete"] is False
|
||||||
assert json_results[0]["title"] == "Please Approve"
|
assert json_results[0]["title"] == "Please Approve"
|
||||||
assert json_results[0]["properties"]["instructionsForEndUser"] == "I am a manual task in another lane"
|
assert json_results[0]["properties"]["instructionsForEndUser"] == "I am a manual task in another lane"
|
||||||
|
|
||||||
|
@ -1692,7 +1692,7 @@ class TestProcessApi(BaseTest):
|
||||||
list(_interstitial_stream(process_instance_id))
|
list(_interstitial_stream(process_instance_id))
|
||||||
list(_interstitial_stream(process_instance_id))
|
list(_interstitial_stream(process_instance_id))
|
||||||
results = list(_interstitial_stream(process_instance_id))
|
results = list(_interstitial_stream(process_instance_id))
|
||||||
json_results = list(map(lambda x: json.loads(x[5:]), results))
|
json_results = list(map(lambda x: json.loads(x[5:]), results)) # type: ignore
|
||||||
assert len(json_results) == 1
|
assert len(json_results) == 1
|
||||||
assert json_results[0]["state"] == "COMPLETED"
|
assert json_results[0]["state"] == "COMPLETED"
|
||||||
assert json_results[0]["properties"]["instructionsForEndUser"] == "I am the end task"
|
assert json_results[0]["properties"]["instructionsForEndUser"] == "I am the end task"
|
||||||
|
|
|
@ -17,6 +17,7 @@ export default function ProcessInterstitial() {
|
||||||
const [status, setStatus] = useState<string>('running');
|
const [status, setStatus] = useState<string>('running');
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const userTasks = ['User Task', 'Manual Task'];
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchEventSource(
|
fetchEventSource(
|
||||||
|
@ -35,7 +36,8 @@ export default function ProcessInterstitial() {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}, []);
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, []); // it is critical to only run this once.
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Added this seperate use effect so that the timer interval will be cleared if
|
// Added this seperate use effect so that the timer interval will be cleared if
|
||||||
|
@ -43,7 +45,7 @@ export default function ProcessInterstitial() {
|
||||||
if (
|
if (
|
||||||
lastTask &&
|
lastTask &&
|
||||||
lastTask.can_complete &&
|
lastTask.can_complete &&
|
||||||
['User Task', 'Manual Task'].includes(lastTask.type)
|
userTasks.includes(lastTask.type)
|
||||||
) {
|
) {
|
||||||
const timerId = setInterval(() => {
|
const timerId = setInterval(() => {
|
||||||
navigate(`/tasks/${lastTask.process_instance_id}/${lastTask.id}`);
|
navigate(`/tasks/${lastTask.process_instance_id}/${lastTask.id}`);
|
||||||
|
@ -57,14 +59,9 @@ export default function ProcessInterstitial() {
|
||||||
if (status !== 'running') {
|
if (status !== 'running') {
|
||||||
setStatus(lastTask.state);
|
setStatus(lastTask.state);
|
||||||
}
|
}
|
||||||
if (
|
if (!lastTask.can_complete && userTasks.includes(lastTask.type)) {
|
||||||
!lastTask.can_complete &&
|
|
||||||
['User Task', 'Manual Task'].includes(lastTask.type)
|
|
||||||
) {
|
|
||||||
setStatus('LOCKED');
|
setStatus('LOCKED');
|
||||||
}
|
}
|
||||||
console.log(`Status is : ${status}}`);
|
|
||||||
console.log('last task is : ', lastTask);
|
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case 'running':
|
case 'running':
|
||||||
return (
|
return (
|
||||||
|
@ -87,10 +84,7 @@ export default function ProcessInterstitial() {
|
||||||
};
|
};
|
||||||
|
|
||||||
const userMessage = (myTask: ProcessInstanceTask) => {
|
const userMessage = (myTask: ProcessInstanceTask) => {
|
||||||
if (
|
if (!myTask.can_complete && userTasks.includes(myTask.type)) {
|
||||||
!myTask.can_complete &&
|
|
||||||
['User Task', 'Manual Task'].includes(myTask.type)
|
|
||||||
) {
|
|
||||||
return <div>This next task must be completed by a different person.</div>;
|
return <div>This next task must be completed by a different person.</div>;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -1,12 +1,5 @@
|
||||||
import { useEffect, useState } from 'react';
|
import { Route, Routes } from 'react-router-dom';
|
||||||
import { Route, Routes, useLocation, useNavigate } from 'react-router-dom';
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { Tabs, TabList, Tab } from '@carbon/react';
|
|
||||||
import TaskShow from './TaskShow';
|
|
||||||
import MyTasks from './MyTasks';
|
|
||||||
import GroupedTasks from './GroupedTasks';
|
|
||||||
import CompletedInstances from './CompletedInstances';
|
|
||||||
import CreateNewInstance from './CreateNewInstance';
|
|
||||||
import ProcessInterstitial from './ProcessInterstitial';
|
import ProcessInterstitial from './ProcessInterstitial';
|
||||||
|
|
||||||
export default function ProcessRoutes() {
|
export default function ProcessRoutes() {
|
||||||
|
|
|
@ -13,7 +13,6 @@ import {
|
||||||
ButtonSet,
|
ButtonSet,
|
||||||
} from '@carbon/react';
|
} from '@carbon/react';
|
||||||
|
|
||||||
import MDEditor from '@uiw/react-md-editor';
|
|
||||||
// eslint-disable-next-line import/no-named-as-default
|
// eslint-disable-next-line import/no-named-as-default
|
||||||
import Form from '../themes/carbon';
|
import Form from '../themes/carbon';
|
||||||
import HttpService from '../services/HttpService';
|
import HttpService from '../services/HttpService';
|
||||||
|
@ -90,13 +89,6 @@ function TypeAheadWidget({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
class UnexpectedHumanTaskType extends Error {
|
|
||||||
constructor(message: string) {
|
|
||||||
super(message);
|
|
||||||
this.name = 'UnexpectedHumanTaskType';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum FormSubmitType {
|
enum FormSubmitType {
|
||||||
Default,
|
Default,
|
||||||
Draft,
|
Draft,
|
||||||
|
@ -108,16 +100,11 @@ export default function TaskShow() {
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [disabled, setDisabled] = useState(false);
|
const [disabled, setDisabled] = useState(false);
|
||||||
const [refreshSeconds, setRefreshSeconds] = useState(0);
|
|
||||||
|
|
||||||
// save current form data so that we can avoid validations in certain situations
|
// save current form data so that we can avoid validations in certain situations
|
||||||
const [currentFormObject, setCurrentFormObject] = useState<any>({});
|
const [currentFormObject, setCurrentFormObject] = useState<any>({});
|
||||||
|
|
||||||
const { addError, removeError } = useAPIError();
|
const { addError, removeError } = useAPIError();
|
||||||
|
|
||||||
// eslint-disable-next-line sonarjs/no-duplicate-string
|
|
||||||
const supportedHumanTaskTypes = ['User Task', 'Manual Task'];
|
|
||||||
|
|
||||||
const navigateToInterstitial = (myTask: ProcessInstanceTask) => {
|
const navigateToInterstitial = (myTask: ProcessInstanceTask) => {
|
||||||
navigate(
|
navigate(
|
||||||
`/process/${modifyProcessIdentifierForPathParam(
|
`/process/${modifyProcessIdentifierForPathParam(
|
||||||
|
@ -361,12 +348,6 @@ export default function TaskShow() {
|
||||||
Save as draft
|
Save as draft
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
<p>
|
|
||||||
<i>Page will refresh in {refreshSeconds} seconds.</i>
|
|
||||||
</p>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
reactFragmentToHideSubmitButton = (
|
reactFragmentToHideSubmitButton = (
|
||||||
<ButtonSet>
|
<ButtonSet>
|
||||||
|
|
Loading…
Reference in New Issue