Update process statuses, update process status rendering in tables, disable process...
authorMichal Klobukowski <michal.klobukowski@contractors.roche.com>
Tue, 4 Sep 2018 09:54:48 +0000 (11:54 +0200)
committerMichal Klobukowski <michal.klobukowski@contractors.roche.com>
Tue, 4 Sep 2018 09:54:48 +0000 (11:54 +0200)
Feature #13776

Arvados-DCO-1.1-Signed-off-by: Michal Klobukowski <michal.klobukowski@contractors.roche.com>

src/components/data-table/data-column.ts
src/store/favorite-panel/favorite-panel-middleware-service.ts
src/store/processes/process.ts
src/store/project-panel/project-panel-middleware-service.ts
src/views-components/data-explorer/renderers.tsx
src/views/favorite-panel/favorite-panel.tsx
src/views/project-panel/project-panel.tsx

index 90e87a88b0a7225a9cbea0d7b842e5146a593d4c..8dbdf0cc7ad0a1f1d13d2d773a760bda3e289f7a 100644 (file)
@@ -10,7 +10,7 @@ export interface DataColumn<T, F extends DataTableFilterItem = DataTableFilterIt
     name: string;
     selected: boolean;
     configurable: boolean;
-    sortDirection: SortDirection;
+    sortDirection?: SortDirection;
     filters: F[];
     render: (item: T) => React.ReactElement<any>;
     renderHeader?: () => React.ReactElement<any>;
index f808b97e8835882a682694799a0b52cdc63155f9..c385309f71f28d3295e23b718089fab3e334b7f2 100644 (file)
@@ -18,17 +18,18 @@ import { GroupContentsResource, GroupContentsResourcePrefix } from "~/services/g
 import { resourcesActions } from "~/store/resources/resources-actions";
 import { snackbarActions } from '~/store/snackbar/snackbar-actions';
 import { getDataExplorer } from "~/store/data-explorer/data-explorer-reducer";
+import { loadMissingProcessesInformation } from "~/store/project-panel/project-panel-middleware-service";
 
 export class FavoritePanelMiddlewareService extends DataExplorerMiddlewareService {
     constructor(private services: ServiceRepository, id: string) {
         super(id);
     }
 
-    requestItems(api: MiddlewareAPI<Dispatch, RootState>) {
+    async requestItems(api: MiddlewareAPI<Dispatch, RootState>) {
         const dataExplorer = getDataExplorer(api.getState().dataExplorer, this.getId());
         if (!dataExplorer) {
             api.dispatch(favoritesPanelDataExplorerIsNotSet());
-         } else {
+        } else {
 
             const columns = dataExplorer.columns as DataColumns<string, FavoritePanelFilter>;
             const sortColumn = dataExplorer.columns.find(c => c.sortDirection !== SortDirection.NONE);
@@ -48,36 +49,35 @@ export class FavoritePanelMiddlewareService extends DataExplorerMiddlewareServic
                     .addOrder(direction, "name", GroupContentsResourcePrefix.PROCESS)
                     .addOrder(direction, "name", GroupContentsResourcePrefix.PROJECT);
             }
-
-            this.services.favoriteService
-                .list(this.services.authService.getUuid()!, {
-                    limit: dataExplorer.rowsPerPage,
-                    offset: dataExplorer.page * dataExplorer.rowsPerPage,
-                    linkOrder: linkOrder.getOrder(),
-                    contentOrder: contentOrder.getOrder(),
-                    filters: new FilterBuilder()
-                        .addIsA("headUuid", typeFilters.map(filter => filter.type))
-                        .addILike("name", dataExplorer.searchValue)
-                        .getFilters()
-                })
-                .then(response => {
-                    api.dispatch(resourcesActions.SET_RESOURCES(response.items));
-                    api.dispatch(favoritePanelActions.SET_ITEMS({
-                        items: response.items.map(resource => resource.uuid),
-                        itemsAvailable: response.itemsAvailable,
-                        page: Math.floor(response.offset / response.limit),
-                        rowsPerPage: response.limit
-                    }));
-                    api.dispatch<any>(updateFavorites(response.items.map(item => item.uuid)));
-                })
-                .catch(() => {
-                    api.dispatch(favoritePanelActions.SET_ITEMS({
-                        items: [],
-                        itemsAvailable: 0,
-                        page: 0,
-                        rowsPerPage: dataExplorer.rowsPerPage
-                    }));
-                });
+            try {
+                const response = await this.services.favoriteService
+                    .list(this.services.authService.getUuid()!, {
+                        limit: dataExplorer.rowsPerPage,
+                        offset: dataExplorer.page * dataExplorer.rowsPerPage,
+                        linkOrder: linkOrder.getOrder(),
+                        contentOrder: contentOrder.getOrder(),
+                        filters: new FilterBuilder()
+                            .addIsA("headUuid", typeFilters.map(filter => filter.type))
+                            .addILike("name", dataExplorer.searchValue)
+                            .getFilters()
+                    });
+                api.dispatch(resourcesActions.SET_RESOURCES(response.items));
+                await api.dispatch<any>(loadMissingProcessesInformation(response.items));
+                api.dispatch(favoritePanelActions.SET_ITEMS({
+                    items: response.items.map(resource => resource.uuid),
+                    itemsAvailable: response.itemsAvailable,
+                    page: Math.floor(response.offset / response.limit),
+                    rowsPerPage: response.limit
+                }));
+                api.dispatch<any>(updateFavorites(response.items.map(item => item.uuid)));
+            } catch (e) {
+                api.dispatch(favoritePanelActions.SET_ITEMS({
+                    items: [],
+                    itemsAvailable: 0,
+                    page: 0,
+                    rowsPerPage: dataExplorer.rowsPerPage
+                }));
+            }
         }
     }
 }
index 467fc8a92ef83a7c7dd3358ee3f9d860ff3e56fa..c9e62f943455a2ebaae9b8d8b45c16ff8084a62f 100644 (file)
@@ -2,13 +2,12 @@
 //
 // SPDX-License-Identifier: AGPL-3.0
 
-import { ContainerRequestResource } from '../../models/container-request';
-import { ContainerResource } from '../../models/container';
+import { ContainerRequestResource, ContainerRequestState } from '../../models/container-request';
+import { ContainerResource, ContainerState } from '../../models/container';
 import { ResourcesState, getResource } from '~/store/resources/resources';
 import { filterResources } from '../resources/resources';
-import { ResourceKind, Resource } from '~/models/resource';
+import { ResourceKind, Resource, extractUuidKind } from '~/models/resource';
 import { getTimeDiff } from '~/common/formatters';
-import { SubprocessesStatus } from '~/views/process-panel/process-subprocesses-card';
 import { ArvadosTheme } from '~/common/custom-theme';
 
 export interface Process {
@@ -16,16 +15,29 @@ export interface Process {
     container?: ContainerResource;
 }
 
+export enum ProcessStatus {
+    CANCELLED = 'Cancelled',
+    COMPLETED = 'Completed',
+    DRAFT = 'Draft',
+    FAILED = 'Failed',
+    LOCKED = 'Locked',
+    QUEUED = 'Queued',
+    RUNNING = 'Running',
+    UNKNOWN = 'Unknown',
+}
+
 export const getProcess = (uuid: string) => (resources: ResourcesState): Process | undefined => {
-    const containerRequest = getResource<ContainerRequestResource>(uuid)(resources);
-    if (containerRequest) {
-        if (containerRequest.containerUuid) {
-            const container = getResource<ContainerResource>(containerRequest.containerUuid)(resources);
-            if (container) {
-                return { containerRequest, container };
+    if (extractUuidKind(uuid) === ResourceKind.CONTAINER_REQUEST) {
+        const containerRequest = getResource<ContainerRequestResource>(uuid)(resources);
+        if (containerRequest) {
+            if (containerRequest.containerUuid) {
+                const container = getResource<ContainerResource>(containerRequest.containerUuid)(resources);
+                if (container) {
+                    return { containerRequest, container };
+                }
             }
+            return { containerRequest };
         }
-        return { containerRequest };
     }
     return;
 };
@@ -51,27 +63,47 @@ export const getProcessRuntime = ({ container }: Process) =>
 
 export const getProcessStatusColor = (status: string, { customs }: ArvadosTheme) => {
     switch (status) {
-        case SubprocessesStatus.COMPLETED:
+        case ProcessStatus.RUNNING:
+            return customs.colors.blue500;
+        case ProcessStatus.COMPLETED:
             return customs.colors.green700;
-        case SubprocessesStatus.CANCELED:
-            return customs.colors.red900;
-        case SubprocessesStatus.QUEUED:
-            return customs.colors.grey500;
-        case SubprocessesStatus.FAILED:
+        case ProcessStatus.CANCELLED:
+        case ProcessStatus.FAILED:
             return customs.colors.red900;
-        case SubprocessesStatus.ACTIVE:
-            return customs.colors.blue500;
         default:
             return customs.colors.grey500;
     }
 };
 
-export const getProcessStatus = (process: Process) =>
-    process.container
-        ? process.container.state
-        : process.containerRequest.state;
+export const getProcessStatus = ({ containerRequest, container }: Process): ProcessStatus => {
+    switch (true) {
+        case containerRequest.state === ContainerRequestState.UNCOMMITTED:
+            return ProcessStatus.DRAFT;
+
+        case containerRequest.priority === 0:
+        case container && container.state === ContainerState.CANCELLED:
+            return ProcessStatus.CANCELLED;
+
+        case container && container.state === ContainerState.QUEUED:
+            return ProcessStatus.QUEUED;
+
+        case container && container.state === ContainerState.LOCKED:
+            return ProcessStatus.LOCKED;
+
+        case container && container.state === ContainerState.RUNNING:
+            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;
+    }
+};
 
 const isSubprocess = (containerUuid: string) => (resource: Resource) =>
     resource.kind === ResourceKind.CONTAINER_REQUEST
     && (resource as ContainerRequestResource).requestingContainerUuid === containerUuid;
-
index aade4172f99201cc10205109bd8740c37d9f72ee..4b4ec2697989831e734f963eb8c872b144b1ebee 100644 (file)
@@ -20,6 +20,8 @@ import { getProperty } from "~/store/properties/properties";
 import { snackbarActions } from '../snackbar/snackbar-actions';
 import { DataExplorer, getDataExplorer } from '../data-explorer/data-explorer-reducer';
 import { ListResults } from '~/services/common-service/common-resource-service';
+import { loadContainers } from '../processes/processes-actions';
+import { ResourceKind } from '~/models/resource';
 
 export class ProjectPanelMiddlewareService extends DataExplorerMiddlewareService {
     constructor(private services: ServiceRepository, id: string) {
@@ -39,6 +41,7 @@ export class ProjectPanelMiddlewareService extends DataExplorerMiddlewareService
                 const response = await this.services.groupsService.contents(projectUuid, getParams(dataExplorer));
                 api.dispatch<any>(updateFavorites(response.items.map(item => item.uuid)));
                 api.dispatch(updateResources(response.items));
+                await api.dispatch<any>(loadMissingProcessesInformation(response.items));
                 api.dispatch(setItems(response));
             } catch (e) {
                 api.dispatch(couldNotFetchProjectContents());
@@ -47,6 +50,22 @@ export class ProjectPanelMiddlewareService extends DataExplorerMiddlewareService
     }
 }
 
+export const loadMissingProcessesInformation = (resources: GroupContentsResource[]) =>
+    async (dispatch: Dispatch) => {
+        const containerUuids = resources.reduce((uuids, resource) => {
+            return resource.kind === ResourceKind.CONTAINER_REQUEST
+                ? resource.containerUuid
+                    ? [...uuids, resource.containerUuid]
+                    : uuids
+                : uuids;
+        }, []);
+        if (containerUuids.length > 0) {
+            await dispatch<any>(loadContainers(
+                new FilterBuilder().addIn('uuid', containerUuids).getFilters()
+            ));
+        }
+    };
+
 const setItems = (listResults: ListResults<GroupContentsResource>) =>
     projectPanelActions.SET_ITEMS({
         ...listResultsToDataExplorerItemsMeta(listResults),
@@ -65,7 +84,6 @@ const getFilters = (dataExplorer: DataExplorer) => {
     const statusFilters = getDataExplorerColumnFilters(columns, ProjectPanelColumnNames.STATUS);
     return new FilterBuilder()
         .addIsA("uuid", typeFilters.map(f => f.type))
-        .addIn("state", statusFilters.map(f => f.type), GroupContentsResourcePrefix.PROCESS)
         .addILike("name", dataExplorer.searchValue, GroupContentsResourcePrefix.COLLECTION)
         .addILike("name", dataExplorer.searchValue, GroupContentsResourcePrefix.PROCESS)
         .addILike("name", dataExplorer.searchValue, GroupContentsResourcePrefix.PROJECT)
index c96813df95bb25f8b4f773804ed6eda5df7255b2..72644f93af2e9cea7c5a93e965cff53793f8f026 100644 (file)
@@ -3,7 +3,7 @@
 // SPDX-License-Identifier: AGPL-3.0
 
 import * as React from 'react';
-import { Grid, Typography } from '@material-ui/core';
+import { Grid, Typography, withStyles } from '@material-ui/core';
 import { FavoriteStar } from '../favorite-star/favorite-star';
 import { ResourceKind, TrashableResource } from '~/models/resource';
 import { ProjectIcon, CollectionIcon, ProcessIcon, DefaultIcon } from '~/components/icon/icon';
@@ -13,8 +13,9 @@ import { connect } from 'react-redux';
 import { RootState } from '~/store/store';
 import { getResource } from '~/store/resources/resources';
 import { GroupContentsResource } from '~/services/groups-service/groups-service';
-import { ProcessResource } from '~/models/process';
-
+import { getProcess, Process, getProcessStatus, getProcessStatusColor } from '~/store/processes/process';
+import { ArvadosTheme } from '~/common/custom-theme';
+import { compose } from 'redux';
 
 export const renderName = (item: { name: string; uuid: string, kind: string }) =>
     <Grid container alignItems="center" wrap="nowrap" spacing={16}>
@@ -107,13 +108,17 @@ export const ResourceType = connect(
         return { type: resource ? resource.kind : '' };
     })((props: { type: string }) => renderType(props.type));
 
-export const renderStatus = (item: { status?: string }) =>
-    <Typography noWrap align="center" >
-        {item.status || "-"}
-    </Typography>;
-
-export const ProcessStatus = connect(
-    (state: RootState, props: { uuid: string }) => {
-        const resource = getResource<ProcessResource>(props.uuid)(state.resources);
-        return { status: resource ? resource.state : '-' };
-    })((props: { status: string }) => renderType(props.status));
+export const ProcessStatus = compose(
+    connect((state: RootState, props: { uuid: string }) => {
+        return { process: getProcess(props.uuid)(state.resources) };
+    }),
+    withStyles({}, { withTheme: true }))
+    ((props: { process?: Process, theme: ArvadosTheme }) => {
+        const status = props.process ? getProcessStatus(props.process) : "-";
+        return <Typography
+            noWrap
+            align="center"
+            style={{ color: getProcessStatusColor(status, props.theme) }} >
+            {status}
+        </Typography>;
+    });
index 2cb30198e7242c54fca8020bdf5579dc57535a66..803d8002a365105c2691648e2a5f000cbd3be254 100644 (file)
@@ -68,24 +68,7 @@ export const favoritePanelColumns: DataColumns<string, FavoritePanelFilter> = [
         name: "Status",
         selected: true,
         configurable: true,
-        sortDirection: SortDirection.NONE,
-        filters: [
-            {
-                name: ContainerRequestState.COMMITTED,
-                selected: true,
-                type: ContainerRequestState.COMMITTED
-            },
-            {
-                name: ContainerRequestState.FINAL,
-                selected: true,
-                type: ContainerRequestState.FINAL
-            },
-            {
-                name: ContainerRequestState.UNCOMMITTED,
-                selected: true,
-                type: ContainerRequestState.UNCOMMITTED
-            }
-        ],
+        filters: [],
         render: uuid => <ProcessStatus uuid={uuid} />,
         width: "75px"
     },
@@ -93,7 +76,6 @@ export const favoritePanelColumns: DataColumns<string, FavoritePanelFilter> = [
         name: FavoritePanelColumnNames.TYPE,
         selected: true,
         configurable: true,
-        sortDirection: SortDirection.NONE,
         filters: [
             {
                 name: resourceLabel(ResourceKind.COLLECTION),
@@ -118,7 +100,6 @@ export const favoritePanelColumns: DataColumns<string, FavoritePanelFilter> = [
         name: FavoritePanelColumnNames.OWNER,
         selected: true,
         configurable: true,
-        sortDirection: SortDirection.NONE,
         filters: [],
         render: uuid => <ResourceOwner uuid={uuid} />,
         width: "200px"
@@ -127,7 +108,6 @@ export const favoritePanelColumns: DataColumns<string, FavoritePanelFilter> = [
         name: FavoritePanelColumnNames.FILE_SIZE,
         selected: true,
         configurable: true,
-        sortDirection: SortDirection.NONE,
         filters: [],
         render: uuid => <ResourceFileSize uuid={uuid} />,
         width: "50px"
index 1cb72a963b0ff709bff33203ad21859eda374c01..b75b37aab5e7fa99ac8c4bb2d738abb55d764b29 100644 (file)
@@ -72,24 +72,7 @@ export const projectPanelColumns: DataColumns<string, ProjectPanelFilter> = [
         name: "Status",
         selected: true,
         configurable: true,
-        sortDirection: SortDirection.NONE,
-        filters: [
-            {
-                name: ContainerRequestState.COMMITTED,
-                selected: true,
-                type: ContainerRequestState.COMMITTED
-            },
-            {
-                name: ContainerRequestState.FINAL,
-                selected: true,
-                type: ContainerRequestState.FINAL
-            },
-            {
-                name: ContainerRequestState.UNCOMMITTED,
-                selected: true,
-                type: ContainerRequestState.UNCOMMITTED
-            }
-        ],
+        filters: [],
         render: uuid => <ProcessStatus uuid={uuid} />,
         width: "75px"
     },
@@ -97,7 +80,6 @@ export const projectPanelColumns: DataColumns<string, ProjectPanelFilter> = [
         name: ProjectPanelColumnNames.TYPE,
         selected: true,
         configurable: true,
-        sortDirection: SortDirection.NONE,
         filters: [
             {
                 name: resourceLabel(ResourceKind.COLLECTION),
@@ -122,7 +104,6 @@ export const projectPanelColumns: DataColumns<string, ProjectPanelFilter> = [
         name: ProjectPanelColumnNames.OWNER,
         selected: true,
         configurable: true,
-        sortDirection: SortDirection.NONE,
         filters: [],
         render: uuid => <ResourceOwner uuid={uuid} />,
         width: "200px"
@@ -131,7 +112,6 @@ export const projectPanelColumns: DataColumns<string, ProjectPanelFilter> = [
         name: ProjectPanelColumnNames.FILE_SIZE,
         selected: true,
         configurable: true,
-        sortDirection: SortDirection.NONE,
         filters: [],
         render: uuid => <ResourceFileSize uuid={uuid} />,
         width: "50px"