"git@developer.sourcefind.cn:gaoqiong/composable_kernel.git" did not exist on "bc26a2faea287cec6ceca03d6b8d4bbcc2e9a635"
Unverified Commit 8c2f717d authored by Lijiaoa's avatar Lijiaoa Committed by GitHub
Browse files

Refactor kill job (#4772)

parent f24c8380
...@@ -6,7 +6,6 @@ import NavCon from '@components/nav/Nav'; ...@@ -6,7 +6,6 @@ import NavCon from '@components/nav/Nav';
import MessageInfo from '@components/common/MessageInfo'; import MessageInfo from '@components/common/MessageInfo';
import { COLUMN } from '@static/const'; import { COLUMN } from '@static/const';
import { isManagerExperimentPage } from '@static/function'; import { isManagerExperimentPage } from '@static/function';
import '@style/App.scss'; import '@style/App.scss';
import '@style/common/common.scss'; import '@style/common/common.scss';
import '@style/experiment/trialdetail/trialsDetail.scss'; import '@style/experiment/trialdetail/trialsDetail.scss';
...@@ -16,20 +15,6 @@ echarts.registerTheme('nni_theme', { ...@@ -16,20 +15,6 @@ echarts.registerTheme('nni_theme', {
color: '#3c8dbc' color: '#3c8dbc'
}); });
interface AppState {
interval: number;
columnList: string[];
experimentUpdateBroadcast: number;
trialsUpdateBroadcast: number;
maxDurationUnit: string;
metricGraphMode: 'max' | 'min'; // tuner's optimize_mode filed
isillegalFinal: boolean;
expWarningMessage: string;
bestTrialEntries: string; // for overview page: best trial entreis
isUpdate: boolean;
expandRowIDs: Set<string>;
}
export const AppContext = React.createContext({ export const AppContext = React.createContext({
interval: 10, // sendons interval: 10, // sendons
columnList: COLUMN, columnList: COLUMN,
...@@ -52,12 +37,32 @@ export const AppContext = React.createContext({ ...@@ -52,12 +37,32 @@ export const AppContext = React.createContext({
// eslint-disable-next-line @typescript-eslint/no-empty-function // eslint-disable-next-line @typescript-eslint/no-empty-function
updateDetailPage: () => {}, updateDetailPage: () => {},
// eslint-disable-next-line @typescript-eslint/no-empty-function // eslint-disable-next-line @typescript-eslint/no-empty-function
changeExpandRowIDs: (_val: string, _type?: string): void => {} changeExpandRowIDs: (_val: string, _type?: string): void => {},
// eslint-disable-next-line @typescript-eslint/no-empty-function
startTimer: () => {},
// eslint-disable-next-line @typescript-eslint/no-empty-function
closeTimer: (): void => {},
// eslint-disable-next-line @typescript-eslint/no-empty-function
refreshDetailTable: (): void => {}
}); });
interface AppState {
interval: number;
columnList: string[];
experimentUpdateBroadcast: number;
trialsUpdateBroadcast: number;
maxDurationUnit: string;
metricGraphMode: 'max' | 'min'; // tuner's optimize_mode filed
isillegalFinal: boolean;
expWarningMessage: string;
bestTrialEntries: string; // for overview page: best trial entreis
expandRowIDs: Set<string>;
timerIdList: number[];
}
class App extends React.Component<{}, AppState> { class App extends React.Component<{}, AppState> {
private timerId!: number | undefined; private timerId = 0;
private firstLoad: boolean = false; // when click refresh selector options
constructor(props: {}) { constructor(props: {}) {
super(props); super(props);
this.state = { this.state = {
...@@ -70,8 +75,8 @@ class App extends React.Component<{}, AppState> { ...@@ -70,8 +75,8 @@ class App extends React.Component<{}, AppState> {
isillegalFinal: false, isillegalFinal: false,
expWarningMessage: '', expWarningMessage: '',
bestTrialEntries: '10', bestTrialEntries: '10',
isUpdate: true, expandRowIDs: new Set(),
expandRowIDs: new Set() timerIdList: []
}; };
} }
...@@ -82,73 +87,8 @@ class App extends React.Component<{}, AppState> { ...@@ -82,73 +87,8 @@ class App extends React.Component<{}, AppState> {
trialsUpdateBroadcast: state.trialsUpdateBroadcast + 1, trialsUpdateBroadcast: state.trialsUpdateBroadcast + 1,
metricGraphMode: EXPERIMENT.optimizeMode === 'minimize' ? 'min' : 'max' metricGraphMode: EXPERIMENT.optimizeMode === 'minimize' ? 'min' : 'max'
})); }));
this.timerId = window.setTimeout(this.refresh, this.state.interval * 100);
}
changeInterval = (interval: number): void => {
window.clearTimeout(this.timerId);
if (interval === 0) {
return;
}
// setState will trigger page refresh at once.
// setState is asyc, interval not update to (this.state.interval) at once.
this.setState({ interval }, () => {
this.firstLoad = true;
this.refresh();
});
};
// TODO: use local storage
changeColumn = (columnList: string[]): void => {
this.setState({ columnList: columnList });
};
changeExpandRowIDs = (id: string, type?: string): void => {
const currentExpandRowIDs = this.state.expandRowIDs;
if (!currentExpandRowIDs.has(id)) {
currentExpandRowIDs.add(id);
} else {
if (!(type !== undefined && type === 'chart')) {
currentExpandRowIDs.delete(id);
}
}
this.setState({ expandRowIDs: currentExpandRowIDs });
};
changeMetricGraphMode = (val: 'max' | 'min'): void => {
this.setState({ metricGraphMode: val });
};
// overview best trial module
changeEntries = (entries: string): void => {
this.setState({ bestTrialEntries: entries });
};
// overview max duration unit
changeMaxDurationUnit = (unit: string): void => {
this.setState({ maxDurationUnit: unit });
};
updateOverviewPage = (): void => {
this.setState(state => ({
experimentUpdateBroadcast: state.experimentUpdateBroadcast + 1
}));
};
updateDetailPage = (): void => {
this.setState(state => ({
trialsUpdateBroadcast: state.trialsUpdateBroadcast + 1
}));
};
shouldComponentUpdate(nextProps: any, nextState: AppState): boolean { this.startTimer();
if (!(nextState.isUpdate || nextState.isUpdate === undefined)) {
nextState.isUpdate = true;
return false;
}
return true;
} }
render(): React.ReactNode { render(): React.ReactNode {
...@@ -165,7 +105,7 @@ class App extends React.Component<{}, AppState> { ...@@ -165,7 +105,7 @@ class App extends React.Component<{}, AppState> {
expandRowIDs expandRowIDs
} = this.state; } = this.state;
if (experimentUpdateBroadcast === 0 || trialsUpdateBroadcast === 0) { if (experimentUpdateBroadcast === 0 || trialsUpdateBroadcast === 0) {
return null; // TODO: render a loading page return null;
} }
const errorList = [ const errorList = [
{ errorWhere: TRIALS.jobListError(), errorMessage: TRIALS.getJobErrorMessage() }, { errorWhere: TRIALS.jobListError(), errorMessage: TRIALS.getJobErrorMessage() },
...@@ -187,7 +127,7 @@ class App extends React.Component<{}, AppState> { ...@@ -187,7 +127,7 @@ class App extends React.Component<{}, AppState> {
</div> </div>
<Stack className='contentBox'> <Stack className='contentBox'>
<Stack className='content'> <Stack className='content'>
{/* search space & config */} {/* search space & config & dispatcher, nnimanagerlog*/}
<SlideNavBtns /> <SlideNavBtns />
{/* if api has error field, show error message */} {/* if api has error field, show error message */}
{errorList.map( {errorList.map(
...@@ -203,6 +143,7 @@ class App extends React.Component<{}, AppState> { ...@@ -203,6 +143,7 @@ class App extends React.Component<{}, AppState> {
<MessageInfo info={expWarningMessage} typeInfo='warning' /> <MessageInfo info={expWarningMessage} typeInfo='warning' />
</div> </div>
)} )}
{/* <AppContext.Provider */}
<AppContext.Provider <AppContext.Provider
value={{ value={{
interval, interval,
...@@ -212,14 +153,17 @@ class App extends React.Component<{}, AppState> { ...@@ -212,14 +153,17 @@ class App extends React.Component<{}, AppState> {
trialsUpdateBroadcast, trialsUpdateBroadcast,
metricGraphMode, metricGraphMode,
maxDurationUnit, maxDurationUnit,
bestTrialEntries,
changeMaxDurationUnit: this.changeMaxDurationUnit, changeMaxDurationUnit: this.changeMaxDurationUnit,
changeMetricGraphMode: this.changeMetricGraphMode, changeMetricGraphMode: this.changeMetricGraphMode,
bestTrialEntries,
changeEntries: this.changeEntries, changeEntries: this.changeEntries,
updateOverviewPage: this.updateOverviewPage,
updateDetailPage: this.updateDetailPage,
expandRowIDs, expandRowIDs,
changeExpandRowIDs: this.changeExpandRowIDs changeExpandRowIDs: this.changeExpandRowIDs,
updateOverviewPage: this.updateOverviewPage,
updateDetailPage: this.updateDetailPage, // update current record without fetch api
refreshDetailTable: this.refreshDetailTable, // update record with fetch api
startTimer: this.startTimer,
closeTimer: this.closeTimer
}} }}
> >
{this.props.children} {this.props.children}
...@@ -233,38 +177,108 @@ class App extends React.Component<{}, AppState> { ...@@ -233,38 +177,108 @@ class App extends React.Component<{}, AppState> {
} }
private refresh = async (): Promise<void> => { private refresh = async (): Promise<void> => {
// resolve this question: 10s -> 20s, page refresh twice. const [experimentUpdated, trialsUpdated] = await Promise.all([EXPERIMENT.update(), TRIALS.update()]);
// only refresh this page after clicking the refresh options if (experimentUpdated) {
if (this.firstLoad !== true) { this.setState(state => ({ experimentUpdateBroadcast: state.experimentUpdateBroadcast + 1 }));
const [experimentUpdated, trialsUpdated] = await Promise.all([EXPERIMENT.update(), TRIALS.update()]); }
if (experimentUpdated) { if (trialsUpdated) {
this.setState(state => ({ experimentUpdateBroadcast: state.experimentUpdateBroadcast + 1 })); this.setState(state => ({ trialsUpdateBroadcast: state.trialsUpdateBroadcast + 1 }));
}
if (trialsUpdated) {
this.setState(state => ({ trialsUpdateBroadcast: state.trialsUpdateBroadcast + 1 }));
}
} else {
this.firstLoad = false;
} }
// experiment status and /trial-jobs api's status could decide website update // experiment status and /trial-jobs api's status could decide website update
if (['DONE', 'ERROR', 'STOPPED', 'VIEWED'].includes(EXPERIMENT.status) || TRIALS.jobListError()) { if (['DONE', 'ERROR', 'STOPPED', 'VIEWED'].includes(EXPERIMENT.status) || TRIALS.jobListError()) {
// experiment finished, refresh once more to ensure consistency // experiment finished, refresh once more to ensure consistency
this.setState(() => ({ interval: 0, isUpdate: false })); this.setState(() => ({ interval: 0 }));
this.closeTimer();
return; return;
} }
this.timerId = window.setTimeout(this.refresh, this.state.interval * 1000); this.startTimer();
}; };
public async lastRefresh(): Promise<void> { public lastRefresh = async (): Promise<void> => {
await EXPERIMENT.update(); await EXPERIMENT.update();
await TRIALS.update(true); await TRIALS.update(true);
this.setState(state => ({ this.setState(state => ({
experimentUpdateBroadcast: state.experimentUpdateBroadcast + 1, experimentUpdateBroadcast: state.experimentUpdateBroadcast + 1,
trialsUpdateBroadcast: state.trialsUpdateBroadcast + 1 trialsUpdateBroadcast: state.trialsUpdateBroadcast + 1
})); }));
} };
public changeInterval = (interval: number): void => {
this.setState(() => ({ interval: interval })); // reset interval val
this.closeTimer(); // close page auto refresh
if (interval !== 0) {
this.refresh();
}
};
public changeColumn = (columnList: string[]): void => {
this.setState({ columnList: columnList });
};
public changeExpandRowIDs = (id: string, type?: string): void => {
const currentExpandRowIDs = this.state.expandRowIDs;
if (!currentExpandRowIDs.has(id)) {
currentExpandRowIDs.add(id);
} else {
if (!(type !== undefined && type === 'chart')) {
currentExpandRowIDs.delete(id);
}
}
this.setState({ expandRowIDs: currentExpandRowIDs });
};
public changeMetricGraphMode = (val: 'max' | 'min'): void => {
this.setState({ metricGraphMode: val });
};
// overview best trial module
public changeEntries = (entries: string): void => {
this.setState({ bestTrialEntries: entries });
};
// overview max duration unit
public changeMaxDurationUnit = (unit: string): void => {
this.setState({ maxDurationUnit: unit });
};
public updateOverviewPage = (): void => {
this.setState(state => ({
experimentUpdateBroadcast: state.experimentUpdateBroadcast + 1
}));
};
public updateDetailPage = async (): Promise<void> => {
this.setState(state => ({
trialsUpdateBroadcast: state.trialsUpdateBroadcast + 1
}));
};
// fetch api to update table record data
public refreshDetailTable = async (): Promise<void> => {
await TRIALS.update(true);
this.setState(state => ({
trialsUpdateBroadcast: state.trialsUpdateBroadcast + 1
}));
};
// start to refresh page automatically
public startTimer = (): void => {
this.timerId = window.setTimeout(this.refresh, this.state.interval * 1000);
const storeTimerList = this.state.timerIdList;
storeTimerList.push(this.timerId);
this.setState(() => ({ timerIdList: storeTimerList }));
};
public closeTimer = (): void => {
const { timerIdList } = this.state;
timerIdList.forEach(item => {
window.clearTimeout(item);
});
};
} }
export default App; export default App;
...@@ -18,11 +18,11 @@ class LogPathChild extends React.Component<LogpathChildProps, {}> { ...@@ -18,11 +18,11 @@ class LogPathChild extends React.Component<LogpathChildProps, {}> {
<div className='logpath'> <div className='logpath'>
<span className='logName'>{logName}</span> <span className='logName'>{logName}</span>
{isLink ? ( {isLink ? (
<a className='logContent logHref' rel='noopener noreferrer' href={eachLogpath} target='_blank'> <a className='fontColor333 logHref' rel='noopener noreferrer' href={eachLogpath} target='_blank'>
{eachLogpath} {eachLogpath}
</a> </a>
) : ( ) : (
<span className='logContent'>{eachLogpath}</span> <span className='fontColor333'>{eachLogpath}</span>
)} )}
</div> </div>
); );
......
...@@ -11,7 +11,6 @@ import TrialLog from './TrialLog'; ...@@ -11,7 +11,6 @@ import TrialLog from './TrialLog';
import MessageInfo from './MessageInfo'; import MessageInfo from './MessageInfo';
import PanelMonacoEditor from './PanelMonacoEditor'; import PanelMonacoEditor from './PanelMonacoEditor';
import '@style/experiment/overview/overview.scss'; import '@style/experiment/overview/overview.scss';
import '@style/copyParameter.scss';
import '@style/openRow.scss'; import '@style/openRow.scss';
/** /**
......
...@@ -8,7 +8,7 @@ export const Title = (): any => ( ...@@ -8,7 +8,7 @@ export const Title = (): any => (
{(value): React.ReactNode => ( {(value): React.ReactNode => (
<Stack horizontal className='panelTitle'> <Stack horizontal className='panelTitle'>
<Icon iconName={value.icon} /> <Icon iconName={value.icon} />
<span>{value.text}</span> <span className='fontColor333'>{value.text}</span>
</Stack> </Stack>
)} )}
</TitleContext.Consumer> </TitleContext.Consumer>
......
...@@ -14,12 +14,12 @@ export const BasicInfo = (): any => { ...@@ -14,12 +14,12 @@ export const BasicInfo = (): any => {
const descriptionId: string = useId('callout-description'); const descriptionId: string = useId('callout-description');
const ref = React.createRef<HTMLDivElement>(); const ref = React.createRef<HTMLDivElement>();
const [isCalloutVisible, setCalloutVisible] = useState(false); const [isCalloutVisible, setCalloutVisible] = useState(false);
const [isShowLogDrawer, setShowLogDrawer] = useState(false); const [isShowLogPanel, setShowLogPanel] = useState(false);
const onDismiss = useCallback(() => setCalloutVisible(false), []); const onDismiss = useCallback(() => setCalloutVisible(false), []);
const showCallout = useCallback(() => setCalloutVisible(true), []); const showCallout = useCallback(() => setCalloutVisible(true), []);
const closeLogDrawer = useCallback(() => setShowLogDrawer(false), []); const closeLogPanel = useCallback(() => setShowLogPanel(false), []);
const ShowLogDrawer = useCallback(() => setShowLogDrawer(true), []); const ShowLogPanel = useCallback(() => setShowLogPanel(true), []);
return ( return (
<div> <div>
...@@ -53,17 +53,17 @@ export const BasicInfo = (): any => { ...@@ -53,17 +53,17 @@ export const BasicInfo = (): any => {
onDismiss={onDismiss} onDismiss={onDismiss}
setInitialFocus={true} setInitialFocus={true}
> >
<div className={styles.header}> <div className={`${styles.header} font`}>
<p className={`${styles.title} color`} id={labelId}> <p className={`${styles.title} color333`} id={labelId}>
Error Error
</p> </p>
</div> </div>
<div className={styles.inner}> <div className={`${styles.inner} font`}>
<p className={`${styles.subtext} color`} id={descriptionId}> <p className={`${styles.subtext} color333`} id={descriptionId}>
{EXPERIMENT.error} {EXPERIMENT.error}
</p> </p>
<div className={styles.actions}> <div className={styles.actions}>
<Link className={styles.link} onClick={ShowLogDrawer}> <Link className={styles.link} onClick={ShowLogPanel}>
Learn about Learn about
</Link> </Link>
</div> </div>
...@@ -92,7 +92,7 @@ export const BasicInfo = (): any => { ...@@ -92,7 +92,7 @@ export const BasicInfo = (): any => {
</div> </div>
</Stack> </Stack>
{/* learn about click -> default active key is dispatcher. */} {/* learn about click -> default active key is dispatcher. */}
{isShowLogDrawer ? <LogPanel closePanel={closeLogDrawer} activeTab='dispatcher' /> : null} {isShowLogPanel ? <LogPanel closePanel={closeLogPanel} activeTab='dispatcher' /> : null}
</div> </div>
); );
}; };
...@@ -96,7 +96,7 @@ class TrialsDetail extends React.Component<{}, TrialDetailState> { ...@@ -96,7 +96,7 @@ class TrialsDetail extends React.Component<{}, TrialDetailState> {
</div> </div>
{/* trial table list */} {/* trial table list */}
<div className='detailTable' style={{ marginTop: 10 }}> <div className='detailTable' style={{ marginTop: 10 }}>
<TableList tableSource={source} updateDetailPage={this.context.updateDetailPage} /> <TableList tableSource={source} />
</div> </div>
</React.Fragment> </React.Fragment>
)} )}
......
...@@ -172,7 +172,7 @@ class DefaultPoint extends React.Component<DefaultPointProps, DefaultPointState> ...@@ -172,7 +172,7 @@ class DefaultPoint extends React.Component<DefaultPointProps, DefaultPointState>
notMerge={true} // update now notMerge={true} // update now
onEvents={onEvents} onEvents={onEvents}
/> />
<div className='default-metric-noData'>{accNodata}</div> <div className='default-metric-noData fontColor333'>{accNodata}</div>
</div> </div>
</div> </div>
); );
......
...@@ -277,7 +277,7 @@ class Intermediate extends React.Component<IntermediateProps, IntermediateState> ...@@ -277,7 +277,7 @@ class Intermediate extends React.Component<IntermediateProps, IntermediateState>
notMerge={true} // update now notMerge={true} // update now
onEvents={IntermediateEvents} onEvents={IntermediateEvents}
/> />
<div className='xAxis'># Intermediate result</div> <div className='fontColor333'># Intermediate result</div>
</div> </div>
</div> </div>
); );
......
...@@ -10,22 +10,29 @@ import { ...@@ -10,22 +10,29 @@ import {
DirectionalHint, DirectionalHint,
Checkbox Checkbox
} from '@fluentui/react'; } from '@fluentui/react';
import { EXPERIMENT, TRIALS } from '@static/datamodel'; import { Trial } from '@model/trial';
import { TOOLTIP_BACKGROUND_COLOR } from '@static/const'; import { TOOLTIP_BACKGROUND_COLOR } from '@static/const';
import { convertDuration, formatTimestamp, copyAndSort, parametersType, parseMetrics } from '@static/function'; import { EXPERIMENT, TRIALS } from '@static/datamodel';
import {
convertDuration,
formatTimestamp,
copyAndSort,
parametersType,
_inferColumnTitle,
getIntermediateAllKeys
} from '@static/function';
import { TableObj, SortInfo, SearchItems } from '@static/interface'; import { TableObj, SortInfo, SearchItems } from '@static/interface';
import { getTrialsBySearchFilters } from './tableFunction/search/searchFunction';
import { blocked, copy, LineChart, tableListIcon } from '@components/fluent/Icon'; import { blocked, copy, LineChart, tableListIcon } from '@components/fluent/Icon';
import ChangeColumnComponent from '../ChangeColumnComponent'; import Search from './tableFunction/search/Search';
import Compare from './tableFunction/Compare';
import Customize from './tableFunction/CustomizedTrial'; import Customize from './tableFunction/CustomizedTrial';
import TensorboardUI from './tableFunction/tensorboard/TensorboardUI'; import TensorboardUI from './tableFunction/tensorboard/TensorboardUI';
import Search from './tableFunction/search/Search'; import ChangeColumnComponent from '../ChangeColumnComponent';
import KillJob from './tableFunction/killTrial/Killjob'; import Compare from './tableFunction/Compare';
import KillJobIndex from './tableFunction/killJob/KillJobIndex';
import { getTrialsBySearchFilters } from './tableFunction/search/searchFunction';
import ExpandableDetails from '@components/common/ExpandableDetails'; import ExpandableDetails from '@components/common/ExpandableDetails';
import PaginationTable from '@components/common/PaginationTable'; import PaginationTable from '@components/common/PaginationTable';
import CopyButton from '@components/common/CopyButton'; import CopyButton from '@components/common/CopyButton';
import { Trial } from '@model/trial';
require('echarts/lib/chart/line'); require('echarts/lib/chart/line');
require('echarts/lib/component/tooltip'); require('echarts/lib/component/tooltip');
...@@ -35,33 +42,8 @@ type SearchOptionType = 'id' | 'trialnum' | 'status' | 'parameters'; ...@@ -35,33 +42,8 @@ type SearchOptionType = 'id' | 'trialnum' | 'status' | 'parameters';
const defaultDisplayedColumns = ['sequenceId', 'id', 'duration', 'status', 'latestAccuracy']; const defaultDisplayedColumns = ['sequenceId', 'id', 'duration', 'status', 'latestAccuracy'];
function _inferColumnTitle(columnKey: string): string {
if (columnKey === 'sequenceId') {
return 'Trial No.';
} else if (columnKey === 'id') {
return 'ID';
} else if (columnKey === 'intermediateCount') {
return 'Intermediate results (#)';
} else if (columnKey === 'message') {
return 'Message';
} else if (columnKey.startsWith('space/')) {
return columnKey.split('/', 2)[1] + ' (space)';
} else if (columnKey === 'latestAccuracy') {
return 'Default metric'; // to align with the original design
} else if (columnKey.startsWith('metric/')) {
return columnKey.split('/', 2)[1] + ' (metric)';
} else if (columnKey.startsWith('_')) {
return columnKey;
} else {
// camel case to verbose form
const withSpace = columnKey.replace(/[A-Z]/g, letter => ` ${letter.toLowerCase()}`);
return withSpace.charAt(0).toUpperCase() + withSpace.slice(1);
}
}
interface TableListProps { interface TableListProps {
tableSource: TableObj[]; tableSource: TableObj[];
updateDetailPage: () => void;
} }
interface TableListState { interface TableListState {
...@@ -111,6 +93,133 @@ class TableList extends React.Component<TableListProps, TableListState> { ...@@ -111,6 +93,133 @@ class TableList extends React.Component<TableListProps, TableListState> {
this._expandedTrialIds = new Set<string>(); this._expandedTrialIds = new Set<string>();
} }
componentDidUpdate(prevProps: TableListProps): void {
if (this.props.tableSource !== prevProps.tableSource) {
this._updateTableSource();
}
}
componentDidMount(): void {
this._updateTableSource();
}
render(): React.ReactNode {
const {
displayedItems,
columns,
customizeColumnsDialogVisible,
compareDialogVisible,
displayedColumns,
selectedRowIds,
intermediateDialogTrial,
copiedTrialId,
searchItems,
intermediateKeyList
} = this.state;
return (
<div id='tableList'>
<Stack horizontal className='panelTitle' style={{ marginTop: 10 }}>
<span style={{ marginRight: 12 }}>{tableListIcon}</span>
<span className='fontColor333'>Trial jobs</span>
</Stack>
<Stack horizontal className='allList'>
<StackItem>
<Stack horizontal horizontalAlign='end' className='allList'>
<Search
searchFilter={searchItems} // search filter list
changeSearchFilterList={this.changeSearchFilterList}
/>
</Stack>
</StackItem>
<StackItem styles={{ root: { position: 'absolute', right: '0' } }}>
<DefaultButton
className='allList-button-gap'
text='Add/Remove columns'
onClick={(): void => {
this.setState({ customizeColumnsDialogVisible: true });
}}
/>
<DefaultButton
text='Compare'
className='allList-compare'
onClick={(): void => {
this.setState({ compareDialogVisible: true });
}}
disabled={selectedRowIds.length === 0}
/>
<TensorboardUI
selectedRowIds={selectedRowIds}
changeSelectTrialIds={this.changeSelectTrialIds}
/>
</StackItem>
</Stack>
{columns && displayedItems && (
<PaginationTable
columns={columns.filter(
column =>
displayedColumns.includes(column.key) ||
['_expand', '_operation', '_selected'].includes(column.key)
)}
items={displayedItems}
compact={true}
selectionMode={0}
selectionPreservedOnEmptyClick={true}
onRenderRow={(props): any => {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
return <ExpandableDetails detailsProps={props!} isExpand={props!.item._expandDetails} />;
}}
/>
)}
{compareDialogVisible && (
<Compare
title='Compare trials'
showDetails={true}
trials={this.props.tableSource.filter(trial => selectedRowIds.includes(trial.id))}
onHideDialog={(): void => {
this.setState({ compareDialogVisible: false });
}}
changeSelectTrialIds={this.changeSelectTrialIds}
/>
)}
{intermediateDialogTrial !== undefined && (
<Compare
title='Intermediate results'
showDetails={false}
trials={[intermediateDialogTrial]}
intermediateKeyList={intermediateKeyList}
onHideDialog={(): void => {
this.setState({ intermediateDialogTrial: undefined });
}}
/>
)}
{customizeColumnsDialogVisible && (
<ChangeColumnComponent
selectedColumns={displayedColumns}
allColumns={columns
.filter(column => !column.key.startsWith('_'))
.map(column => ({ key: column.key, name: column.name }))}
onSelectedChange={this._updateDisplayedColumns.bind(this)}
onHideDialog={(): void => {
this.setState({ customizeColumnsDialogVisible: false });
}}
whichComponent='table'
/>
)}
{/* Clone a trial and customize a set of new parameters */}
{/* visible is done inside because prompt is needed even when the dialog is closed */}
<Customize
visible={copiedTrialId !== undefined}
copyTrialId={copiedTrialId || ''}
closeCustomizeModal={(): void => {
this.setState({ copiedTrialId: undefined });
}}
/>
</div>
);
}
/* Table basic function related methods */ /* Table basic function related methods */
private _onColumnClick(ev: React.MouseEvent<HTMLElement>, column: IColumn): void { private _onColumnClick(ev: React.MouseEvent<HTMLElement>, column: IColumn): void {
...@@ -288,6 +397,9 @@ class TableList extends React.Component<TableListProps, TableListState> { ...@@ -288,6 +397,9 @@ class TableList extends React.Component<TableListProps, TableListState> {
...(k === 'status' && { ...(k === 'status' && {
// color status // color status
onRender: (record): React.ReactNode => ( onRender: (record): React.ReactNode => (
// kill 成功之后,重新拉取的数据如果有 endtime 字段,会马上render出user_cancel
// 的状态,反之,没有这个字段,table依然是部分刷新,只刷新duration,不会
// 刷新 status
<span className={`${record.status} commonStyle`}>{record.status}</span> <span className={`${record.status} commonStyle`}>{record.status}</span>
) )
}), }),
...@@ -434,7 +546,7 @@ class TableList extends React.Component<TableListProps, TableListState> { ...@@ -434,7 +546,7 @@ class TableList extends React.Component<TableListProps, TableListState> {
onClick={(): void => { onClick={(): void => {
const { tableSource } = this.props; const { tableSource } = this.props;
const trial = tableSource.find(trial => trial.id === record.id) as TableObj; const trial = tableSource.find(trial => trial.id === record.id) as TableObj;
const intermediateKeyListResult = this.getIntermediateAllKeys(trial); const intermediateKeyListResult = getIntermediateAllKeys(trial);
this.setState({ this.setState({
intermediateDialogTrial: trial, intermediateDialogTrial: trial,
intermediateKeyList: intermediateKeyListResult intermediateKeyList: intermediateKeyListResult
...@@ -448,7 +560,7 @@ class TableList extends React.Component<TableListProps, TableListState> { ...@@ -448,7 +560,7 @@ class TableList extends React.Component<TableListProps, TableListState> {
{blocked} {blocked}
</PrimaryButton> </PrimaryButton>
) : ( ) : (
<KillJob trial={record} /> <KillJobIndex trialId={record.id} />
)} )}
<PrimaryButton <PrimaryButton
className='detail-button-operation' className='detail-button-operation'
...@@ -464,166 +576,11 @@ class TableList extends React.Component<TableListProps, TableListState> { ...@@ -464,166 +576,11 @@ class TableList extends React.Component<TableListProps, TableListState> {
); );
} }
private changeSearchFilterList = (arr: Array<SearchItems>): void => { public changeSearchFilterList = (arr: Array<SearchItems>): void => {
this.setState(() => ({ this.setState(() => ({
searchItems: arr searchItems: arr
})); }));
}; };
private getIntermediateAllKeys = (intermediateDialogTrial: any): string[] => {
let intermediateAllKeysList: string[] = [];
if (
intermediateDialogTrial!.intermediateMetrics !== undefined &&
intermediateDialogTrial!.intermediateMetrics[0]
) {
const parsedMetric = parseMetrics(intermediateDialogTrial!.intermediateMetrics[0].data);
if (parsedMetric !== undefined && typeof parsedMetric === 'object') {
const allIntermediateKeys: string[] = [];
// just add type=number keys
for (const key in parsedMetric) {
if (typeof parsedMetric[key] === 'number') {
allIntermediateKeys.push(key);
}
}
intermediateAllKeysList = allIntermediateKeys;
}
}
if (intermediateAllKeysList.includes('default') && intermediateAllKeysList[0] !== 'default') {
intermediateAllKeysList = intermediateAllKeysList.filter(item => item !== 'default');
intermediateAllKeysList.unshift('default');
}
return intermediateAllKeysList;
};
componentDidUpdate(prevProps: TableListProps): void {
if (this.props.tableSource !== prevProps.tableSource) {
this._updateTableSource();
}
}
componentDidMount(): void {
this._updateTableSource();
}
render(): React.ReactNode {
const {
displayedItems,
columns,
customizeColumnsDialogVisible,
compareDialogVisible,
displayedColumns,
selectedRowIds,
intermediateDialogTrial,
copiedTrialId,
searchItems,
intermediateKeyList
} = this.state;
return (
<div id='tableList'>
<Stack horizontal className='panelTitle' style={{ marginTop: 10 }}>
<span style={{ marginRight: 12 }}>{tableListIcon}</span>
<span>Trial jobs</span>
</Stack>
<Stack horizontal className='allList'>
<StackItem>
<Stack horizontal horizontalAlign='end' className='allList'>
<Search
searchFilter={searchItems} // search filter list
changeSearchFilterList={this.changeSearchFilterList}
updatePage={this.props.updateDetailPage}
/>
</Stack>
</StackItem>
<StackItem styles={{ root: { position: 'absolute', right: '0' } }}>
<DefaultButton
className='allList-button-gap'
text='Add/Remove columns'
onClick={(): void => {
this.setState({ customizeColumnsDialogVisible: true });
}}
/>
<DefaultButton
text='Compare'
className='allList-compare'
onClick={(): void => {
this.setState({ compareDialogVisible: true });
}}
disabled={selectedRowIds.length === 0}
/>
<TensorboardUI
selectedRowIds={selectedRowIds}
changeSelectTrialIds={this.changeSelectTrialIds}
/>
</StackItem>
</Stack>
{columns && displayedItems && (
<PaginationTable
columns={columns.filter(
column =>
displayedColumns.includes(column.key) ||
['_expand', '_operation', '_selected'].includes(column.key)
)}
items={displayedItems}
compact={true}
selectionMode={0}
selectionPreservedOnEmptyClick={true}
onRenderRow={(props): any => {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
return <ExpandableDetails detailsProps={props!} isExpand={props!.item._expandDetails} />;
}}
/>
)}
{compareDialogVisible && (
<Compare
title='Compare trials'
showDetails={true}
trials={this.props.tableSource.filter(trial => selectedRowIds.includes(trial.id))}
onHideDialog={(): void => {
this.setState({ compareDialogVisible: false });
}}
changeSelectTrialIds={this.changeSelectTrialIds}
/>
)}
{intermediateDialogTrial !== undefined && (
<Compare
title='Intermediate results'
showDetails={false}
trials={[intermediateDialogTrial]}
intermediateKeyList={intermediateKeyList}
onHideDialog={(): void => {
this.setState({ intermediateDialogTrial: undefined });
}}
/>
)}
{customizeColumnsDialogVisible && (
<ChangeColumnComponent
selectedColumns={displayedColumns}
allColumns={columns
.filter(column => !column.key.startsWith('_'))
.map(column => ({ key: column.key, name: column.name }))}
onSelectedChange={this._updateDisplayedColumns.bind(this)}
onHideDialog={(): void => {
this.setState({ customizeColumnsDialogVisible: false });
}}
whichComponent='table'
/>
)}
{/* Clone a trial and customize a set of new parameters */}
{/* visible is done inside because prompt is needed even when the dialog is closed */}
<Customize
visible={copiedTrialId !== undefined}
copyTrialId={copiedTrialId || ''}
closeCustomizeModal={(): void => {
this.setState({ copiedTrialId: undefined });
}}
/>
</div>
);
}
} }
export default TableList; export default TableList;
...@@ -194,7 +194,7 @@ class Compare extends React.Component<CompareProps, CompareState> { ...@@ -194,7 +194,7 @@ class Compare extends React.Component<CompareProps, CompareState> {
const metricKeys = this._overlapKeys(items.map(item => item.metrics)); const metricKeys = this._overlapKeys(items.map(item => item.metrics));
return ( return (
<table className={`compare-modal-table ${scrollClass}`}> <table className={`compare-modal-table ${scrollClass} fontColor333`}>
<tbody> <tbody>
{this._renderRow('id', 'ID', 'value idList', items, item => item.id)} {this._renderRow('id', 'ID', 'value idList', items, item => item.id)}
{this._renderRow('trialnum', 'Trial No.', 'value', items, item => item.sequenceId.toString())} {this._renderRow('trialnum', 'Trial No.', 'value', items, item => item.sequenceId.toString())}
...@@ -281,7 +281,7 @@ class Compare extends React.Component<CompareProps, CompareState> { ...@@ -281,7 +281,7 @@ class Compare extends React.Component<CompareProps, CompareState> {
) : null} ) : null}
<Stack className='compare-modal-intermediate'> <Stack className='compare-modal-intermediate'>
{this._intermediates(items)} {this._intermediates(items)}
<Stack className='compare-yAxis'># Intermediate result</Stack> <Stack className='compare-yAxis fontColor333'># Intermediate result</Stack>
</Stack> </Stack>
{showDetails && <Stack>{this._columns(items)}</Stack>} {showDetails && <Stack>{this._columns(items)}</Stack>}
</div> </div>
......
...@@ -205,13 +205,6 @@ class Customize extends React.Component<CustomizeProps, CustomizeState> { ...@@ -205,13 +205,6 @@ class Customize extends React.Component<CustomizeProps, CustomizeState> {
</StackItem> </StackItem>
</Stack> </Stack>
))} ))}
{/* disable [tag] because we havn't support */}
{/* <Stack key="tag" horizontal className="hyper-form tag-input">
<StackItem grow={9} className="title">Tag</StackItem>
<StackItem grow={15} className="inputs">
<input type="text" value='Customized' />
</StackItem>
</Stack> */}
</form> </form>
<DialogFooter> <DialogFooter>
<PrimaryButton text='Submit' onClick={this.addNewTrial} /> <PrimaryButton text='Submit' onClick={this.addNewTrial} />
......
import React from 'react';
import PropTypes from 'prop-types';
import { PrimaryButton, Dialog, DialogType, DialogFooter } from '@fluentui/react';
function KillJobDialog(props): any {
const { onHideDialog, trialId, isError } = props;
const dialogContentProps = {
type: DialogType.normal,
title: 'Kill job'
};
return (
<Dialog hidden={false} dialogContentProps={dialogContentProps} modalProps={{ className: 'dialog' }}>
{isError.isError ? (
<div>
<div>Trial {trialId} kill failed!</div>
<span>Error message: {isError.message}</span>
</div>
) : (
<div>
Trial <span className='bold'>{trialId}</span> had been killed successfully.
</div>
)}
<DialogFooter>
<PrimaryButton onClick={onHideDialog} text='Close' />
</DialogFooter>
</Dialog>
);
}
KillJobDialog.propTypes = {
trialId: PropTypes.string,
isError: PropTypes.object,
onHideDialog: PropTypes.func
};
export default KillJobDialog;
import React, { useState, useRef, useContext } from 'react';
import PropTypes from 'prop-types';
import axios from 'axios';
import { Stack, FocusTrapCallout, DefaultButton, FocusZone, PrimaryButton } from '@fluentui/react';
import { MANAGER_IP } from '@static/const';
import KillJobDialog from './KillJobDialog';
import { blocked } from '@components/fluent/Icon';
import { gap10 } from '@components/fluent/ChildrenGap';
import { styles } from '@components/experiment/overview/params/basicInfoStyles';
import { AppContext } from '@/App';
function KillJobIndex(props): any {
const menuButtonElement = useRef(null);
const { startTimer, closeTimer, interval, refreshDetailTable } = useContext(AppContext);
const { trialId } = props;
const [isCalloutVisible, setCalloutVisible] = useState(false);
const [isVisibleKillDialog, setKillDialogVisible] = useState(false);
const [error, setError] = useState({ isError: false, message: '' });
const promptString = 'Are you sure to cancel this trial?';
// kill trial
const killJob = (id: string): void => {
if (interval !== 0) {
closeTimer(); // close auto refresh to confirm show the kill model
}
axios(`${MANAGER_IP}/trial-jobs/${id}`, {
method: 'DELETE',
headers: {
'Content-Type': 'application/json;charset=utf-8'
}
})
.then(res => {
if (res.status === 200) {
setKillDialogVisible(true);
setError({ isError: false, message: '' });
} else {
setKillDialogVisible(true);
setError({ isError: false, message: 'fail to cancel the job' });
}
})
.catch(error => {
if (error.response) {
setKillDialogVisible(true);
setError({ isError: false, message: error.response.data.error || 'Fail to cancel the job' });
} else {
setKillDialogVisible(true);
setError({
isError: false,
message: error.response.data.error || '500 error, fail to cancel the job'
});
}
});
};
const onDismissKillJobMessageDialog = async (): Promise<void> => {
setKillDialogVisible(false);
await refreshDetailTable();
if (interval !== 0) {
startTimer(); // start refresh
}
};
const onDismiss = (): void => {
setCalloutVisible(false);
};
const onKill = (): void => {
setCalloutVisible(false);
killJob(props.trialId);
};
const openPrompt = (event: React.SyntheticEvent<EventTarget>): void => {
event.preventDefault();
event.stopPropagation();
setCalloutVisible(true);
};
return (
<div>
<div className={styles.buttonArea} ref={menuButtonElement}>
<PrimaryButton className='detail-button-operation' onClick={openPrompt} title='kill'>
{blocked}
</PrimaryButton>
</div>
{isCalloutVisible ? (
<div>
<FocusTrapCallout
role='alertdialog'
className={styles.callout}
gapSpace={0}
target={menuButtonElement}
onDismiss={onDismiss}
setInitialFocus={true}
>
<div className={`${styles.header} font`}>
<p className={`${styles.title} color333`}>Kill trial</p>
</div>
<div className={`${styles.inner} font`}>
<div>
<p className={`${styles.subtext} color333`}>{promptString}</p>
</div>
</div>
<FocusZone>
<Stack className={styles.buttons} tokens={gap10} horizontal>
<DefaultButton onClick={onDismiss}>No</DefaultButton>
<PrimaryButton onClick={onKill}>Yes</PrimaryButton>
</Stack>
</FocusZone>
</FocusTrapCallout>
</div>
) : null}
{/* kill job status dialog */}
{isVisibleKillDialog && (
<KillJobDialog trialId={trialId} isError={error} onHideDialog={onDismissKillJobMessageDialog} />
)}
</div>
);
}
KillJobIndex.propTypes = {
trialId: PropTypes.string,
updatePage: PropTypes.func
};
export default KillJobIndex;
import * as React from 'react';
import { Stack, FocusTrapCallout, DefaultButton, FocusZone, PrimaryButton } from '@fluentui/react';
import { killJob } from '@static/function';
import { blocked } from '@components/fluent/Icon';
import { styles } from '@components/experiment/overview/params/basicInfoStyles';
interface KillJobState {
isCalloutVisible: boolean;
}
interface KillJobProps {
trial: any;
}
class KillJob extends React.Component<KillJobProps, KillJobState> {
private menuButtonElement!: HTMLElement | null;
constructor(props: KillJobProps) {
super(props);
this.state = { isCalloutVisible: false };
}
render(): React.ReactNode {
const { isCalloutVisible } = this.state;
const prompString = 'Are you sure to cancel this trial?';
return (
<div>
<div className={styles.buttonArea} ref={(menuButton): any => (this.menuButtonElement = menuButton)}>
<PrimaryButton className='detail-button-operation' onClick={this.openPromot} title='kill'>
{blocked}
</PrimaryButton>
</div>
{isCalloutVisible ? (
<div>
<FocusTrapCallout
role='alertdialog'
className={styles.callout}
gapSpace={0}
target={this.menuButtonElement}
onDismiss={this.onDismiss}
setInitialFocus={true}
>
<div className={styles.header}>
<p className={styles.title} style={{ color: '#333' }}>
Kill trial
</p>
</div>
<div className={styles.inner}>
<div>
<p className={styles.subtext} style={{ color: '#333' }}>
{prompString}
</p>
</div>
</div>
<FocusZone>
<Stack className={styles.buttons} gap={8} horizontal>
<DefaultButton onClick={this.onDismiss}>No</DefaultButton>
<PrimaryButton onClick={this.onKill}>Yes</PrimaryButton>
</Stack>
</FocusZone>
</FocusTrapCallout>
</div>
) : null}
</div>
);
}
private onDismiss = (): void => {
this.setState(() => ({ isCalloutVisible: false }));
};
private onKill = (): void => {
this.setState({ isCalloutVisible: false }, () => {
const { trial } = this.props;
killJob(trial.key, trial.id, trial.status);
});
};
private openPromot = (event: React.SyntheticEvent<EventTarget>): void => {
event.preventDefault();
event.stopPropagation();
this.setState({ isCalloutVisible: true });
};
}
export default KillJob;
import React, { useState } from 'react'; import React, { useState, useContext } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { Stack, PrimaryButton } from '@fluentui/react'; import { Stack, PrimaryButton } from '@fluentui/react';
import { gap10 } from '@components/fluent/ChildrenGap'; import { gap10 } from '@components/fluent/ChildrenGap';
import { AppContext } from '@/App';
import { getSearchInputValueBySearchList } from './searchFunction'; import { getSearchInputValueBySearchList } from './searchFunction';
// This file is for search trial ['Trial id', 'Trial No.'] // This file is for search trial ['Trial id', 'Trial No.']
function GeneralSearch(props): any { function GeneralSearch(props): any {
const { updateDetailPage } = useContext(AppContext);
// searchName val: Trial No. | Trial id // searchName val: Trial No. | Trial id
const { searchName, searchFilter, dismiss, changeSearchFilterList, setSearchInputVal, updatePage } = props; const { searchName, searchFilter, dismiss, changeSearchFilterList, setSearchInputVal } = props;
const [firstInputVal, setFirstInputVal] = useState(getSearchNameInit()); const [firstInputVal, setFirstInputVal] = useState(getSearchNameInit());
function updateFirstInputVal(ev: React.ChangeEvent<HTMLInputElement>): void { function updateFirstInputVal(ev: React.ChangeEvent<HTMLInputElement>): void {
...@@ -56,7 +58,7 @@ function GeneralSearch(props): any { ...@@ -56,7 +58,7 @@ function GeneralSearch(props): any {
} }
setSearchInputVal(getSearchInputValueBySearchList(searchFilterConditions)); setSearchInputVal(getSearchInputValueBySearchList(searchFilterConditions));
changeSearchFilterList(searchFilterConditions); changeSearchFilterList(searchFilterConditions);
updatePage(); updateDetailPage();
dismiss(); // close menu dismiss(); // close menu
} }
...@@ -75,8 +77,7 @@ GeneralSearch.propTypes = { ...@@ -75,8 +77,7 @@ GeneralSearch.propTypes = {
searchFilter: PropTypes.array, searchFilter: PropTypes.array,
dismiss: PropTypes.func, dismiss: PropTypes.func,
setSearchInputVal: PropTypes.func, setSearchInputVal: PropTypes.func,
changeSearchFilterList: PropTypes.func, changeSearchFilterList: PropTypes.func
updatePage: PropTypes.func
}; };
export default GeneralSearch; export default GeneralSearch;
import React, { useState } from 'react'; import React, { useState, useContext } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { import {
Stack, Stack,
...@@ -13,11 +13,13 @@ import { SearchItems } from '@static/interface'; ...@@ -13,11 +13,13 @@ import { SearchItems } from '@static/interface';
import SearchParameterConditions from './SearchParameterConditions'; import SearchParameterConditions from './SearchParameterConditions';
import GeneralSearch from './GeneralSearch'; import GeneralSearch from './GeneralSearch';
import { classNames, isChoiceType } from './searchFunction'; import { classNames, isChoiceType } from './searchFunction';
import { AppContext } from '@/App';
// TableList search layout // TableList search layout
function Search(props): any { function Search(props): any {
const { searchFilter, changeSearchFilterList, updatePage } = props; const { searchFilter, changeSearchFilterList } = props;
const { updateDetailPage } = useContext(AppContext);
const [searchInputVal, setSearchInputVal] = useState(''); const [searchInputVal, setSearchInputVal] = useState('');
function getSearchMenu(parameterList): IContextualMenuProps { function getSearchMenu(parameterList): IContextualMenuProps {
...@@ -80,7 +82,7 @@ function Search(props): any { ...@@ -80,7 +82,7 @@ function Search(props): any {
parameter={item.text} parameter={item.text}
searchFilter={searchFilter} // search filter list searchFilter={searchFilter} // search filter list
changeSearchFilterList={changeSearchFilterList} changeSearchFilterList={changeSearchFilterList}
updatePage={updatePage} updatePage={updateDetailPage}
setSearchInputVal={setSearchInputVal} setSearchInputVal={setSearchInputVal}
dismiss={dismissMenu} // close menu dismiss={dismissMenu} // close menu
/> />
...@@ -94,7 +96,6 @@ function Search(props): any { ...@@ -94,7 +96,6 @@ function Search(props): any {
searchFilter={searchFilter} // search fliter list searchFilter={searchFilter} // search fliter list
changeSearchFilterList={changeSearchFilterList} changeSearchFilterList={changeSearchFilterList}
setSearchInputVal={setSearchInputVal} setSearchInputVal={setSearchInputVal}
updatePage={updatePage}
dismiss={dismissMenu} // after click Apply button to close menu dismiss={dismissMenu} // after click Apply button to close menu
/> />
); );
...@@ -107,7 +108,7 @@ function Search(props): any { ...@@ -107,7 +108,7 @@ function Search(props): any {
// update TableList page // update TableList page
function changeTableListPage(searchFilterList: Array<SearchItems>): void { function changeTableListPage(searchFilterList: Array<SearchItems>): void {
changeSearchFilterList(searchFilterList); changeSearchFilterList(searchFilterList);
updatePage(); updateDetailPage();
} }
// deal with the format 1.[x, (space)xx] 2. (space)[x] // deal with the format 1.[x, (space)xx] 2. (space)[x]
...@@ -266,8 +267,7 @@ function Search(props): any { ...@@ -266,8 +267,7 @@ function Search(props): any {
Search.propTypes = { Search.propTypes = {
searchFilter: PropTypes.array, searchFilter: PropTypes.array,
changeSearchFilterList: PropTypes.func, changeSearchFilterList: PropTypes.func
updatePage: PropTypes.func
}; };
export default Search; export default Search;
import React, { useState } from 'react'; import React, { useState, useContext } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { Stack, PrimaryButton, Dropdown, IDropdownOption } from '@fluentui/react'; import { Stack, PrimaryButton, Dropdown, IDropdownOption } from '@fluentui/react';
import { EXPERIMENT } from '@static/datamodel'; import { EXPERIMENT } from '@static/datamodel';
import { getDropdownOptions, getSearchInputValueBySearchList } from './searchFunction'; import { getDropdownOptions, getSearchInputValueBySearchList } from './searchFunction';
import { gap10 } from '@components/fluent/ChildrenGap'; import { gap10 } from '@components/fluent/ChildrenGap';
import { AppContext } from '@/App';
// This file is for filtering trial parameters and trial status // This file is for filtering trial parameters and trial status
function SearchParameterConditions(props): any { function SearchParameterConditions(props): any {
const { parameter, searchFilter, dismiss, changeSearchFilterList, updatePage, setSearchInputVal } = props; const { parameter, searchFilter, dismiss, changeSearchFilterList, setSearchInputVal } = props;
const { updateDetailPage } = useContext(AppContext);
const isChoiceTypeSearchFilter = parameter === 'StatusNNI' || EXPERIMENT.searchSpace[parameter]._type === 'choice'; const isChoiceTypeSearchFilter = parameter === 'StatusNNI' || EXPERIMENT.searchSpace[parameter]._type === 'choice';
const operatorList = isChoiceTypeSearchFilter ? ['=', ''] : ['between', '>', '<', '=', '']; const operatorList = isChoiceTypeSearchFilter ? ['=', ''] : ['between', '>', '<', '=', ''];
...@@ -125,7 +127,7 @@ function SearchParameterConditions(props): any { ...@@ -125,7 +127,7 @@ function SearchParameterConditions(props): any {
setSearchInputVal(getSearchInputValueBySearchList(newSearchFilters)); setSearchInputVal(getSearchInputValueBySearchList(newSearchFilters));
changeSearchFilterList(newSearchFilters); changeSearchFilterList(newSearchFilters);
updatePage(); updateDetailPage();
dismiss(); // close menu dismiss(); // close menu
} }
......
...@@ -25,12 +25,7 @@ class TrialIdColumn extends React.Component<TrialIdColumnProps, {}> { ...@@ -25,12 +25,7 @@ class TrialIdColumn extends React.Component<TrialIdColumnProps, {}> {
{item.status === 'STOPPED' ? ( {item.status === 'STOPPED' ? (
<div className='idColor'>{item.id}</div> <div className='idColor'>{item.id}</div>
) : ( ) : (
<a <a href={webuiPortal} className='link' target='_blank' rel='noopener noreferrer'>
href={webuiPortal}
className='link toAnotherExp idColor'
target='_blank'
rel='noopener noreferrer'
>
{item.id} {item.id}
</a> </a>
)} )}
......
...@@ -2,7 +2,7 @@ import * as React from 'react'; ...@@ -2,7 +2,7 @@ import * as React from 'react';
import axios from 'axios'; import axios from 'axios';
import { Stack, StackItem, CommandBarButton, IContextualMenuProps } from '@fluentui/react'; import { Stack, StackItem, CommandBarButton, IContextualMenuProps } from '@fluentui/react';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { WEBUIDOC, MANAGER_IP } from '@static/const'; import { MANAGER_IP, WEBUIDOC } from '@static/const';
import ExperimentSummaryPanel from './slideNav/ExperimentSummaryPanel'; import ExperimentSummaryPanel from './slideNav/ExperimentSummaryPanel';
import { OVERVIEWTABS, DETAILTABS, NNILOGO } from './slideNav/NNItabs'; import { OVERVIEWTABS, DETAILTABS, NNILOGO } from './slideNav/NNItabs';
import { EXPERIMENT } from '@static/datamodel'; import { EXPERIMENT } from '@static/datamodel';
...@@ -160,7 +160,7 @@ class NavCon extends React.Component<NavProps, NavState> { ...@@ -160,7 +160,7 @@ class NavCon extends React.Component<NavProps, NavState> {
</StackItem> </StackItem>
{isvisibleExperimentDrawer && ( {isvisibleExperimentDrawer && (
<ExperimentSummaryPanel <ExperimentSummaryPanel
closeExpDrawer={this.closeExpDrawer} closeExpPanel={this.closeExpDrawer}
experimentProfile={EXPERIMENT.profile} experimentProfile={EXPERIMENT.profile}
/> />
)} )}
......
...@@ -7,26 +7,26 @@ import { EXPERIMENT, TRIALS } from '@static/datamodel'; ...@@ -7,26 +7,26 @@ import { EXPERIMENT, TRIALS } from '@static/datamodel';
import { caclMonacoEditorHeight } from '@static/function'; import { caclMonacoEditorHeight } from '@static/function';
import '@style/logPanel.scss'; import '@style/logPanel.scss';
interface ExpDrawerProps { interface ExpPanelProps {
closeExpDrawer: () => void; closeExpPanel: () => void;
experimentProfile: object; experimentProfile: object;
} }
interface ExpDrawerState { interface ExpPanelState {
experiment: string; experiment: string;
expDrawerHeight: number; expPanelHeight: number;
} }
class ExperimentSummaryPanel extends React.Component<ExpDrawerProps, ExpDrawerState> { class ExperimentSummaryPanel extends React.Component<ExpPanelProps, ExpPanelState> {
public _isExperimentMount!: boolean; public _isExperimentMount!: boolean;
private refreshId!: number | undefined; private refreshId!: number | undefined;
constructor(props: ExpDrawerProps) { constructor(props: ExpPanelProps) {
super(props); super(props);
this.state = { this.state = {
experiment: '', experiment: '',
expDrawerHeight: window.innerHeight expPanelHeight: window.innerHeight
}; };
} }
...@@ -67,7 +67,7 @@ class ExperimentSummaryPanel extends React.Component<ExpDrawerProps, ExpDrawerSt ...@@ -67,7 +67,7 @@ class ExperimentSummaryPanel extends React.Component<ExpDrawerProps, ExpDrawerSt
}; };
onWindowResize = (): void => { onWindowResize = (): void => {
this.setState(() => ({ expDrawerHeight: window.innerHeight })); this.setState(() => ({ expPanelHeight: window.innerHeight }));
}; };
componentDidMount(): void { componentDidMount(): void {
...@@ -84,12 +84,12 @@ class ExperimentSummaryPanel extends React.Component<ExpDrawerProps, ExpDrawerSt ...@@ -84,12 +84,12 @@ class ExperimentSummaryPanel extends React.Component<ExpDrawerProps, ExpDrawerSt
} }
render(): React.ReactNode { render(): React.ReactNode {
const { closeExpDrawer } = this.props; const { closeExpPanel } = this.props;
const { experiment, expDrawerHeight } = this.state; const { experiment, expPanelHeight } = this.state;
const monacoEditorHeight = caclMonacoEditorHeight(expDrawerHeight); const monacoEditorHeight = caclMonacoEditorHeight(expPanelHeight);
return ( return (
<Panel isOpen={true} hasCloseButton={false} isLightDismiss={true} onLightDismissClick={closeExpDrawer}> <Panel isOpen={true} hasCloseButton={false} isLightDismiss={true} onLightDismissClick={closeExpPanel}>
<div className='panel'> <div className='panel'>
<div className='panelName'>Summary</div> <div className='panelName'>Summary</div>
<MonacoEditor <MonacoEditor
...@@ -104,7 +104,7 @@ class ExperimentSummaryPanel extends React.Component<ExpDrawerProps, ExpDrawerSt ...@@ -104,7 +104,7 @@ class ExperimentSummaryPanel extends React.Component<ExpDrawerProps, ExpDrawerSt
<PrimaryButton text='Download' onClick={this.downExperimentParameters} /> <PrimaryButton text='Download' onClick={this.downExperimentParameters} />
</StackItem> </StackItem>
<StackItem grow={50} className='close'> <StackItem grow={50} className='close'>
<DefaultButton text='Close' onClick={closeExpDrawer} /> <DefaultButton text='Close' onClick={closeExpPanel} />
</StackItem> </StackItem>
</Stack> </Stack>
</div> </div>
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment