cleaing up the design a little bit so it looks pretty and in keeping with the rest of the site.

This commit is contained in:
Dan 2023-04-18 16:58:59 -04:00
parent fb2af46521
commit 02e8add28f
7 changed files with 123 additions and 69 deletions

View File

@ -358,9 +358,11 @@ def _render_instructions_for_end_user(spiff_task: SpiffTask, task: Task):
if task.properties and "instructionsForEndUser" in task.properties: if task.properties and "instructionsForEndUser" in task.properties:
if task.properties["instructionsForEndUser"]: if task.properties["instructionsForEndUser"]:
try: try:
task.properties["instructionsForEndUser"] = _render_jinja_template( instructions = _render_jinja_template(
task.properties["instructionsForEndUser"], spiff_task task.properties["instructionsForEndUser"], spiff_task
) )
task.properties["instructionsForEndUser"] = instructions
return instructions
except WorkflowTaskException as wfe: except WorkflowTaskException as wfe:
wfe.add_note("Failed to render instructions for end user.") wfe.add_note("Failed to render instructions for end user.")
raise ApiError.from_workflow_exception("instructions_error", str(wfe), exp=wfe) from wfe raise ApiError.from_workflow_exception("instructions_error", str(wfe), exp=wfe) from wfe
@ -393,19 +395,22 @@ def process_data_show(
def interstitial(process_instance_id: int): def interstitial(process_instance_id: int):
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.
def get_data(): def get_data():
spiff_task = processor.next_task() spiff_task = processor.next_task()
last_task = None last_task = None
while last_task != spiff_task: while last_task != spiff_task:
task = ProcessInstanceService.spiff_task_to_api_task(processor, processor.next_task()) task = ProcessInstanceService.spiff_task_to_api_task(processor, processor.next_task())
_render_instructions_for_end_user(spiff_task, task) instructions = _render_instructions_for_end_user(spiff_task, task)
yield f'data: {current_app.json.dumps(task)} \n\n' if instructions and spiff_task.id not in reported_ids:
reported_ids.append(spiff_task.id)
yield f'data: {current_app.json.dumps(task)} \n\n'
last_task = spiff_task last_task = spiff_task
processor.do_engine_steps(execution_strategy_name="run_until_user_message")
processor.do_engine_steps(execution_strategy_name="one_at_a_time") processor.do_engine_steps(execution_strategy_name="one_at_a_time")
spiff_task = processor.next_task() spiff_task = processor.next_task()
# Note, this has to be done in case someone leaves the page, # Note, this has to be done in case someone leaves the page,
# which can cancel this function before saving. # which can otherwise cancel this function and leave completed tasks un-registered.
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?
return return

View File

@ -443,6 +443,7 @@ class ProcessInstanceService:
process_identifier=spiff_task.task_spec._wf_spec.name, process_identifier=spiff_task.task_spec._wf_spec.name,
process_instance_id=processor.process_instance_model.id, process_instance_id=processor.process_instance_model.id,
process_model_identifier=processor.process_model_identifier, process_model_identifier=processor.process_model_identifier,
process_model_display_name=processor.process_model_display_name,
properties=props, properties=props,
parent=parent_id, parent=parent_id,
event_definition=serialized_task_spec.get("event_definition"), event_definition=serialized_task_spec.get("event_definition"),

View File

@ -50,11 +50,11 @@ class TaskModelSavingDelegate(EngineStepDelegate):
""" """
def __init__( def __init__(
self, self,
serializer: BpmnWorkflowSerializer, serializer: BpmnWorkflowSerializer,
process_instance: ProcessInstanceModel, process_instance: ProcessInstanceModel,
bpmn_definition_to_task_definitions_mappings: dict, bpmn_definition_to_task_definitions_mappings: dict,
secondary_engine_step_delegate: Optional[EngineStepDelegate] = None, secondary_engine_step_delegate: Optional[EngineStepDelegate] = None,
) -> None: ) -> None:
self.secondary_engine_step_delegate = secondary_engine_step_delegate self.secondary_engine_step_delegate = secondary_engine_step_delegate
self.process_instance = process_instance self.process_instance = process_instance
@ -132,12 +132,12 @@ class TaskModelSavingDelegate(EngineStepDelegate):
# excludes COMPLETED. the others were required to get PP1 to go to completion. # excludes COMPLETED. the others were required to get PP1 to go to completion.
# process FUTURE tasks because Boundary events are not processed otherwise. # process FUTURE tasks because Boundary events are not processed otherwise.
for waiting_spiff_task in bpmn_process_instance.get_tasks( for waiting_spiff_task in bpmn_process_instance.get_tasks(
TaskState.WAITING TaskState.WAITING
| TaskState.CANCELLED | TaskState.CANCELLED
| TaskState.READY | TaskState.READY
| TaskState.MAYBE | TaskState.MAYBE
| TaskState.LIKELY | TaskState.LIKELY
| TaskState.FUTURE | TaskState.FUTURE
): ):
# these will be removed from the parent and then ignored # these will be removed from the parent and then ignored
if waiting_spiff_task._has_state(TaskState.PREDICTED_MASK): if waiting_spiff_task._has_state(TaskState.PREDICTED_MASK):
@ -243,6 +243,7 @@ class GreedyExecutionStrategy(ExecutionStrategy):
if non_human_waiting_task is not None: if non_human_waiting_task is not None:
self.run_until_user_input_required(exit_at) self.run_until_user_input_required(exit_at)
class RunUntilServiceTaskExecutionStrategy(ExecutionStrategy): class RunUntilServiceTaskExecutionStrategy(ExecutionStrategy):
"""For illustration purposes, not currently integrated. """For illustration purposes, not currently integrated.
@ -264,22 +265,29 @@ class RunUntilServiceTaskExecutionStrategy(ExecutionStrategy):
self.delegate.after_engine_steps(bpmn_process_instance) self.delegate.after_engine_steps(bpmn_process_instance)
class RunUntilUserMessageExecutionStrategy(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, or
until there are no other engine steps to complete.""" until there are no other engine steps to complete."""
def get_engine_steps(self, bpmn_process_instance: BpmnWorkflow) -> List[SpiffTask]:
return list([t for t in bpmn_process_instance.get_tasks(TaskState.READY) \
if t.task_spec.spec_type not in ["User Task", "Manual Task"] and
not (hasattr(t.task_spec, "extensions") and
t.task_spec.extensions.get("instructionsForEndUser", None))
])
def spiff_run(self, bpmn_process_instance: BpmnWorkflow, exit_at: None = None) -> None: def spiff_run(self, bpmn_process_instance: BpmnWorkflow, exit_at: None = None) -> None:
engine_steps = self.get_ready_engine_steps(bpmn_process_instance)
engine_steps = self.get_engine_steps(bpmn_process_instance)
while engine_steps: while engine_steps:
for spiff_task in engine_steps: for task in engine_steps:
self.delegate.will_complete_task(spiff_task) self.delegate.will_complete_task(task)
spiff_task.run() task.run()
self.delegate.did_complete_task(spiff_task) self.delegate.did_complete_task(task)
if spiff_task.task_spec.properties.get("instructionsForEndUser", None) is not None: engine_steps = self.get_engine_steps(bpmn_process_instance)
break
engine_steps = self.get_ready_engine_steps(bpmn_process_instance)
self.delegate.after_engine_steps(bpmn_process_instance) self.delegate.after_engine_steps(bpmn_process_instance)
class OneAtATimeExecutionStrategy(ExecutionStrategy): class OneAtATimeExecutionStrategy(ExecutionStrategy):
"""When you want to run only one engine step at a time.""" """When you want to run only one engine step at a time."""
@ -297,7 +305,7 @@ def execution_strategy_named(name: str, delegate: EngineStepDelegate) -> Executi
cls = { cls = {
"greedy": GreedyExecutionStrategy, "greedy": GreedyExecutionStrategy,
"run_until_service_task": RunUntilServiceTaskExecutionStrategy, "run_until_service_task": RunUntilServiceTaskExecutionStrategy,
"run_until_user_message": RunUntilUserMessageExecutionStrategy, "run_until_user_message": RunUntilUserTaskOrMessageExecutionStrategy,
"one_at_a_time": OneAtATimeExecutionStrategy, "one_at_a_time": OneAtATimeExecutionStrategy,
}[name] }[name]
@ -307,16 +315,17 @@ def execution_strategy_named(name: str, delegate: EngineStepDelegate) -> Executi
ProcessInstanceCompleter = Callable[[BpmnWorkflow], None] ProcessInstanceCompleter = Callable[[BpmnWorkflow], None]
ProcessInstanceSaver = Callable[[], None] ProcessInstanceSaver = Callable[[], None]
class WorkflowExecutionService: class WorkflowExecutionService:
"""Provides the driver code for workflow execution.""" """Provides the driver code for workflow execution."""
def __init__( def __init__(
self, self,
bpmn_process_instance: BpmnWorkflow, bpmn_process_instance: BpmnWorkflow,
process_instance_model: ProcessInstanceModel, process_instance_model: ProcessInstanceModel,
execution_strategy: ExecutionStrategy, execution_strategy: ExecutionStrategy,
process_instance_completer: ProcessInstanceCompleter, process_instance_completer: ProcessInstanceCompleter,
process_instance_saver: ProcessInstanceSaver, process_instance_saver: ProcessInstanceSaver,
): ):
"""__init__.""" """__init__."""
self.bpmn_process_instance = bpmn_process_instance self.bpmn_process_instance = bpmn_process_instance
@ -366,7 +375,8 @@ class WorkflowExecutionService:
for bpmn_message in bpmn_messages: for bpmn_message in bpmn_messages:
message_instance = MessageInstanceModel( message_instance = MessageInstanceModel(
process_instance_id=self.process_instance_model.id, process_instance_id=self.process_instance_model.id,
user_id=self.process_instance_model.process_initiator_id, # TODO: use the correct swimlane user when that is set up user_id=self.process_instance_model.process_initiator_id,
# TODO: use the correct swimlane user when that is set up
message_type="send", message_type="send",
name=bpmn_message.name, name=bpmn_message.name,
payload=bpmn_message.payload, payload=bpmn_message.payload,
@ -392,12 +402,12 @@ class WorkflowExecutionService:
for event in waiting_message_events: for event in waiting_message_events:
# Ensure we are only creating one message instance for each waiting message # Ensure we are only creating one message instance for each waiting message
if ( if (
MessageInstanceModel.query.filter_by( MessageInstanceModel.query.filter_by(
process_instance_id=self.process_instance_model.id, process_instance_id=self.process_instance_model.id,
message_type="receive", message_type="receive",
name=event["name"], name=event["name"],
).count() ).count()
> 0 > 0
): ):
continue continue

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

@ -1,14 +1,19 @@
import { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import {useNavigate, useParams} from 'react-router-dom'; import { useNavigate, useParams } from 'react-router-dom';
import { fetchEventSource } from '@microsoft/fetch-event-source'; import { fetchEventSource } from '@microsoft/fetch-event-source';
// @ts-ignore
import { Loading, Grid, Column } from '@carbon/react';
import { BACKEND_BASE_URL } from '../config'; import { BACKEND_BASE_URL } from '../config';
import { getBasicHeaders } from '../services/HttpService'; import { getBasicHeaders } from '../services/HttpService';
import {Task} from '../interfaces';
// @ts-ignore
import InstructionsForEndUser from '../components/InstructionsForEndUser'; import InstructionsForEndUser from '../components/InstructionsForEndUser';
import ProcessBreadcrumb from '../components/ProcessBreadcrumb';
export default function ProcessInterstitial() { export default function ProcessInterstitial() {
const [data, setData] = useState<any[]>([]); const [data, setData] = useState<any[]>([]);
const [lastTask, setLastTask] = useState<any>(null); const [lastTask, setLastTask] = useState<any>(null);
const [status, setStatus] = useState<string>('running');
const params = useParams(); const params = useParams();
const navigate = useNavigate(); const navigate = useNavigate();
@ -24,7 +29,8 @@ export default function ProcessInterstitial() {
setLastTask(task); setLastTask(task);
}, },
onclose() { onclose() {
console.log("Connection Closed by the Server"); setStatus('closed');
console.log('Connection Closed by the Server');
}, },
} }
); );
@ -42,30 +48,63 @@ export default function ProcessInterstitial() {
return undefined; return undefined;
}, [lastTask]); }, [lastTask]);
const processStatusImage = () => {
if (status !== 'running') {
setStatus(lastTask.state);
}
console.log(`Status is : ${status}}`);
console.log('last task is : ', lastTask);
switch (status) {
case 'running':
return (
<Loading description="Active loading indicator" withOverlay={false} />
);
case 'WAITING':
return <img src="/interstitial/clock.png" alt="Waiting ...." />;
case 'COMPLETED':
return <img src="/interstitial/checkmark.png" alt="Completed" />;
default:
return null;
}
};
if (lastTask) {
return (
<>
<ProcessBreadcrumb
hotCrumbs={[
['Process Groups', '/admin'],
{
entityToExplode: lastTask.process_model_identifier,
entityType: 'process-model-id',
linkLastItem: true,
},
[`Process Instance Id: ${lastTask.process_instance_id}`],
]}
/>
<h1 style={{display: 'inline-flex', alignItems: 'center'}}>
{processStatusImage()}
{lastTask.process_model_display_name}: {lastTask.process_instance_id}
</h1>
return ( <Grid condensed fullWidth>
<div className="container"> <Column md={6} lg={8} sm={4}>
<h3 className="p-3 text-center">React - Display a list of items</h3> <table className="table table-bordered">
<table className="table table-striped table-bordered"> <tbody>
<thead> {data &&
<tr> data.map((d) => (
<th>Task Title</th> <tr key={d.id}>
</tr> <td><h3>{d.title}</h3></td>
</thead> <td>
<tbody> <InstructionsForEndUser task={d} />
{data && </td>
data.map((d) => ( </tr>
<tr key={d.id}> ))}
<td>{d.title}</td> </tbody>
<td>{d.state}</td> </table>
<td>{d.type}</td> </Column>
<td> </Grid>
<InstructionsForEndUser task={d} /> </>
</td> );
</tr> }
))} return null;
</tbody>
</table>
</div>
);
} }

View File

@ -17,7 +17,6 @@ import {
import MDEditor from '@uiw/react-md-editor'; 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 Loading from '../themes/carbon';
import HttpService from '../services/HttpService'; import HttpService from '../services/HttpService';
import useAPIError from '../hooks/UseApiError'; import useAPIError from '../hooks/UseApiError';
import { modifyProcessIdentifierForPathParam } from '../helpers'; import { modifyProcessIdentifierForPathParam } from '../helpers';