Unverified Commit bd0eae1f authored by Lijiaoa's avatar Lijiaoa Committed by GitHub
Browse files

[webui] Update default metric tooltip (#3316)



* use same default metric component

* review self

* rename boolean type variable

* del whichGraph field

* rename my_theme to nni_theme
Co-authored-by: default avatarLijiao <Lijiaoa@outlook.com>
parent e1ceae41
...@@ -6,8 +6,13 @@ import { isManagerExperimentPage } from './static/function'; ...@@ -6,8 +6,13 @@ import { isManagerExperimentPage } from './static/function';
import NavCon from './components/NavCon'; import NavCon from './components/NavCon';
import MessageInfo from './components/modals/MessageInfo'; import MessageInfo from './components/modals/MessageInfo';
import { SlideNavBtns } from './components/slideNav/SlideNavBtns'; import { SlideNavBtns } from './components/slideNav/SlideNavBtns';
const echarts = require('echarts/lib/echarts');
echarts.registerTheme('nni_theme', {
color: '#3c8dbc'
});
import './App.scss'; import './App.scss';
import './static/style/common.scss'; import './static/style/common.scss';
import './static/style/trialsDetail.scss';
interface AppState { interface AppState {
interval: number; interval: number;
......
...@@ -5,7 +5,7 @@ import { Trial } from '../static/model/trial'; ...@@ -5,7 +5,7 @@ import { Trial } from '../static/model/trial';
import { AppContext } from '../App'; import { AppContext } from '../App';
import { Title } from './overview/Title'; import { Title } from './overview/Title';
import SuccessTable from './overview/table/SuccessTable'; import SuccessTable from './overview/table/SuccessTable';
import Accuracy from './overview/Accuracy'; import DefaultPoint from './trial-detail/DefaultMetricPoint';
import { BasicInfo } from './overview/params/BasicInfo'; import { BasicInfo } from './overview/params/BasicInfo';
import { ExpDuration } from './overview/count/ExpDuration'; import { ExpDuration } from './overview/count/ExpDuration';
import { ExpDurationContext } from './overview/count/ExpDurationContext'; import { ExpDurationContext } from './overview/count/ExpDurationContext';
...@@ -60,11 +60,9 @@ class Overview extends React.Component<{}, OverviewState> { ...@@ -60,11 +60,9 @@ class Overview extends React.Component<{}, OverviewState> {
const bestTrials = this.findBestTrials(); const bestTrials = this.findBestTrials();
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const bestAccuracy = bestTrials.length > 0 ? bestTrials[0].accuracy! : NaN; const bestAccuracy = bestTrials.length > 0 ? bestTrials[0].accuracy! : NaN;
const accuracyGraphData = this.generateAccuracyGraph(bestTrials);
const noDataMessage = bestTrials.length > 0 ? '' : 'No data';
const maxExecDuration = EXPERIMENT.profile.params.maxExecDuration; const maxExecDuration = EXPERIMENT.profile.params.maxExecDuration;
const execDuration = EXPERIMENT.profile.execDuration; const execDuration = EXPERIMENT.profile.execDuration;
return ( return (
<AppContext.Consumer> <AppContext.Consumer>
{(value): React.ReactNode => { {(value): React.ReactNode => {
...@@ -167,7 +165,11 @@ class Overview extends React.Component<{}, OverviewState> { ...@@ -167,7 +165,11 @@ class Overview extends React.Component<{}, OverviewState> {
</div> </div>
</Stack> </Stack>
<div className='overviewChart'> <div className='overviewChart'>
<Accuracy accuracyData={accuracyGraphData} accNodata={noDataMessage} /> <DefaultPoint
trialIds={bestTrials.map(trial => trial.info.trialJobId)}
chartHeight={300}
hasBestCurve={false}
/>
<SuccessTable <SuccessTable
trialIds={bestTrials.map(trial => trial.info.trialJobId)} trialIds={bestTrials.map(trial => trial.info.trialJobId)}
updateOverviewPage={updateOverviewPage} updateOverviewPage={updateOverviewPage}
......
...@@ -7,7 +7,6 @@ import Duration from './trial-detail/Duration'; ...@@ -7,7 +7,6 @@ import Duration from './trial-detail/Duration';
import Para from './trial-detail/Para'; import Para from './trial-detail/Para';
import Intermediate from './trial-detail/Intermediate'; import Intermediate from './trial-detail/Intermediate';
import TableList from './trial-detail/TableList'; import TableList from './trial-detail/TableList';
import '../static/style/trialsDetail.scss';
import '../static/style/search.scss'; import '../static/style/search.scss';
interface TrialDetailState { interface TrialDetailState {
...@@ -53,22 +52,18 @@ class TrialsDetail extends React.Component<{}, TrialDetailState> { ...@@ -53,22 +52,18 @@ class TrialsDetail extends React.Component<{}, TrialDetailState> {
{/* <PivotItem tab={this.titleOfacc} key="1"> doesn't work*/} {/* <PivotItem tab={this.titleOfacc} key="1"> doesn't work*/}
<PivotItem headerText='Default metric' itemIcon='HomeGroup' key='Default metric'> <PivotItem headerText='Default metric' itemIcon='HomeGroup' key='Default metric'>
<Stack className='graph'> <Stack className='graph'>
<DefaultPoint trialIds={trialIds} visible={whichChart === 'Default metric'} /> <DefaultPoint trialIds={trialIds} hasBestCurve={true} chartHeight={402} />
</Stack> </Stack>
</PivotItem> </PivotItem>
{/* <PivotItem tab={this.titleOfhyper} key="2"> */} {/* <PivotItem tab={this.titleOfhyper} key="2"> */}
<PivotItem headerText='Hyper-parameter' itemIcon='Equalizer' key='Hyper-parameter'> <PivotItem headerText='Hyper-parameter' itemIcon='Equalizer' key='Hyper-parameter'>
<Stack className='graph'> <Stack className='graph'>
<Para <Para trials={source} searchSpace={EXPERIMENT.searchSpaceNew} />
trials={source}
searchSpace={EXPERIMENT.searchSpaceNew}
whichChart={whichChart}
/>
</Stack> </Stack>
</PivotItem> </PivotItem>
{/* <PivotItem tab={this.titleOfDuration} key="3"> */} {/* <PivotItem tab={this.titleOfDuration} key="3"> */}
<PivotItem headerText='Duration' itemIcon='BarChartHorizontal' key='Duration'> <PivotItem headerText='Duration' itemIcon='BarChartHorizontal' key='Duration'>
<Duration source={source} whichChart={whichChart} /> <Duration source={source} />
</PivotItem> </PivotItem>
{/* <PivotItem tab={this.titleOfIntermediate} key="4"> */} {/* <PivotItem tab={this.titleOfIntermediate} key="4"> */}
<PivotItem <PivotItem
...@@ -77,7 +72,7 @@ class TrialsDetail extends React.Component<{}, TrialDetailState> { ...@@ -77,7 +72,7 @@ class TrialsDetail extends React.Component<{}, TrialDetailState> {
key='Intermediate result' key='Intermediate result'
> >
{/* *why this graph has small footprint? */} {/* *why this graph has small footprint? */}
<Intermediate source={source} whichChart={whichChart} /> <Intermediate source={source} />
</PivotItem> </PivotItem>
</Pivot> </Pivot>
</div> </div>
......
import * as React from 'react';
import ReactEcharts from 'echarts-for-react';
import echarts from 'echarts/lib/echarts';
echarts.registerTheme('my_theme', {
color: '#3c8dbc'
});
import 'echarts/lib/chart/scatter';
import 'echarts/lib/component/tooltip';
import 'echarts/lib/component/title';
interface AccuracyProps {
accuracyData: object;
accNodata: string;
}
class Accuracy extends React.Component<AccuracyProps, {}> {
constructor(props: AccuracyProps) {
super(props);
}
render(): React.ReactNode {
const { accNodata, accuracyData } = this.props;
return (
<div className='defaultMetricContainer'>
<ReactEcharts option={accuracyData} theme='my_theme' />
<div className='showMess'>{accNodata}</div>
</div>
);
}
}
export default Accuracy;
...@@ -2,6 +2,7 @@ import * as React from 'react'; ...@@ -2,6 +2,7 @@ import * as React from 'react';
import { TRIALS } from '../../static/datamodel'; import { TRIALS } from '../../static/datamodel';
import { formatAccuracy } from '../../static/function'; import { formatAccuracy } from '../../static/function';
// oview page table: default metric column render
interface DefaultMetricProps { interface DefaultMetricProps {
trialId: string; trialId: string;
} }
......
...@@ -24,7 +24,8 @@ const EmptyGraph = { ...@@ -24,7 +24,8 @@ const EmptyGraph = {
interface DefaultPointProps { interface DefaultPointProps {
trialIds: string[]; trialIds: string[];
visible: boolean; chartHeight: number;
hasBestCurve: boolean;
} }
interface DefaultPointState { interface DefaultPointState {
...@@ -47,10 +48,6 @@ class DefaultPoint extends React.Component<DefaultPointProps, DefaultPointState> ...@@ -47,10 +48,6 @@ class DefaultPoint extends React.Component<DefaultPointProps, DefaultPointState>
this.setState({ bestCurveEnabled: checked }); this.setState({ bestCurveEnabled: checked });
}; };
shouldComponentUpdate(nextProps: DefaultPointProps): boolean {
return nextProps.visible;
}
metricDataZoom = (e: EventMap): void => { metricDataZoom = (e: EventMap): void => {
if (e.batch !== undefined) { if (e.batch !== undefined) {
this.setState(() => ({ this.setState(() => ({
...@@ -69,6 +66,7 @@ class DefaultPoint extends React.Component<DefaultPointProps, DefaultPointState> ...@@ -69,6 +66,7 @@ class DefaultPoint extends React.Component<DefaultPointProps, DefaultPointState>
tooltip: { tooltip: {
trigger: 'item', trigger: 'item',
enterable: true, enterable: true,
confine: true, // confirm always show tooltip box rather than hidden by background
position: (point: number[], data: TooltipForAccuracy): number[] => [ position: (point: number[], data: TooltipForAccuracy): number[] => [
data.data[0] < maxSequenceId ? point[0] : point[0] - 300, data.data[0] < maxSequenceId ? point[0] : point[0] - 300,
80 80
...@@ -149,24 +147,27 @@ class DefaultPoint extends React.Component<DefaultPointProps, DefaultPointState> ...@@ -149,24 +147,27 @@ class DefaultPoint extends React.Component<DefaultPointProps, DefaultPointState>
} }
render(): React.ReactNode { render(): React.ReactNode {
const { hasBestCurve, chartHeight } = this.props;
const graph = this.generateGraph(); const graph = this.generateGraph();
const accNodata = graph === EmptyGraph ? 'No data' : ''; const accNodata = graph === EmptyGraph ? 'No data' : '';
const onEvents = { dataZoom: this.metricDataZoom }; const onEvents = { dataZoom: this.metricDataZoom };
return ( return (
<div> <div>
{hasBestCurve && (
<Stack horizontalAlign='end' className='default-metric'> <Stack horizontalAlign='end' className='default-metric'>
<Toggle label='Optimization curve' inlineLabel onChange={this.loadDefault} /> <Toggle label='Optimization curve' inlineLabel onChange={this.loadDefault} />
</Stack> </Stack>
)}
<div className='default-metric-graph'> <div className='default-metric-graph'>
<ReactEcharts <ReactEcharts
option={graph} option={graph}
style={{ style={{
width: '100%', width: '100%',
height: 402, height: chartHeight,
margin: '0 auto' margin: '0 auto'
}} }}
theme='my_theme' theme='nni_theme'
notMerge={true} // update now notMerge={true} // update now
onEvents={onEvents} onEvents={onEvents}
/> />
......
...@@ -13,7 +13,6 @@ interface Runtrial { ...@@ -13,7 +13,6 @@ interface Runtrial {
interface DurationProps { interface DurationProps {
source: Array<TableObj>; source: Array<TableObj>;
whichChart: string;
} }
interface DurationState { interface DurationState {
...@@ -177,21 +176,20 @@ class Duration extends React.Component<DurationProps, DurationState> { ...@@ -177,21 +176,20 @@ class Duration extends React.Component<DurationProps, DurationState> {
componentDidUpdate(prevProps: DurationProps): void { componentDidUpdate(prevProps: DurationProps): void {
// add this if to prevent endless loop // add this if to prevent endless loop
if (this.props.source !== prevProps.source) { if (this.props.source !== prevProps.source) {
if (this.props.whichChart === 'Duration') {
this.drawDurationGraph(this.props.source); this.drawDurationGraph(this.props.source);
} }
} }
}
render(): React.ReactNode { render(): React.ReactNode {
const { durationSource } = this.state; const { durationSource } = this.state;
const onEvents = { dataZoom: this.durationDataZoom }; const onEvents = { dataZoom: this.durationDataZoom };
return ( return (
<div> <div>
<ReactEcharts <ReactEcharts
option={durationSource} option={durationSource}
style={{ width: '94%', height: 412, margin: '0 auto', marginTop: 15 }} style={{ width: '94%', height: 412, margin: '0 auto', marginTop: 15 }}
theme='my_theme' theme='nni_theme'
notMerge={true} // update now notMerge={true} // update now
onEvents={onEvents} onEvents={onEvents}
/> />
......
...@@ -24,7 +24,6 @@ interface IntermediateState { ...@@ -24,7 +24,6 @@ interface IntermediateState {
interface IntermediateProps { interface IntermediateProps {
source: Array<TableObj>; source: Array<TableObj>;
whichChart: string;
} }
class Intermediate extends React.Component<IntermediateProps, IntermediateState> { class Intermediate extends React.Component<IntermediateProps, IntermediateState> {
...@@ -230,9 +229,8 @@ class Intermediate extends React.Component<IntermediateProps, IntermediateState> ...@@ -230,9 +229,8 @@ class Intermediate extends React.Component<IntermediateProps, IntermediateState>
componentDidUpdate(prevProps: IntermediateProps, prevState: any): void { componentDidUpdate(prevProps: IntermediateProps, prevState: any): void {
if (this.props.source !== prevProps.source || this.state.isFilter !== prevState.isFilter) { if (this.props.source !== prevProps.source || this.state.isFilter !== prevState.isFilter) {
const { isFilter, filterSource } = this.state; const { isFilter, filterSource } = this.state;
const { whichChart, source } = this.props; const { source } = this.props;
if (whichChart === 'Intermediate result') {
if (isFilter === true) { if (isFilter === true) {
const pointVal = this.pointInput !== null ? this.pointInput.value : ''; const pointVal = this.pointInput !== null ? this.pointInput.value : '';
const minVal = this.minValInput !== null ? this.minValInput.value : ''; const minVal = this.minValInput !== null ? this.minValInput.value : '';
...@@ -246,7 +244,6 @@ class Intermediate extends React.Component<IntermediateProps, IntermediateState> ...@@ -246,7 +244,6 @@ class Intermediate extends React.Component<IntermediateProps, IntermediateState>
} }
} }
} }
}
render(): React.ReactNode { render(): React.ReactNode {
const { interSource, isLoadconfirmBtn, isFilter } = this.state; const { interSource, isLoadconfirmBtn, isFilter } = this.state;
......
...@@ -24,7 +24,6 @@ interface ParaState { ...@@ -24,7 +24,6 @@ interface ParaState {
interface ParaProps { interface ParaProps {
trials: Array<TableObj>; trials: Array<TableObj>;
searchSpace: SearchSpace; searchSpace: SearchSpace;
whichChart: string;
} }
class Para extends React.Component<ParaProps, ParaState> { class Para extends React.Component<ParaProps, ParaState> {
...@@ -81,12 +80,9 @@ class Para extends React.Component<ParaProps, ParaState> { ...@@ -81,12 +80,9 @@ class Para extends React.Component<ParaProps, ParaState> {
componentDidUpdate(prevProps: ParaProps): void { componentDidUpdate(prevProps: ParaProps): void {
// FIXME: redundant update // FIXME: redundant update
if (this.props.trials !== prevProps.trials || this.props.searchSpace !== prevProps.searchSpace) { if (this.props.trials !== prevProps.trials || this.props.searchSpace !== prevProps.searchSpace) {
const { whichChart } = this.props;
if (whichChart === 'Hyper-parameter') {
this.renderParallelCoordinates(); this.renderParallelCoordinates();
} }
} }
}
render(): React.ReactNode { render(): React.ReactNode {
const { const {
......
...@@ -38,13 +38,9 @@ import ExpandableDetails from '../public-child/ExpandableDetails'; ...@@ -38,13 +38,9 @@ import ExpandableDetails from '../public-child/ExpandableDetails';
import PaginationTable from '../public-child/PaginationTable'; import PaginationTable from '../public-child/PaginationTable';
import { Trial } from '../../static/model/trial'; import { Trial } from '../../static/model/trial';
const echarts = require('echarts/lib/echarts');
require('echarts/lib/chart/line'); require('echarts/lib/chart/line');
require('echarts/lib/component/tooltip'); require('echarts/lib/component/tooltip');
require('echarts/lib/component/title'); require('echarts/lib/component/title');
echarts.registerTheme('my_theme', {
color: '#3c8dbc'
});
type SearchOptionType = 'id' | 'trialnum' | 'status' | 'parameters'; type SearchOptionType = 'id' | 'trialnum' | 'status' | 'parameters';
const searchOptionLiterals = { const searchOptionLiterals = {
......
...@@ -121,13 +121,3 @@ $boxGapPadding: 10px; ...@@ -121,13 +121,3 @@ $boxGapPadding: 10px;
.overviewChart { .overviewChart {
margin-top: 20px; margin-top: 20px;
} }
.defaultMetricContainer {
position: relative;
.showMess {
position: absolute;
top: 42%;
left: 48%;
}
}
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