mirror of
https://github.com/sartography/spiff-arena.git
synced 2025-01-11 10:06:09 +00:00
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:
parent
fb2af46521
commit
02e8add28f
@ -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["instructionsForEndUser"]:
|
||||
try:
|
||||
task.properties["instructionsForEndUser"] = _render_jinja_template(
|
||||
instructions = _render_jinja_template(
|
||||
task.properties["instructionsForEndUser"], spiff_task
|
||||
)
|
||||
task.properties["instructionsForEndUser"] = instructions
|
||||
return instructions
|
||||
except WorkflowTaskException as wfe:
|
||||
wfe.add_note("Failed to render instructions for end user.")
|
||||
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):
|
||||
process_instance = _find_process_instance_by_id_or_raise(process_instance_id)
|
||||
processor = ProcessInstanceProcessor(process_instance)
|
||||
|
||||
reported_ids = [] # bit of an issue with end tasks showing as getting completed twice.
|
||||
def get_data():
|
||||
spiff_task = processor.next_task()
|
||||
last_task = None
|
||||
while last_task != spiff_task:
|
||||
task = ProcessInstanceService.spiff_task_to_api_task(processor, processor.next_task())
|
||||
_render_instructions_for_end_user(spiff_task, task)
|
||||
yield f'data: {current_app.json.dumps(task)} \n\n'
|
||||
instructions = _render_instructions_for_end_user(spiff_task, task)
|
||||
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
|
||||
processor.do_engine_steps(execution_strategy_name="run_until_user_message")
|
||||
processor.do_engine_steps(execution_strategy_name="one_at_a_time")
|
||||
spiff_task = processor.next_task()
|
||||
# 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?
|
||||
return
|
||||
|
||||
|
@ -443,6 +443,7 @@ class ProcessInstanceService:
|
||||
process_identifier=spiff_task.task_spec._wf_spec.name,
|
||||
process_instance_id=processor.process_instance_model.id,
|
||||
process_model_identifier=processor.process_model_identifier,
|
||||
process_model_display_name=processor.process_model_display_name,
|
||||
properties=props,
|
||||
parent=parent_id,
|
||||
event_definition=serialized_task_spec.get("event_definition"),
|
||||
|
@ -50,11 +50,11 @@ class TaskModelSavingDelegate(EngineStepDelegate):
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
serializer: BpmnWorkflowSerializer,
|
||||
process_instance: ProcessInstanceModel,
|
||||
bpmn_definition_to_task_definitions_mappings: dict,
|
||||
secondary_engine_step_delegate: Optional[EngineStepDelegate] = None,
|
||||
self,
|
||||
serializer: BpmnWorkflowSerializer,
|
||||
process_instance: ProcessInstanceModel,
|
||||
bpmn_definition_to_task_definitions_mappings: dict,
|
||||
secondary_engine_step_delegate: Optional[EngineStepDelegate] = None,
|
||||
) -> None:
|
||||
self.secondary_engine_step_delegate = secondary_engine_step_delegate
|
||||
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.
|
||||
# process FUTURE tasks because Boundary events are not processed otherwise.
|
||||
for waiting_spiff_task in bpmn_process_instance.get_tasks(
|
||||
TaskState.WAITING
|
||||
| TaskState.CANCELLED
|
||||
| TaskState.READY
|
||||
| TaskState.MAYBE
|
||||
| TaskState.LIKELY
|
||||
| TaskState.FUTURE
|
||||
TaskState.WAITING
|
||||
| TaskState.CANCELLED
|
||||
| TaskState.READY
|
||||
| TaskState.MAYBE
|
||||
| TaskState.LIKELY
|
||||
| TaskState.FUTURE
|
||||
):
|
||||
# these will be removed from the parent and then ignored
|
||||
if waiting_spiff_task._has_state(TaskState.PREDICTED_MASK):
|
||||
@ -243,6 +243,7 @@ class GreedyExecutionStrategy(ExecutionStrategy):
|
||||
if non_human_waiting_task is not None:
|
||||
self.run_until_user_input_required(exit_at)
|
||||
|
||||
|
||||
class RunUntilServiceTaskExecutionStrategy(ExecutionStrategy):
|
||||
"""For illustration purposes, not currently integrated.
|
||||
|
||||
@ -264,22 +265,29 @@ class RunUntilServiceTaskExecutionStrategy(ExecutionStrategy):
|
||||
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
|
||||
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:
|
||||
engine_steps = self.get_ready_engine_steps(bpmn_process_instance)
|
||||
|
||||
engine_steps = self.get_engine_steps(bpmn_process_instance)
|
||||
while engine_steps:
|
||||
for spiff_task in engine_steps:
|
||||
self.delegate.will_complete_task(spiff_task)
|
||||
spiff_task.run()
|
||||
self.delegate.did_complete_task(spiff_task)
|
||||
if spiff_task.task_spec.properties.get("instructionsForEndUser", None) is not None:
|
||||
break
|
||||
engine_steps = self.get_ready_engine_steps(bpmn_process_instance)
|
||||
for task in engine_steps:
|
||||
self.delegate.will_complete_task(task)
|
||||
task.run()
|
||||
self.delegate.did_complete_task(task)
|
||||
engine_steps = self.get_engine_steps(bpmn_process_instance)
|
||||
self.delegate.after_engine_steps(bpmn_process_instance)
|
||||
|
||||
|
||||
class OneAtATimeExecutionStrategy(ExecutionStrategy):
|
||||
"""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 = {
|
||||
"greedy": GreedyExecutionStrategy,
|
||||
"run_until_service_task": RunUntilServiceTaskExecutionStrategy,
|
||||
"run_until_user_message": RunUntilUserMessageExecutionStrategy,
|
||||
"run_until_user_message": RunUntilUserTaskOrMessageExecutionStrategy,
|
||||
"one_at_a_time": OneAtATimeExecutionStrategy,
|
||||
}[name]
|
||||
|
||||
@ -307,16 +315,17 @@ def execution_strategy_named(name: str, delegate: EngineStepDelegate) -> Executi
|
||||
ProcessInstanceCompleter = Callable[[BpmnWorkflow], None]
|
||||
ProcessInstanceSaver = Callable[[], None]
|
||||
|
||||
|
||||
class WorkflowExecutionService:
|
||||
"""Provides the driver code for workflow execution."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
bpmn_process_instance: BpmnWorkflow,
|
||||
process_instance_model: ProcessInstanceModel,
|
||||
execution_strategy: ExecutionStrategy,
|
||||
process_instance_completer: ProcessInstanceCompleter,
|
||||
process_instance_saver: ProcessInstanceSaver,
|
||||
self,
|
||||
bpmn_process_instance: BpmnWorkflow,
|
||||
process_instance_model: ProcessInstanceModel,
|
||||
execution_strategy: ExecutionStrategy,
|
||||
process_instance_completer: ProcessInstanceCompleter,
|
||||
process_instance_saver: ProcessInstanceSaver,
|
||||
):
|
||||
"""__init__."""
|
||||
self.bpmn_process_instance = bpmn_process_instance
|
||||
@ -366,7 +375,8 @@ class WorkflowExecutionService:
|
||||
for bpmn_message in bpmn_messages:
|
||||
message_instance = MessageInstanceModel(
|
||||
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",
|
||||
name=bpmn_message.name,
|
||||
payload=bpmn_message.payload,
|
||||
@ -392,12 +402,12 @@ class WorkflowExecutionService:
|
||||
for event in waiting_message_events:
|
||||
# Ensure we are only creating one message instance for each waiting message
|
||||
if (
|
||||
MessageInstanceModel.query.filter_by(
|
||||
process_instance_id=self.process_instance_model.id,
|
||||
message_type="receive",
|
||||
name=event["name"],
|
||||
).count()
|
||||
> 0
|
||||
MessageInstanceModel.query.filter_by(
|
||||
process_instance_id=self.process_instance_model.id,
|
||||
message_type="receive",
|
||||
name=event["name"],
|
||||
).count()
|
||||
> 0
|
||||
):
|
||||
continue
|
||||
|
||||
|
BIN
spiffworkflow-frontend/public/interstitial/checkmark.png
Normal file
BIN
spiffworkflow-frontend/public/interstitial/checkmark.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.9 KiB |
BIN
spiffworkflow-frontend/public/interstitial/clock.png
Normal file
BIN
spiffworkflow-frontend/public/interstitial/clock.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.1 KiB |
@ -1,14 +1,19 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import {useNavigate, useParams} from 'react-router-dom';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
import { fetchEventSource } from '@microsoft/fetch-event-source';
|
||||
// @ts-ignore
|
||||
import { Loading, Grid, Column } from '@carbon/react';
|
||||
import { BACKEND_BASE_URL } from '../config';
|
||||
import { getBasicHeaders } from '../services/HttpService';
|
||||
import {Task} from '../interfaces';
|
||||
|
||||
// @ts-ignore
|
||||
import InstructionsForEndUser from '../components/InstructionsForEndUser';
|
||||
import ProcessBreadcrumb from '../components/ProcessBreadcrumb';
|
||||
|
||||
export default function ProcessInterstitial() {
|
||||
const [data, setData] = useState<any[]>([]);
|
||||
const [lastTask, setLastTask] = useState<any>(null);
|
||||
const [status, setStatus] = useState<string>('running');
|
||||
const params = useParams();
|
||||
const navigate = useNavigate();
|
||||
|
||||
@ -24,7 +29,8 @@ export default function ProcessInterstitial() {
|
||||
setLastTask(task);
|
||||
},
|
||||
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;
|
||||
}, [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 (
|
||||
<div className="container">
|
||||
<h3 className="p-3 text-center">React - Display a list of items</h3>
|
||||
<table className="table table-striped table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Task Title</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{data &&
|
||||
data.map((d) => (
|
||||
<tr key={d.id}>
|
||||
<td>{d.title}</td>
|
||||
<td>{d.state}</td>
|
||||
<td>{d.type}</td>
|
||||
<td>
|
||||
<InstructionsForEndUser task={d} />
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
<Grid condensed fullWidth>
|
||||
<Column md={6} lg={8} sm={4}>
|
||||
<table className="table table-bordered">
|
||||
<tbody>
|
||||
{data &&
|
||||
data.map((d) => (
|
||||
<tr key={d.id}>
|
||||
<td><h3>{d.title}</h3></td>
|
||||
<td>
|
||||
<InstructionsForEndUser task={d} />
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</Column>
|
||||
</Grid>
|
||||
</>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -17,7 +17,6 @@ import {
|
||||
import MDEditor from '@uiw/react-md-editor';
|
||||
// eslint-disable-next-line import/no-named-as-default
|
||||
import Form from '../themes/carbon';
|
||||
import Loading from '../themes/carbon';
|
||||
import HttpService from '../services/HttpService';
|
||||
import useAPIError from '../hooks/UseApiError';
|
||||
import { modifyProcessIdentifierForPathParam } from '../helpers';
|
||||
|
Loading…
x
Reference in New Issue
Block a user