Skip to content

Commit

Permalink
chore: update based on PR review
Browse files Browse the repository at this point in the history
  • Loading branch information
Caleb Kang committed Jul 17, 2020
1 parent 91188dd commit b0ccc27
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 27 deletions.
82 changes: 55 additions & 27 deletions webui/react/src/pages/ExperimentList.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ExclamationCircleOutlined } from '@ant-design/icons';
import { Button, Input, Modal, Table } from 'antd';
import { Button, Input, Modal, Spin, Table } from 'antd';
import { SelectValue } from 'antd/lib/select';
import React, { useCallback, useEffect, useMemo, useState } from 'react';

Expand Down Expand Up @@ -38,7 +38,8 @@ enum Action {
Cancel = 'Cancel',
Kill = 'Kill',
Pause = 'Pause',
OpenTensorBoard = 'Open Tensorboard',
OpenTensorBoard = 'OpenTensorboard',
Unarchive = 'Unarchive',
}

const defaultFilters: ExperimentFilters = {
Expand Down Expand Up @@ -78,22 +79,35 @@ const ExperimentList: React.FC = () => {
return selectedRowKeys.map(key => experimentMap[key]);
}, [ experimentMap, selectedRowKeys ]);

const { hasActivatable, hasArchivable, hasCancelable, hasKillable, hasPausible } = useMemo(() => {
const {
hasActivatable,
hasArchivable,
hasCancelable,
hasKillable,
hasPausable,
hasUnarchivable,
} = useMemo(() => {
const tracker = {
hasActivatable: false,
hasArchivable: false,
hasCancelable: false,
hasKillable: false,
hasPausible: false,
hasPausable: false,
hasUnarchivable: false,
};
for (let i = 0; i < selectedExperiments.length; i++) {
const experiment = selectedExperiments[i];
const isTerminal = terminalRunStates.includes(experiment.state);
if (!experiment.archived && isTerminal) tracker.hasArchivable = true;
if (cancellableRunStates.includes(experiment.state)) tracker.hasCancelable = true;
if (isTaskKillable(experiment)) tracker.hasKillable = true;
if (experiment.state === RunState.Paused) tracker.hasActivatable = true;
if (experiment.state === RunState.Active) tracker.hasPausible = true;
const isArchivable = !experiment.archived && terminalRunStates.includes(experiment.state);
const isCancelable = cancellableRunStates.includes(experiment.state);
const isKillable = isTaskKillable(experiment);
const isActivatable = experiment.state === RunState.Paused;
const isPausable = experiment.state === RunState.Active;
if (!tracker.hasArchivable && isArchivable) tracker.hasArchivable = true;
if (!tracker.hasUnarchivable && experiment.archived) tracker.hasUnarchivable = true;
if (!tracker.hasCancelable && isCancelable) tracker.hasCancelable = true;
if (!tracker.hasKillable && isKillable) tracker.hasKillable = true;
if (!tracker.hasActivatable && isActivatable) tracker.hasActivatable = true;
if (!tracker.hasPausable && isPausable) tracker.hasPausable = true;
}
return tracker;
}, [ selectedExperiments ]);
Expand Down Expand Up @@ -132,7 +146,7 @@ const ExperimentList: React.FC = () => {
handleFilterChange({ ...filters, username });
}, [ filters, handleFilterChange ]);

const getBatchActions = useCallback((action: Action): Promise<void[] | Command> => {
const sendBatchActions = useCallback((action: Action): Promise<void[] | Command> => {
if (action === Action.OpenTensorBoard) {
return launchTensorboard({
ids: selectedExperiments.map(experiment => experiment.id),
Expand All @@ -141,29 +155,38 @@ const ExperimentList: React.FC = () => {
}
return Promise.all(selectedExperiments
.map(experiment => {
if (action === Action.Activate) {
return setExperimentState({ experimentId: experiment.id, state: RunState.Active });
} else if (action === Action.Archive) {
return archiveExperiment(experiment.id, true);
} else if (action === Action.Cancel) {
return setExperimentState({ experimentId: experiment.id, state: RunState.Canceled });
} else if (action === Action.Kill) {
return killExperiment({ experimentId: experiment.id });
} else if (action === Action.Pause) {
return setExperimentState({ experimentId: experiment.id, state: RunState.Paused });
switch (action) {
case Action.Activate:
return setExperimentState({ experimentId: experiment.id, state: RunState.Active });
case Action.Archive:
return archiveExperiment(experiment.id, true);
case Action.Cancel:
return setExperimentState({ experimentId: experiment.id, state: RunState.Canceled });
case Action.Kill:
return killExperiment({ experimentId: experiment.id });
case Action.Pause:
return setExperimentState({ experimentId: experiment.id, state: RunState.Paused });
case Action.Unarchive:
return archiveExperiment(experiment.id, false);
default:
return Promise.resolve();
}
return Promise.resolve();
}));
}, [ selectedExperiments ]);

const handleBatchAction = useCallback(async (action: Action) => {
try {
const result = await getBatchActions(action);
const result = await sendBatchActions(action);
if (action === Action.OpenTensorBoard) {
const url = waitPageUrl(result as Command);
if (url) openBlank(setupUrlForDev(url));
}

// Refetch experiment list to get updates based on batch action.
await fetchExperiments();

// Clear out previously selected rows upon succession.
setSelectedRowKeys([]);
} catch (e) {
const publicSubject = action === Action.OpenTensorBoard ?
'Unable to Open TensorBoard for Selected Experiments' :
Expand All @@ -178,7 +201,7 @@ const ExperimentList: React.FC = () => {
type: ErrorType.Server,
});
}
}, [ getBatchActions, fetchExperiments ]);
}, [ fetchExperiments, sendBatchActions ]);

const handleConfirmation = useCallback((action: Action) => {
Modal.confirm({
Expand Down Expand Up @@ -222,18 +245,23 @@ const ExperimentList: React.FC = () => {
</div>
</div>
<TableBatch message="Apply batch operations to multiple experiments." show={showBatch}>
<Button onClick={(): Promise<void> => handleBatchAction(Action.OpenTensorBoard)}>
<Button
type="primary"
onClick={(): Promise<void> => handleBatchAction(Action.OpenTensorBoard)}>
Open TensorBoard
</Button>
<Button
disabled={!hasActivatable && false}
disabled={!hasActivatable}
onClick={(): void => handleConfirmation(Action.Activate)}>Activate</Button>
<Button
disabled={!hasPausible}
disabled={!hasPausable}
onClick={(): void => handleConfirmation(Action.Pause)}>Pause</Button>
<Button
disabled={!hasArchivable}
onClick={(): void => handleConfirmation(Action.Archive)}>Archive</Button>
<Button
disabled={!hasUnarchivable}
onClick={(): void => handleConfirmation(Action.Unarchive)}>Unarchive</Button>
<Button
disabled={!hasCancelable}
onClick={(): void => handleConfirmation(Action.Cancel)}>Cancel</Button>
Expand Down
8 changes: 8 additions & 0 deletions webui/react/src/styles/antd.scss
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@ html {

.ant-btn[disabled],
.ant-btn[disabled]:hover {
background-color: var(--theme-colors-monochrome-15);
border-color: var(--theme-colors-monochrome-12);
color: var(--theme-colors-monochrome-9);
}
.ant-btn-primary[disabled],
.ant-btn-primary[disabled]:hover,
.ant-btn-primary.ant-btn-dangerous[disabled],
.ant-btn-primary.ant-btn-dangerous[disabled]:hover {
background-color: var(--theme-colors-monochrome-11);
border-color: var(--theme-colors-monochrome-11);
color: var(--theme-colors-monochrome-17);
Expand Down

0 comments on commit b0ccc27

Please sign in to comment.