"docs/en_US/Tutorial/FAQ.rst" did not exist on "abc221589c65d75b494407c60a81ca87c3020463"
Unverified Commit f1105409 authored by liuzhe-lz's avatar liuzhe-lz Committed by GitHub
Browse files

Merge pull request #2959 from microsoft/v1.9

Merge v1.9 back to master
parents 0a6c234a 88a225f8
import React from 'react';
import { Stack, TooltipHost, ProgressIndicator } from '@fluentui/react';
import { EXPERIMENT } from '../../../static/datamodel';
import { CONTROLTYPE } from '../../../static/const';
import { convertDuration } from '../../../static/function';
import { EditExperimentParam } from './EditExperimentParam';
import { ExpDurationContext } from './ExpDurationContext';
import { EditExpeParamContext } from './context';
import '../../../static/style/overview/count.scss';
const itemStyle1: React.CSSProperties = {
width: '62%',
height: 80
};
const itemStyle2: React.CSSProperties = {
width: '63%',
height: 80,
textAlign: 'right'
};
export const ExpDuration = (): any => (
<ExpDurationContext.Consumer>
{(value): React.ReactNode => {
const { maxExecDuration, execDuration, updateOverviewPage } = value;
const tooltip = maxExecDuration - execDuration;
const maxExecDurationStr = convertDuration(maxExecDuration);
const percent = execDuration / maxExecDuration;
return (
<Stack horizontal className='ExpDuration'>
<div style={itemStyle1}>
<TooltipHost content={`${convertDuration(tooltip)} remaining`}>
<ProgressIndicator percentComplete={percent} barHeight={15} />
</TooltipHost>
</div>
<div style={itemStyle2}>
<Stack horizontal></Stack>
<EditExpeParamContext.Provider
value={{
editType: CONTROLTYPE[0],
field: 'maxExecDuration',
title: 'Max duration',
maxExecDuration: maxExecDurationStr,
maxTrialNum: EXPERIMENT.profile.params.maxTrialNum,
trialConcurrency: EXPERIMENT.profile.params.trialConcurrency,
updateOverviewPage
}}
>
<EditExperimentParam />
</EditExpeParamContext.Provider>
</div>
</Stack>
);
}}
</ExpDurationContext.Consumer>
);
import React from 'react';
export const ExpDurationContext = React.createContext({
maxExecDuration: 0,
execDuration: 0,
// eslint-disable-next-line @typescript-eslint/no-empty-function
updateOverviewPage: (): void => {}
});
import * as React from 'react';
import { Stack, TooltipHost, ProgressIndicator } from '@fluentui/react';
import { EXPERIMENT, TRIALS } from '../../../static/datamodel';
import { CONTROLTYPE } from '../../../static/const';
import { EditExperimentParam } from './EditExperimentParam';
import { EditExpeParamContext } from './context';
import { ExpDurationContext } from './ExpDurationContext';
const itemStyles: React.CSSProperties = {
width: '62%'
};
const itemStyle2: React.CSSProperties = {
width: '63%',
textAlign: 'right'
};
const itemStyle1: React.CSSProperties = {
width: '30%',
height: 50
};
const itemRunning: React.CSSProperties = {
width: '42%',
height: 56
};
export const TrialCount = (): any => {
const count = TRIALS.countStatus();
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const stoppedCount = count.get('USER_CANCELED')! + count.get('SYS_CANCELED')! + count.get('EARLY_STOPPED')!;
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const bar2 = count.get('RUNNING')! + count.get('SUCCEEDED')! + count.get('FAILED')! + stoppedCount;
// support type [0, 1], not 98%
const bar2Percent = bar2 / EXPERIMENT.profile.params.maxTrialNum;
return (
<ExpDurationContext.Consumer>
{(value): React.ReactNode => {
const { updateOverviewPage } = value;
return (
<React.Fragment>
<Stack horizontal horizontalAlign='space-between' className='ExpDuration'>
<div style={itemStyles}>
<TooltipHost content={bar2.toString()}>
<ProgressIndicator percentComplete={bar2Percent} barHeight={15} />
</TooltipHost>
<Stack horizontal className='mess'>
<div style={itemRunning} className='basic'>
<p>Running</p>
<div>{count.get('RUNNING')}</div>
</div>
<div style={itemStyle1} className='basic'>
<p>Failed</p>
<div>{count.get('FAILED')}</div>
</div>
<div style={itemStyle1} className='basic'>
<p>Stopped</p>
<div>{stoppedCount}</div>
</div>
</Stack>
<Stack horizontal horizontalAlign='space-between' className='mess'>
<div style={itemStyle1} className='basic'>
<p>Succeeded</p>
<div>{count.get('SUCCEEDED')}</div>
</div>
<div style={itemStyle1} className='basic'>
<p>Waiting</p>
<div>{count.get('WAITING')}</div>
</div>
</Stack>
</div>
<div style={itemStyle2}>
<EditExpeParamContext.Provider
value={{
title: 'Max trial numbers',
field: 'maxTrialNum',
editType: CONTROLTYPE[1],
maxExecDuration: '',
maxTrialNum: EXPERIMENT.profile.params.maxTrialNum,
trialConcurrency: EXPERIMENT.profile.params.trialConcurrency,
updateOverviewPage
}}
>
<EditExperimentParam />
</EditExpeParamContext.Provider>
<EditExpeParamContext.Provider
value={{
title: 'Concurrency',
field: 'trialConcurrency',
editType: CONTROLTYPE[2],
// maxExecDuration: EXPERIMENT.profile.params.maxExecDuration,
maxExecDuration: '',
maxTrialNum: EXPERIMENT.profile.params.maxTrialNum,
trialConcurrency: EXPERIMENT.profile.params.trialConcurrency,
updateOverviewPage
}}
>
<EditExperimentParam />
</EditExpeParamContext.Provider>
</div>
</Stack>
</React.Fragment>
);
}}
</ExpDurationContext.Consumer>
);
};
import React from 'react';
/***
* const CONTROLTYPE = ['MAX_EXEC_DURATION', 'MAX_TRIAL_NUM', 'TRIAL_CONCURRENCY', 'SEARCH_SPACE'];
* [0], 'MAX_EXEC_DURATION', params.maxExecDuration
* [1], 'MAX_TRIAL_NUM', params.maxTrialNum
* [2], 'TRIAL_CONCURRENCY', params.trialConcurrency
*/
export const EditExpeParamContext = React.createContext({
editType: '',
field: '',
title: '',
maxExecDuration: '',
maxTrialNum: 0,
trialConcurrency: 0,
// eslint-disable-next-line @typescript-eslint/no-empty-function
updateOverviewPage: (): void => {}
});
import React, { useState, useCallback } from 'react';
import { Stack, Callout, Link, IconButton } from '@fluentui/react';
import LogDrawer from '../../modals/LogPanel';
import { EXPERIMENT } from '../../../static/datamodel';
import { formatTimestamp } from '../../../static/function';
import { useId } from '@uifabric/react-hooks';
import { BestMetricContext } from '../../Overview';
import { styles } from './basicInfoStyles';
import '../../../static/style/progress/progress.scss';
import '../../../static/style/progress/probar.scss';
export const ReBasicInfo = (): any => {
const labelId: string = useId('callout-label');
const descriptionId: string = useId('callout-description');
const ref = React.createRef<HTMLDivElement>();
const [isCalloutVisible, setCalloutVisible] = useState(false);
const [isShowLogDrawer, setShowLogDrawer] = useState(false);
const onDismiss = useCallback(() => setCalloutVisible(false), []);
const showCallout = useCallback(() => setCalloutVisible(true), []);
const closeLogDrawer = useCallback(() => setShowLogDrawer(false), []);
const ShowLogDrawer = useCallback(() => setShowLogDrawer(true), []);
return (
<div>
<div className='basic'>
<p>ID: {EXPERIMENT.profile.id}</p>
<div>{EXPERIMENT.profile.params.experimentName}</div>
</div>
<div className='basic'>
<Stack className='basic'>
<p>Status</p>
<Stack horizontal className='status'>
<span className={`${EXPERIMENT.status} status-text`}>{EXPERIMENT.status}</span>
{EXPERIMENT.status === 'ERROR' ? (
<div>
<div className={styles.buttonArea} ref={ref}>
<IconButton
iconProps={{ iconName: 'info' }}
onClick={isCalloutVisible ? onDismiss : showCallout}
/>
</div>
{isCalloutVisible && (
<Callout
className={styles.callout}
ariaLabelledBy={labelId}
ariaDescribedBy={descriptionId}
role='alertdialog'
gapSpace={0}
target={ref}
onDismiss={onDismiss}
setInitialFocus={true}
>
<div className={styles.header}>
<p className={styles.title} id={labelId}>
Error
</p>
</div>
<div className={styles.inner}>
<p className={styles.subtext} id={descriptionId}>
{EXPERIMENT.error}
</p>
<div className={styles.actions}>
<Link className={styles.link} onClick={ShowLogDrawer}>
Learn about
</Link>
</div>
</div>
</Callout>
)}
</div>
) : null}
</Stack>
</Stack>
</div>
<div className='basic'>
<BestMetricContext.Consumer>
{(value): React.ReactNode => (
<Stack>
<p>Best metric</p>
<div>{isNaN(value.bestAccuracy) ? 'N/A' : value.bestAccuracy.toFixed(6)}</div>
</Stack>
)}
</BestMetricContext.Consumer>
</div>
<div className='basic'>
<p>Start time</p>
<div className='nowrap'>{formatTimestamp(EXPERIMENT.profile.startTime)}</div>
</div>
<div className='basic'>
<p>End time</p>
<div className='nowrap'>{formatTimestamp(EXPERIMENT.profile.endTime)}</div>
</div>
{/* learn about click -> default active key is dispatcher. */}
{isShowLogDrawer ? <LogDrawer closeDrawer={closeLogDrawer} activeTab='dispatcher' /> : null}
</div>
);
};
import React from 'react';
import { TooltipHost, Stack } from '@fluentui/react';
import { EXPERIMENT } from '../../../static/datamodel';
import '../../../static/style/overview/command.scss';
export const Command = (): any => {
const clusterMetaData = EXPERIMENT.profile.params.clusterMetaData;
const tuner = EXPERIMENT.profile.params.tuner;
const advisor = EXPERIMENT.profile.params.advisor;
const assessor = EXPERIMENT.profile.params.assessor;
let title = '';
let builtinName = '';
let trialCommand = 'unknown';
if (tuner !== undefined) {
title = title.concat('Tuner');
if (tuner.builtinTunerName !== undefined) {
builtinName = builtinName.concat(tuner.builtinTunerName);
}
}
if (advisor !== undefined) {
title = title.concat('/ Assessor');
if (advisor.builtinAdvisorName !== undefined) {
builtinName = builtinName.concat(advisor.builtinAdvisorName);
}
}
if (assessor !== undefined) {
title = title.concat('/ Addvisor');
if (assessor.builtinAssessorName !== undefined) {
builtinName = builtinName.concat(assessor.builtinAssessorName);
}
}
if (clusterMetaData !== undefined) {
for (const item of clusterMetaData) {
if (item.key === 'command') {
trialCommand = item.value;
}
}
}
return (
<div className='command basic'>
<div className='command1'>
<p>Training platform</p>
<div className='nowrap'>{EXPERIMENT.profile.params.trainingServicePlatform}</div>
<p className='lineMargin'>{title}</p>
<div className='nowrap'>{builtinName}</div>
</div>
<Stack className='command2'>
<p>Log directory</p>
<div className='nowrap'>
<TooltipHost content={EXPERIMENT.profile.logDir || 'unknown'} className='nowrap'>
{EXPERIMENT.profile.logDir || 'unknown'}
</TooltipHost>
</div>
<p className='lineMargin'>Trial command</p>
<div className='nowrap'>
<TooltipHost content={trialCommand || 'unknown'} className='nowrap'>
{trialCommand || 'unknown'}
</TooltipHost>
</div>
</Stack>
</div>
);
};
import { FontWeights, mergeStyleSets, getTheme } from '@fluentui/react';
const theme = getTheme();
export const styles = mergeStyleSets({
buttonArea: {
verticalAlign: 'top',
display: 'inline-block',
textAlign: 'center',
// margin: '0 100px',
minWidth: 30,
height: 30
},
callout: {
maxWidth: 300
},
header: {
padding: '18px 24px 12px'
},
title: [
theme.fonts.xLarge,
{
margin: 0,
color: theme.palette.neutralPrimary,
fontWeight: FontWeights.semilight
}
],
inner: {
height: '100%',
padding: '0 24px 20px'
},
actions: {
position: 'relative',
marginTop: 20,
width: '100%',
whiteSpace: 'nowrap'
},
subtext: [
theme.fonts.small,
{
margin: 0,
color: theme.palette.neutralPrimary,
fontWeight: FontWeights.semilight
}
],
link: [
theme.fonts.medium,
{
color: theme.palette.neutralPrimary
}
]
});
const itemStyle1: React.CSSProperties = {
width: '75%'
};
const itemStyleSucceed: React.CSSProperties = {
width: '28%'
};
const itemStyle2: React.CSSProperties = {
height: 38
};
// top trials entries
const entriesOption = [
{ key: '10', text: '10' },
{ key: '20', text: '20' },
{ key: '30', text: '30' },
{ key: '50', text: '40' },
{ key: '100', text: '100' }
];
export { itemStyle1, itemStyleSucceed, itemStyle2, entriesOption };
import * as React from 'react';
import { DetailsRow, IDetailsRowBaseProps } from '@fluentui/react';
import OpenRow from '../public-child/OpenRow';
import OpenRow from '../../public-child/OpenRow';
interface DetailsProps {
detailsProps: IDetailsRowBaseProps;
......
import * as React from 'react';
import { DetailsList, IDetailsListProps, IColumn } from '@fluentui/react';
import DefaultMetric from '../public-child/DefaultMetric';
import DefaultMetric from '../../public-child/DefaultMetric';
import Details from './Details';
import { convertDuration } from '../../static/function';
import { TRIALS } from '../../static/datamodel';
import { DETAILTABS } from '../stateless-component/NNItabs';
import '../../static/style/succTable.scss';
import '../../static/style/openRow.scss';
import { convertDuration } from '../../../static/function';
import { TRIALS } from '../../../static/datamodel';
import { DETAILTABS } from '../../stateless-component/NNItabs';
import '../../../static/style/succTable.scss';
import '../../../static/style/tableStatus.css';
import '../../../static/style/openRow.scss';
interface SuccessTableProps {
trialIds: string[];
......@@ -15,12 +16,17 @@ interface SuccessTableProps {
interface SuccessTableState {
columns: IColumn[];
source: Array<any>;
innerWidth: number;
}
class SuccessTable extends React.Component<SuccessTableProps, SuccessTableState> {
constructor(props: SuccessTableProps) {
super(props);
this.state = { columns: this.columns, source: TRIALS.table(this.props.trialIds) };
this.state = {
columns: this.columns,
source: TRIALS.table(this.props.trialIds),
innerWidth: window.innerWidth
};
}
private onRenderRow: IDetailsListProps['onRenderRow'] = props => {
......@@ -70,8 +76,8 @@ class SuccessTable extends React.Component<SuccessTableProps, SuccessTableState>
name: 'Trial No.',
key: 'sequenceId',
fieldName: 'sequenceId', // required!
minWidth: 60,
maxWidth: 120,
minWidth: (window.innerWidth * 0.333 - 150) / 5,
maxWidth: (window.innerWidth * 0.333 - 150) / 5,
isResizable: true,
data: 'number',
onColumnClick: this.onColumnClick
......@@ -80,8 +86,8 @@ class SuccessTable extends React.Component<SuccessTableProps, SuccessTableState>
name: 'ID',
key: 'id',
fieldName: 'id',
minWidth: 80,
maxWidth: 100,
minWidth: (window.innerWidth * 0.333 - 150) / 5,
maxWidth: (window.innerWidth * 0.333 - 150) / 5,
isResizable: true,
className: 'tableHead leftTitle',
data: 'string',
......@@ -90,8 +96,8 @@ class SuccessTable extends React.Component<SuccessTableProps, SuccessTableState>
{
name: 'Duration',
key: 'duration',
minWidth: 100,
maxWidth: 210,
minWidth: (window.innerWidth * 0.333 - 150) / 5,
maxWidth: (window.innerWidth * 0.333 - 150) / 5,
isResizable: true,
fieldName: 'duration',
data: 'number',
......@@ -105,8 +111,8 @@ class SuccessTable extends React.Component<SuccessTableProps, SuccessTableState>
{
name: 'Status',
key: 'status',
minWidth: 140,
maxWidth: 210,
minWidth: (window.innerWidth * 0.333 - 150) / 5,
maxWidth: (window.innerWidth * 0.333 - 150) / 5,
isResizable: true,
fieldName: 'status',
onRender: (item: any): React.ReactNode => {
......@@ -117,8 +123,8 @@ class SuccessTable extends React.Component<SuccessTableProps, SuccessTableState>
name: 'Default metric',
key: 'accuracy',
fieldName: 'accuracy',
minWidth: 120,
maxWidth: 360,
minWidth: (window.innerWidth * 0.333 - 200) / 5,
// maxWidth: (window.innerWidth * 0.333 - 150) / 5,
isResizable: true,
data: 'number',
onColumnClick: this.onColumnClick,
......@@ -128,6 +134,17 @@ class SuccessTable extends React.Component<SuccessTableProps, SuccessTableState>
}
];
setInnerWidth = (): void => {
this.setState(() => ({ innerWidth: window.innerWidth }));
};
componentDidMount(): void {
window.addEventListener('resize', this.setInnerWidth);
}
componentWillUnmount(): void {
window.removeEventListener('resize', this.setInnerWidth);
}
componentDidUpdate(prevProps: SuccessTableProps): void {
if (this.props.trialIds !== prevProps.trialIds) {
const { trialIds } = this.props;
......@@ -138,7 +155,6 @@ class SuccessTable extends React.Component<SuccessTableProps, SuccessTableState>
render(): React.ReactNode {
const { columns, source } = this.state;
const isNoneData = source.length === 0 ? true : false;
return (
<div id='succTable'>
<DetailsList
......
import * as React from 'react';
import { DetailsRow, IDetailsRowBaseProps } from '@fluentui/react';
import OpenRow from '../public-child/OpenRow';
interface ExpandableDetailsProps {
detailsProps: IDetailsRowBaseProps;
isExpand: boolean;
}
class ExpandableDetails extends React.Component<ExpandableDetailsProps, {}> {
render(): React.ReactNode {
const { detailsProps, isExpand } = this.props;
return (
<div>
<DetailsRow {...detailsProps} />
{isExpand && <OpenRow trialId={detailsProps.item.id} />}
</div>
);
}
}
export default ExpandableDetails;
......@@ -8,7 +8,7 @@ import JSONTree from 'react-json-tree';
import PaiTrialLog from '../public-child/PaiTrialLog';
import TrialLog from '../public-child/TrialLog';
import MessageInfo from '../modals/MessageInfo';
import '../../static/style/overview.scss';
import '../../static/style/overview/overview.scss';
import '../../static/style/copyParameter.scss';
import '../../static/style/openRow.scss';
......
import * as React from 'react';
import { DetailsList, Dropdown, Icon, IDetailsListProps, IDropdownOption, IStackTokens, Stack } from '@fluentui/react';
import ReactPaginate from 'react-paginate';
interface PaginationTableState {
itemsPerPage: number;
currentPage: number;
itemsOnPage: any[]; // this needs to be stored in state to prevent re-rendering
}
const horizontalGapStackTokens: IStackTokens = {
childrenGap: 20,
padding: 10
};
function _currentTableOffset(perPage: number, currentPage: number, source: any[]): number {
return perPage === -1 ? 0 : Math.min(currentPage, Math.floor((source.length - 1) / perPage)) * perPage;
}
function _obtainPaginationSlice(perPage: number, currentPage: number, source: any[]): any[] {
if (perPage === -1) {
return source;
} else {
const offset = _currentTableOffset(perPage, currentPage, source);
return source.slice(offset, offset + perPage);
}
}
class PaginationTable extends React.PureComponent<IDetailsListProps, PaginationTableState> {
constructor(props: IDetailsListProps) {
super(props);
this.state = {
itemsPerPage: 20,
currentPage: 0,
itemsOnPage: []
};
}
private _onItemsPerPageSelect(event: React.FormEvent<HTMLDivElement>, item: IDropdownOption | undefined): void {
if (item !== undefined) {
const { items } = this.props;
// use current offset to calculate the next `current_page`
const currentOffset = _currentTableOffset(this.state.itemsPerPage, this.state.currentPage, items);
const itemsPerPage = item.key as number;
const currentPage = Math.floor(currentOffset / itemsPerPage);
this.setState({
itemsPerPage: itemsPerPage,
currentPage: currentPage,
itemsOnPage: _obtainPaginationSlice(itemsPerPage, currentPage, this.props.items)
});
}
}
private _onPageSelect(event: any): void {
const currentPage = event.selected;
this.setState({
currentPage: currentPage,
itemsOnPage: _obtainPaginationSlice(this.state.itemsPerPage, currentPage, this.props.items)
});
}
componentDidUpdate(prevProps: IDetailsListProps): void {
if (prevProps.items !== this.props.items) {
this.setState({
itemsOnPage: _obtainPaginationSlice(this.state.itemsPerPage, this.state.currentPage, this.props.items)
});
}
}
render(): React.ReactNode {
const { itemsPerPage, itemsOnPage } = this.state;
const detailListProps = {
...this.props,
items: itemsOnPage
};
const itemsCount = this.props.items.length;
const pageCount = itemsPerPage === -1 ? 1 : Math.ceil(itemsCount / itemsPerPage);
const perPageOptions = [
{ key: 10, text: '10 items per page' },
{ key: 20, text: '20 items per page' },
{ key: 50, text: '50 items per page' },
{ key: -1, text: 'All items' }
];
return (
<div>
<DetailsList {...detailListProps} />
<Stack
horizontal
horizontalAlign='end'
verticalAlign='baseline'
styles={{ root: { padding: 10 } }}
tokens={horizontalGapStackTokens}
>
<Dropdown
selectedKey={itemsPerPage}
options={perPageOptions}
onChange={this._onItemsPerPageSelect.bind(this)}
styles={{ dropdown: { width: 150 } }}
/>
<ReactPaginate
previousLabel={<Icon aria-hidden={true} iconName='ChevronLeft' />}
nextLabel={<Icon aria-hidden={true} iconName='ChevronRight' />}
breakLabel={'...'}
breakClassName={'break'}
pageCount={pageCount}
marginPagesDisplayed={2}
pageRangeDisplayed={2}
onPageChange={this._onPageSelect.bind(this)}
containerClassName={itemsCount === 0 ? 'pagination hidden' : 'pagination'}
subContainerClassName={'pages pagination'}
disableInitialCallback={false}
activeClassName={'active'}
/>
</Stack>
</div>
);
}
}
export default PaginationTable;
import React, { useState, useCallback } from 'react';
import { DefaultButton, Stack } from '@fluentui/react';
import TrialConfigPanel from './TrialConfigPanel';
import '../../../static/style/overview/config.scss';
export const TrialConfigButton = (): any => {
const [isShowConfigPanel, setShowConfigPanle] = useState(false);
const [activeTab, setActiveTab] = useState('1');
const hideConfigPanel = useCallback(() => setShowConfigPanle(false), []);
const showTrialConfigpPanel = useCallback(() => {
setShowConfigPanle(true);
setActiveTab('config');
}, []);
const showSearchSpacePanel = useCallback(() => {
setShowConfigPanle(true);
setActiveTab('search space');
}, []);
return (
<React.Fragment>
<Stack className='config'>
<DefaultButton text='Config' onClick={showTrialConfigpPanel} />
<DefaultButton text='Search space' onClick={showSearchSpacePanel} />
</Stack>
{isShowConfigPanel && <TrialConfigPanel hideConfigPanel={hideConfigPanel} activeTab={activeTab} />}
</React.Fragment>
);
};
import * as React from 'react';
import { Stack, Panel, Pivot, PivotItem, PrimaryButton } from '@fluentui/react';
import { EXPERIMENT } from '../../../static/datamodel';
import MonacoEditor from 'react-monaco-editor';
import { MONACO } from '../../../static/const';
import { convertDuration } from '../../../static/function';
import { prettyStringify } from '../../../static/json_util';
import lodash from 'lodash';
import '../../../static/style/logDrawer.scss';
interface LogDrawerProps {
hideConfigPanel: () => void;
activeTab?: string;
}
interface LogDrawerState {
panelInnerHeight: number;
}
class TrialConfigPanel extends React.Component<LogDrawerProps, LogDrawerState> {
constructor(props: LogDrawerProps) {
super(props);
this.state = {
panelInnerHeight: window.innerHeight
};
}
setLogDrawerHeight(): void {
this.setState(() => ({ panelInnerHeight: window.innerHeight }));
}
async componentDidMount(): Promise<void> {
window.addEventListener('resize', this.setLogDrawerHeight);
}
componentWillUnmount(): void {
window.removeEventListener('resize', this.setLogDrawerHeight);
}
render(): React.ReactNode {
const { hideConfigPanel, activeTab } = this.props;
const { panelInnerHeight } = this.state;
// [marginTop 16px] + [Search space 46px] +
// button[height: 32px, marginTop: 45px, marginBottom: 25px] + [padding-bottom: 20px]
const monacoEditorHeight = panelInnerHeight - 184;
const blacklist = [
'id',
'logDir',
'startTime',
'endTime',
'experimentName',
'searchSpace',
'trainingServicePlatform'
];
const filter = (key: string, val: any): any => {
return blacklist.includes(key) ? undefined : val;
};
const profile = lodash.cloneDeep(EXPERIMENT.profile);
profile.execDuration = convertDuration(profile.execDuration);
profile.params.maxExecDuration = convertDuration(profile.params.maxExecDuration);
const showProfile = JSON.stringify(profile, filter, 2);
return (
<Stack>
<Panel
isOpen={true}
hasCloseButton={false}
isFooterAtBottom={true}
isLightDismiss={true}
onLightDismissClick={hideConfigPanel}
>
<div className='log-tab-body'>
<Pivot initialSelectedKey={activeTab} style={{ minHeight: 190, paddingTop: '16px' }}>
<PivotItem headerText='Search space' itemKey='search space'>
<MonacoEditor
height={monacoEditorHeight}
language='json'
theme='vs-light'
value={prettyStringify(EXPERIMENT.searchSpace, 300, 2)}
options={MONACO}
/>
</PivotItem>
<PivotItem headerText='Config' itemKey='config'>
<div className='profile'>
<MonacoEditor
width='100%'
height={monacoEditorHeight}
language='json'
theme='vs-light'
value={showProfile}
options={MONACO}
/>
</div>
</PivotItem>
</Pivot>
</div>
<PrimaryButton text='Close' className='configClose' onClick={hideConfigPanel} />
</Panel>
</Stack>
);
}
}
export default TrialConfigPanel;
......@@ -15,7 +15,7 @@ const DETAILTABS = (
const NNILOGO = (
<NavLink to={'/oview'}>
<img src={require('../../static/img/logo2.png')} alt='NNI logo' style={{ height: 40 }} />
<img src={require('../../static/img/logo.png')} alt='NNI logo' style={{ height: 40 }} />
</NavLink>
);
......
import * as d3 from 'd3';
import { Dropdown, IDropdownOption, Stack } from '@fluentui/react';
import { Dropdown, IDropdownOption, Stack, DefaultButton } from '@fluentui/react';
import ParCoords from 'parcoord-es';
import 'parcoord-es/dist/parcoords.css';
import * as React from 'react';
......@@ -9,12 +9,16 @@ import { filterByStatus } from '../../static/function';
import { TableObj, SingleAxis, MultipleAxes } from '../../static/interface';
import '../../static/style/button.scss';
import '../../static/style/para.scss';
import ChangeColumnComponent from '../modals/ChangeColumnComponent';
interface ParaState {
dimName: string[];
selectedPercent: string;
primaryMetricKey: string;
noChart: boolean;
customizeColumnsDialogVisible: boolean;
availableDimensions: string[];
chosenDimensions: string[];
}
interface ParaProps {
......@@ -45,7 +49,10 @@ class Para extends React.Component<ParaProps, ParaState> {
dimName: [],
primaryMetricKey: 'default',
selectedPercent: '1',
noChart: true
noChart: true,
customizeColumnsDialogVisible: false,
availableDimensions: [],
chosenDimensions: []
};
}
......@@ -82,11 +89,24 @@ class Para extends React.Component<ParaProps, ParaState> {
}
render(): React.ReactNode {
const { selectedPercent, noChart } = this.state;
const {
selectedPercent,
noChart,
customizeColumnsDialogVisible,
availableDimensions,
chosenDimensions
} = this.state;
return (
<div className='parameter'>
<Stack horizontal className='para-filter' horizontalAlign='end'>
<DefaultButton
text='Add/Remove axes'
onClick={(): void => {
this.setState({ customizeColumnsDialogVisible: true });
}}
styles={{ root: { marginRight: 10 } }}
/>
<Dropdown
selectedKey={selectedPercent}
onChange={this.percentNum}
......@@ -101,6 +121,21 @@ class Para extends React.Component<ParaProps, ParaState> {
/>
{this.finalKeysDropdown()}
</Stack>
{customizeColumnsDialogVisible && availableDimensions.length > 0 && (
<ChangeColumnComponent
selectedColumns={chosenDimensions}
allColumns={availableDimensions.map(dim => ({ key: dim, name: dim }))}
onSelectedChange={(selected: string[]): void => {
this.setState({ chosenDimensions: selected }, () => {
this.renderParallelCoordinates();
});
}}
onHideDialog={(): void => {
this.setState({ customizeColumnsDialogVisible: false });
}}
minSelected={2}
/>
)}
<div className='parcoords' style={this.chartMulineStyle} ref={this.paraRef} />
{noChart && <div className='nodata'>No data</div>}
</div>
......@@ -143,13 +178,13 @@ class Para extends React.Component<ParaProps, ParaState> {
private renderParallelCoordinates(): void {
const { searchSpace } = this.props;
const percent = parseFloat(this.state.selectedPercent);
const { primaryMetricKey } = this.state;
const { primaryMetricKey, chosenDimensions } = this.state;
const inferredSearchSpace = TRIALS.inferredSearchSpace(searchSpace);
const inferredMetricSpace = TRIALS.inferredMetricSpace();
let convertedTrials = this.getTrialsAsObjectList(inferredSearchSpace, inferredMetricSpace);
const dimensions: [any, any][] = [];
const dimensions: [string, any][] = [];
let colorDim: string | undefined = undefined,
colorScale: any = undefined;
// treat every axis as numeric to fit for brush
......@@ -213,7 +248,11 @@ class Para extends React.Component<ParaProps, ParaState> {
}
this.pcs
.data(convertedTrials)
.dimensions(dimensions.reduce((obj, entry) => ({ ...obj, [entry[0]]: entry[1] }), {}));
.dimensions(
dimensions
.filter(([d, _]) => chosenDimensions.length === 0 || chosenDimensions.includes(d))
.reduce((obj, entry) => ({ ...obj, [entry[0]]: entry[1] }), {})
);
if (firstRun) {
this.pcs
.margin(this.innerChartMargins)
......@@ -230,6 +269,12 @@ class Para extends React.Component<ParaProps, ParaState> {
if (firstRun) {
this.setState({ noChart: false });
}
// set new available dims
this.setState({
availableDimensions: dimensions.map(e => e[0]),
chosenDimensions: chosenDimensions.length === 0 ? dimensions.map(e => e[0]) : chosenDimensions
});
}
private getTrialsAsObjectList(inferredSearchSpace: MultipleAxes, inferredMetricSpace: MultipleAxes): {}[] {
......
......@@ -5,13 +5,20 @@ import { BrowserRouter as Router, Route, Switch, Redirect } from 'react-router-d
const Overview = lazy(() => import('./components/Overview'));
const TrialsDetail = lazy(() => import('./components/TrialsDetail'));
import './index.css';
import './static/style/loading.scss';
import * as serviceWorker from './serviceWorker';
ReactDOM.render(
<Router>
<App>
<Switch>
<Suspense fallback={null}>
<Suspense
fallback={
<div className='loading'>
<img src={require('./static/img/loading.gif')} />
</div>
}
>
<Route path='/oview' component={Overview} />
<Route path='/detail' component={TrialsDetail} />
<Route path='/' render={(): React.ReactNode => <Redirect to={{ pathname: '/oview' }} />} />
......
......@@ -15,7 +15,7 @@ const trialJobStatus = [
'SYS_CANCELED',
'EARLY_STOPPED'
];
const CONTROLTYPE = ['SEARCH_SPACE', 'TRIAL_CONCURRENCY', 'MAX_EXEC_DURATION'];
const CONTROLTYPE = ['MAX_EXEC_DURATION', 'MAX_TRIAL_NUM', 'TRIAL_CONCURRENCY', 'SEARCH_SPACE'];
const MONACO = {
readOnly: true,
automaticLayout: true,
......
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