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["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)
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

View File

@ -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"),

View File

@ -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,6 +315,7 @@ 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."""
@ -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,

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 {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,23 +48,52 @@ 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 (
<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>
<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>
<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>{d.title}</td>
<td>{d.state}</td>
<td>{d.type}</td>
<td><h3>{d.title}</h3></td>
<td>
<InstructionsForEndUser task={d} />
</td>
@ -66,6 +101,10 @@ export default function ProcessInterstitial() {
))}
</tbody>
</table>
</div>
</Column>
</Grid>
</>
);
}
return null;
}

View File

@ -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';