X-Git-Url: https://git.arvados.org/arvados-workbench2.git/blobdiff_plain/a2bf1ed3a15cf55e426c51ad8e6febd7ff2e3020..756da23ade34fd225e80e448fb27795d453294c6:/src/store/processes/process.ts diff --git a/src/store/processes/process.ts b/src/store/processes/process.ts index ab8093b8..ec55535d 100644 --- a/src/store/processes/process.ts +++ b/src/store/processes/process.ts @@ -4,11 +4,11 @@ import { ContainerRequestResource, ContainerRequestState } from '../../models/container-request'; import { ContainerResource, ContainerState } from '../../models/container'; -import { ResourcesState, getResource } from '~/store/resources/resources'; +import { ResourcesState, getResource } from 'store/resources/resources'; import { filterResources } from '../resources/resources'; -import { ResourceKind, Resource, extractUuidKind } from '~/models/resource'; -import { getTimeDiff } from '~/common/formatters'; -import { ArvadosTheme } from '~/common/custom-theme'; +import { ResourceKind, Resource, extractUuidKind } from 'models/resource'; +import { getTimeDiff } from 'common/formatters'; +import { ArvadosTheme } from 'common/custom-theme'; export interface Process { containerRequest: ContainerRequestResource; @@ -19,11 +19,14 @@ export enum ProcessStatus { CANCELLED = 'Cancelled', COMPLETED = 'Completed', DRAFT = 'Draft', + FAILING = 'Failing', FAILED = 'Failed', - LOCKED = 'Locked', + ONHOLD = 'On hold', QUEUED = 'Queued', RUNNING = 'Running', + WARNING = 'Warning', UNKNOWN = 'Unknown', + REUSED = 'Reused', } export const getProcess = (uuid: string) => (resources: ResourcesState): Process | undefined => { @@ -56,54 +59,146 @@ export const getSubprocesses = (uuid: string) => (resources: ResourcesState) => return []; }; -export const getProcessRuntime = ({ container }: Process) => - container - ? getTimeDiff(container.finishedAt || '', container.startedAt || '') - : 0; +export const getProcessRuntime = ({ container }: Process) => { + if (container) { + if (container.startedAt === null) { + return 0; + } + if (container.finishedAt === null) { + // Count it from now + return new Date().getTime() - new Date(container.startedAt).getTime(); + } + return getTimeDiff(container.finishedAt, container.startedAt); + } else { + return 0; + } +}; + -export const getProcessStatusColor = (status: string, { customs, palette }: ArvadosTheme) => { +export const getProcessStatusStyles = (status: string, theme: ArvadosTheme): React.CSSProperties => { + let color = theme.customs.colors.grey500; + let running = false; switch (status) { case ProcessStatus.RUNNING: - return customs.colors.blue500; + color = theme.customs.colors.green800; + running = true; + break; case ProcessStatus.COMPLETED: - return customs.colors.green700; + case ProcessStatus.REUSED: + color = theme.customs.colors.green800; + break; + case ProcessStatus.WARNING: + color = theme.customs.colors.green800; + running = true; + break; + case ProcessStatus.FAILING: + color = theme.customs.colors.red900; + running = true; + break; case ProcessStatus.CANCELLED: case ProcessStatus.FAILED: - return customs.colors.red900; + color = theme.customs.colors.red900; + break; + case ProcessStatus.QUEUED: + color = theme.customs.colors.grey600; + running = true; + break; default: - return palette.grey["500"]; + color = theme.customs.colors.grey600; + break; } + + // Using color and running we build the text, border, and background style properties + return { + // Set background color when not running, otherwise use white + backgroundColor: running ? theme.palette.common.white : color, + // Set text color to status color when running, else use white text for solid button + color: running ? color : theme.palette.common.white, + // Set border color when running, else omit the style entirely + ...(running ? { border: `2px solid ${color}` } : {}), + }; }; export const getProcessStatus = ({ containerRequest, container }: Process): ProcessStatus => { switch (true) { + case containerRequest.containerUuid && !container: + return ProcessStatus.UNKNOWN; + + case containerRequest.state === ContainerRequestState.FINAL && + container?.state !== ContainerState.COMPLETE: + // Request was finalized before its container started (or the + // container was cancelled) + return ProcessStatus.CANCELLED; + case containerRequest.state === ContainerRequestState.UNCOMMITTED: return ProcessStatus.DRAFT; - case containerRequest.priority === 0: - case container && container.state === ContainerState.CANCELLED: + case container && container.state === ContainerState.COMPLETE: + if (container?.exitCode === 0) { + if (containerRequest && container.finishedAt) { + // don't compare on createdAt because the container can + // have a slightly earlier creation time when it is created + // in the same transaction as the container request. + // use finishedAt because most people will assume "reused" means + // no additional work needed to be done, it's possible + // to share a running container but calling it "reused" in that case + // is more likely to just be confusing. + const finishedAt = new Date(container.finishedAt).getTime(); + const createdAt = new Date(containerRequest.createdAt).getTime(); + if (finishedAt < createdAt) { + return ProcessStatus.REUSED; + } + } + return ProcessStatus.COMPLETED; + } + return ProcessStatus.FAILED; + + case container?.state === ContainerState.CANCELLED: return ProcessStatus.CANCELLED; - case container && container.state === ContainerState.QUEUED: + case container?.state === ContainerState.QUEUED || + container?.state === ContainerState.LOCKED: + if (containerRequest.priority === 0) { + return ProcessStatus.ONHOLD; + } return ProcessStatus.QUEUED; - case container && container.state === ContainerState.LOCKED: - return ProcessStatus.LOCKED; - - case container && container.state === ContainerState.RUNNING: + case container?.state === ContainerState.RUNNING: + if (!!container?.runtimeStatus.error) { + return ProcessStatus.FAILING; + } + if (!!container?.runtimeStatus.warning) { + return ProcessStatus.WARNING; + } return ProcessStatus.RUNNING; - case container && container.state === ContainerState.COMPLETE && container.exitCode === 0: - return ProcessStatus.COMPLETED; - - case container && container.state === ContainerState.COMPLETE && container.exitCode !== 0: - return ProcessStatus.FAILED; - default: return ProcessStatus.UNKNOWN; } }; +export const isProcessRunnable = ({ containerRequest }: Process): boolean => ( + containerRequest.state === ContainerRequestState.UNCOMMITTED +); + +export const isProcessResumable = ({ containerRequest, container }: Process): boolean => ( + containerRequest.state === ContainerRequestState.COMMITTED && + containerRequest.priority === 0 && + // Don't show run button when container is present & running or cancelled + !(container && (container.state === ContainerState.RUNNING || + container.state === ContainerState.CANCELLED || + container.state === ContainerState.COMPLETE)) +); + +export const isProcessCancelable = ({ containerRequest, container }: Process): boolean => ( + containerRequest.priority !== null && + containerRequest.priority > 0 && + container !== undefined && + (container.state === ContainerState.QUEUED || + container.state === ContainerState.LOCKED || + container.state === ContainerState.RUNNING) +); + const isSubprocess = (containerUuid: string) => (resource: Resource) => resource.kind === ResourceKind.CONTAINER_REQUEST && (resource as ContainerRequestResource).requestingContainerUuid === containerUuid;