mirror of
https://github.com/sartography/spiff-arena.git
synced 2025-02-23 14:48:35 +00:00
make sure we can properly display lanes.
This commit is contained in:
parent
02e8add28f
commit
933ccc02ef
@ -4,6 +4,7 @@ from __future__ import annotations
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
from flask import g
|
||||||
from sqlalchemy import ForeignKey
|
from sqlalchemy import ForeignKey
|
||||||
from sqlalchemy.orm import relationship
|
from sqlalchemy.orm import relationship
|
||||||
|
|
||||||
@ -68,12 +69,19 @@ class HumanTaskModel(SpiffworkflowBaseDBModel):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def to_task(cls, task: HumanTaskModel) -> Task:
|
def to_task(cls, task: HumanTaskModel) -> Task:
|
||||||
"""To_task."""
|
"""To_task."""
|
||||||
|
can_complete = False
|
||||||
|
for user in task.human_task_users:
|
||||||
|
if user.user_id == g.user.id:
|
||||||
|
can_complete = True
|
||||||
|
break
|
||||||
|
|
||||||
new_task = Task(
|
new_task = Task(
|
||||||
task.task_id,
|
task.task_id,
|
||||||
task.task_name,
|
task.task_name,
|
||||||
task.task_title,
|
task.task_title,
|
||||||
task.task_type,
|
task.task_type,
|
||||||
task.task_status,
|
task.task_status,
|
||||||
|
can_complete,
|
||||||
process_instance_id=task.process_instance_id,
|
process_instance_id=task.process_instance_id,
|
||||||
)
|
)
|
||||||
if hasattr(task, "process_model_display_name"):
|
if hasattr(task, "process_model_display_name"):
|
||||||
|
@ -88,6 +88,7 @@ class Task:
|
|||||||
title: str,
|
title: str,
|
||||||
type: str,
|
type: str,
|
||||||
state: str,
|
state: str,
|
||||||
|
can_complete: bool,
|
||||||
lane: Union[str, None] = None,
|
lane: Union[str, None] = None,
|
||||||
form: None = None,
|
form: None = None,
|
||||||
documentation: str = "",
|
documentation: str = "",
|
||||||
@ -116,6 +117,7 @@ class Task:
|
|||||||
self.title = title
|
self.title = title
|
||||||
self.type = type
|
self.type = type
|
||||||
self.state = state
|
self.state = state
|
||||||
|
self.can_complete = can_complete
|
||||||
self.form = form
|
self.form = form
|
||||||
self.documentation = documentation
|
self.documentation = documentation
|
||||||
self.lane = lane
|
self.lane = lane
|
||||||
@ -160,6 +162,7 @@ class Task:
|
|||||||
"type": self.type,
|
"type": self.type,
|
||||||
"state": self.state,
|
"state": self.state,
|
||||||
"lane": self.lane,
|
"lane": self.lane,
|
||||||
|
"can_complete": self.can_complete,
|
||||||
"form": self.form,
|
"form": self.form,
|
||||||
"documentation": self.documentation,
|
"documentation": self.documentation,
|
||||||
"data": self.data,
|
"data": self.data,
|
||||||
|
@ -10,7 +10,7 @@ from typing import Tuple
|
|||||||
from urllib.parse import unquote
|
from urllib.parse import unquote
|
||||||
|
|
||||||
import sentry_sdk
|
import sentry_sdk
|
||||||
from flask import current_app
|
from flask import current_app, g
|
||||||
from SpiffWorkflow.bpmn.specs.events.IntermediateEvent import _BoundaryEventParent # type: ignore
|
from SpiffWorkflow.bpmn.specs.events.IntermediateEvent import _BoundaryEventParent # type: ignore
|
||||||
from SpiffWorkflow.task import Task as SpiffTask # type: ignore
|
from SpiffWorkflow.task import Task as SpiffTask # type: ignore
|
||||||
|
|
||||||
@ -26,7 +26,8 @@ from spiffworkflow_backend.models.process_instance_file_data import (
|
|||||||
from spiffworkflow_backend.models.process_model import ProcessModelInfo
|
from spiffworkflow_backend.models.process_model import ProcessModelInfo
|
||||||
from spiffworkflow_backend.models.task import Task
|
from spiffworkflow_backend.models.task import Task
|
||||||
from spiffworkflow_backend.models.user import UserModel
|
from spiffworkflow_backend.models.user import UserModel
|
||||||
from spiffworkflow_backend.services.authorization_service import AuthorizationService
|
from spiffworkflow_backend.services.authorization_service import AuthorizationService, HumanTaskNotFoundError, \
|
||||||
|
UserDoesNotHaveAccessToTaskError
|
||||||
from spiffworkflow_backend.services.git_service import GitCommandError
|
from spiffworkflow_backend.services.git_service import GitCommandError
|
||||||
from spiffworkflow_backend.services.git_service import GitService
|
from spiffworkflow_backend.services.git_service import GitService
|
||||||
from spiffworkflow_backend.services.process_instance_processor import (
|
from spiffworkflow_backend.services.process_instance_processor import (
|
||||||
@ -422,6 +423,18 @@ class ProcessInstanceService:
|
|||||||
else:
|
else:
|
||||||
lane = None
|
lane = None
|
||||||
|
|
||||||
|
# Check for a human task, and if it exists, check to see if the current user
|
||||||
|
# can complete it.
|
||||||
|
can_complete = False
|
||||||
|
try:
|
||||||
|
AuthorizationService.assert_user_can_complete_spiff_task(processor.process_instance_model.id, spiff_task,
|
||||||
|
g.user)
|
||||||
|
can_complete = True
|
||||||
|
except HumanTaskNotFoundError as e:
|
||||||
|
can_complete = False
|
||||||
|
except UserDoesNotHaveAccessToTaskError as ude:
|
||||||
|
can_complete = False
|
||||||
|
|
||||||
if hasattr(spiff_task.task_spec, "spec"):
|
if hasattr(spiff_task.task_spec, "spec"):
|
||||||
call_activity_process_identifier = spiff_task.task_spec.spec
|
call_activity_process_identifier = spiff_task.task_spec.spec
|
||||||
else:
|
else:
|
||||||
@ -439,6 +452,7 @@ class ProcessInstanceService:
|
|||||||
spiff_task.task_spec.description,
|
spiff_task.task_spec.description,
|
||||||
task_type,
|
task_type,
|
||||||
spiff_task.get_state_name(),
|
spiff_task.get_state_name(),
|
||||||
|
can_complete=can_complete,
|
||||||
lane=lane,
|
lane=lane,
|
||||||
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,
|
||||||
|
@ -41,9 +41,7 @@ export interface Task {
|
|||||||
id: number;
|
id: number;
|
||||||
guid: string;
|
guid: string;
|
||||||
bpmn_identifier: string;
|
bpmn_identifier: string;
|
||||||
|
|
||||||
bpmn_name?: string;
|
bpmn_name?: string;
|
||||||
|
|
||||||
bpmn_process_direct_parent_guid: string;
|
bpmn_process_direct_parent_guid: string;
|
||||||
bpmn_process_definition_identifier: string;
|
bpmn_process_definition_identifier: string;
|
||||||
data: any;
|
data: any;
|
||||||
@ -64,7 +62,7 @@ export interface TaskIds {
|
|||||||
export interface ProcessInstanceTask {
|
export interface ProcessInstanceTask {
|
||||||
id: string;
|
id: string;
|
||||||
task_id: string;
|
task_id: string;
|
||||||
|
can_complete: boolean;
|
||||||
calling_subprocess_task_id: string;
|
calling_subprocess_task_id: string;
|
||||||
created_at_in_seconds: number;
|
created_at_in_seconds: number;
|
||||||
current_user_is_potential_owner: number;
|
current_user_is_potential_owner: number;
|
||||||
|
@ -9,6 +9,7 @@ import { getBasicHeaders } from '../services/HttpService';
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import InstructionsForEndUser from '../components/InstructionsForEndUser';
|
import InstructionsForEndUser from '../components/InstructionsForEndUser';
|
||||||
import ProcessBreadcrumb from '../components/ProcessBreadcrumb';
|
import ProcessBreadcrumb from '../components/ProcessBreadcrumb';
|
||||||
|
import { ProcessInstanceTask } from '../interfaces';
|
||||||
|
|
||||||
export default function ProcessInterstitial() {
|
export default function ProcessInterstitial() {
|
||||||
const [data, setData] = useState<any[]>([]);
|
const [data, setData] = useState<any[]>([]);
|
||||||
@ -39,7 +40,11 @@ export default function ProcessInterstitial() {
|
|||||||
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
|
||||||
// we end up redirecting back to the TaskShow page.
|
// we end up redirecting back to the TaskShow page.
|
||||||
if (lastTask && ['User Task', 'Manual Task'].includes(lastTask.type)) {
|
if (
|
||||||
|
lastTask &&
|
||||||
|
lastTask.can_complete &&
|
||||||
|
['User Task', 'Manual Task'].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}`);
|
||||||
}, 1000);
|
}, 1000);
|
||||||
@ -52,6 +57,12 @@ export default function ProcessInterstitial() {
|
|||||||
if (status !== 'running') {
|
if (status !== 'running') {
|
||||||
setStatus(lastTask.state);
|
setStatus(lastTask.state);
|
||||||
}
|
}
|
||||||
|
if (
|
||||||
|
!lastTask.can_complete &&
|
||||||
|
['User Task', 'Manual Task'].includes(lastTask.type)
|
||||||
|
) {
|
||||||
|
setStatus('LOCKED');
|
||||||
|
}
|
||||||
console.log(`Status is : ${status}}`);
|
console.log(`Status is : ${status}}`);
|
||||||
console.log('last task is : ', lastTask);
|
console.log('last task is : ', lastTask);
|
||||||
switch (status) {
|
switch (status) {
|
||||||
@ -63,10 +74,25 @@ export default function ProcessInterstitial() {
|
|||||||
return <img src="/interstitial/clock.png" alt="Waiting ...." />;
|
return <img src="/interstitial/clock.png" alt="Waiting ...." />;
|
||||||
case 'COMPLETED':
|
case 'COMPLETED':
|
||||||
return <img src="/interstitial/checkmark.png" alt="Completed" />;
|
return <img src="/interstitial/checkmark.png" alt="Completed" />;
|
||||||
|
case 'LOCKED':
|
||||||
|
return <img src="/interstitial/lock.png" alt="Locked, Waiting on someone else." />;
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const userMessage = (myTask: ProcessInstanceTask) => {
|
||||||
|
if (
|
||||||
|
!myTask.can_complete &&
|
||||||
|
['User Task', 'Manual Task'].includes(myTask.type)
|
||||||
|
) {
|
||||||
|
return (
|
||||||
|
<div>This task is assigned to another user or group to complete. </div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return <InstructionsForEndUser task={myTask} />;
|
||||||
|
};
|
||||||
|
|
||||||
if (lastTask) {
|
if (lastTask) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -93,9 +119,11 @@ export default function ProcessInterstitial() {
|
|||||||
{data &&
|
{data &&
|
||||||
data.map((d) => (
|
data.map((d) => (
|
||||||
<tr key={d.id}>
|
<tr key={d.id}>
|
||||||
<td><h3>{d.title}</h3></td>
|
|
||||||
<td>
|
<td>
|
||||||
<InstructionsForEndUser task={d} />
|
<h3>{d.title}</h3>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<p>{userMessage(d)}</p>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
))}
|
))}
|
||||||
|
@ -91,8 +91,6 @@ function TypeAheadWidget({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class UnexpectedHumanTaskType extends Error {
|
class UnexpectedHumanTaskType extends Error {
|
||||||
constructor(message: string) {
|
constructor(message: string) {
|
||||||
super(message);
|
super(message);
|
||||||
@ -121,11 +119,24 @@ export default function TaskShow() {
|
|||||||
// eslint-disable-next-line sonarjs/no-duplicate-string
|
// eslint-disable-next-line sonarjs/no-duplicate-string
|
||||||
const supportedHumanTaskTypes = ['User Task', 'Manual Task'];
|
const supportedHumanTaskTypes = ['User Task', 'Manual Task'];
|
||||||
|
|
||||||
|
const navigateToInterstitial = (myTask: ProcessInstanceTask) => {
|
||||||
|
navigate(
|
||||||
|
`/process/${modifyProcessIdentifierForPathParam(
|
||||||
|
myTask.process_model_identifier
|
||||||
|
)}/${myTask.process_instance_id}/interstitial`
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const processResult = (result: ProcessInstanceTask) => {
|
const processResult = (result: ProcessInstanceTask) => {
|
||||||
setTask(result);
|
setTask(result);
|
||||||
setDisabled(false);
|
setDisabled(false);
|
||||||
|
|
||||||
|
if (!result.can_complete) {
|
||||||
|
navigateToInterstitial(result);
|
||||||
|
}
|
||||||
|
|
||||||
/* Disable call to load previous tasks -- do not display menu.
|
/* Disable call to load previous tasks -- do not display menu.
|
||||||
const url = `/v1.0/process-instances/for-me/${modifyProcessIdentifierForPathParam(
|
const url = `/v1.0/process-instances/for-me/${modifyProcessIdentifierForPathParam(
|
||||||
result.process_model_identifier
|
result.process_model_identifier
|
||||||
@ -162,14 +173,10 @@ export default function TaskShow() {
|
|||||||
if (result.ok) {
|
if (result.ok) {
|
||||||
navigate(`/tasks`);
|
navigate(`/tasks`);
|
||||||
} else if (result.process_instance_id) {
|
} else if (result.process_instance_id) {
|
||||||
if (result.type in supportedHumanTaskTypes) {
|
if (result.can_complete) {
|
||||||
navigate(`/tasks/${result.process_instance_id}/${result.id}`);
|
navigate(`/tasks/${result.process_instance_id}/${result.id}`);
|
||||||
} else {
|
} else {
|
||||||
navigate(
|
navigateToInterstitial(result);
|
||||||
`/process/${modifyProcessIdentifierForPathParam(
|
|
||||||
result.process_model_identifier
|
|
||||||
)}/${result.process_instance_id}/interstitial`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
addError(result);
|
addError(result);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user