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

[webui] format code by eslint and prettier (#2744)



* update

* format as prettier

* fix error

* add stylelint for scss css file

* add arrow-parens rule

* update

* fix lint

* fix conflict
Co-authored-by: default avatarLijiao <15910218274@163.com>
Co-authored-by: default avatarLijiao <Lijiaoa@outlook.com>
Co-authored-by: default avatarLijiao <1425861283@qq.com>
parent 4668fc08
......@@ -23,16 +23,13 @@ interface DurationState {
}
class Duration extends React.Component<DurationProps, DurationState> {
constructor(props: DurationProps) {
super(props);
this.state = {
startDuration: 0, // for record data zoom
endDuration: 100,
durationSource: this.initDuration(this.props.source),
durationSource: this.initDuration(this.props.source)
};
}
initDuration = (source: Array<TableObj>): any => {
......@@ -50,12 +47,15 @@ class Duration extends React.Component<DurationProps, DurationState> {
axisPointer: {
type: 'shadow'
},
formatter: (data: any): React.ReactNode => (
formatter: (data: any): React.ReactNode =>
'<div>' +
'<div>Trial No.: ' + data[0].dataIndex + '</div>' +
'<div>Duration: ' + convertDuration(data[0].data) + '</div>' +
'<div>Trial No.: ' +
data[0].dataIndex +
'</div>' +
'<div>Duration: ' +
convertDuration(data[0].data) +
'</div>' +
'</div>'
),
},
grid: {
bottom: '3%',
......@@ -71,11 +71,11 @@ class Duration extends React.Component<DurationProps, DurationState> {
filterMode: 'empty',
start: 0,
end: 100
},
}
],
xAxis: {
name: 'Time/s',
type: 'value',
type: 'value'
},
yAxis: {
name: 'Trial No.',
......@@ -85,12 +85,14 @@ class Duration extends React.Component<DurationProps, DurationState> {
padding: [0, 0, 0, 30]
}
},
series: [{
type: 'bar',
data: trialTime
}]
series: [
{
type: 'bar',
data: trialTime
}
]
};
}
};
getOption = (dataObj: Runtrial): any => {
const { startDuration, endDuration } = this.state;
......@@ -100,12 +102,15 @@ class Duration extends React.Component<DurationProps, DurationState> {
axisPointer: {
type: 'shadow'
},
formatter: (data: any): React.ReactNode => (
formatter: (data: any): React.ReactNode =>
'<div>' +
'<div>Trial No.: ' + data[0].dataIndex + '</div>' +
'<div>Duration: ' + convertDuration(data[0].data) + '</div>' +
'<div>Trial No.: ' +
data[0].dataIndex +
'</div>' +
'<div>Duration: ' +
convertDuration(data[0].data) +
'</div>' +
'</div>'
),
},
grid: {
bottom: '3%',
......@@ -121,11 +126,11 @@ class Duration extends React.Component<DurationProps, DurationState> {
filterMode: 'empty',
start: startDuration,
end: endDuration
},
}
],
xAxis: {
name: 'Time',
type: 'value',
type: 'value'
},
yAxis: {
name: 'Trial',
......@@ -135,12 +140,14 @@ class Duration extends React.Component<DurationProps, DurationState> {
padding: [0, 0, 0, 30]
}
},
series: [{
type: 'bar',
data: dataObj.trialTime
}]
series: [
{
type: 'bar',
data: dataObj.trialTime
}
]
};
}
};
drawDurationGraph = (source: Array<TableObj>): void => {
// why this function run two times when props changed?
......@@ -160,7 +167,7 @@ class Duration extends React.Component<DurationProps, DurationState> {
this.setState({
durationSource: this.getOption(trialRun[0])
});
}
};
componentDidMount(): void {
const { source } = this.props;
......@@ -178,13 +185,13 @@ class Duration extends React.Component<DurationProps, DurationState> {
render(): React.ReactNode {
const { durationSource } = this.state;
const onEvents = { 'dataZoom': this.durationDataZoom };
const onEvents = { dataZoom: this.durationDataZoom };
return (
<div>
<ReactEcharts
option={durationSource}
style={{ width: '94%', height: 412, margin: '0 auto', marginTop: 15 }}
theme="my_theme"
theme='my_theme'
notMerge={true} // update now
onEvents={onEvents}
/>
......@@ -195,11 +202,11 @@ class Duration extends React.Component<DurationProps, DurationState> {
private durationDataZoom = (e: EventMap): void => {
if (e.batch !== undefined) {
this.setState(() => ({
startDuration: (e.batch[0].start !== null ? e.batch[0].start : 0),
endDuration: (e.batch[0].end !== null ? e.batch[0].end : 100)
startDuration: e.batch[0].start !== null ? e.batch[0].start : 0,
endDuration: e.batch[0].end !== null ? e.batch[0].end : 100
}));
}
}
};
}
export default Duration;
......@@ -28,7 +28,6 @@ interface IntermediateProps {
}
class Intermediate extends React.Component<IntermediateProps, IntermediateState> {
static intervalMediate = 1;
public pointInput!: HTMLInputElement | null;
public minValInput!: HTMLInputElement | null;
......@@ -68,7 +67,9 @@ class Intermediate extends React.Component<IntermediateProps, IntermediateState>
});
});
// find max intermediate number
trialIntermediate.sort((a, b) => { return (b.data.length - a.data.length); });
trialIntermediate.sort((a, b) => {
return b.data.length - a.data.length;
});
const legend: string[] = [];
// max length
const length = trialIntermediate[0].data.length;
......@@ -84,27 +85,35 @@ class Intermediate extends React.Component<IntermediateProps, IntermediateState>
tooltip: {
trigger: 'item',
enterable: true,
position: function (point: number[], data: TooltipForIntermediate): number[] {
position: function(point: number[], data: TooltipForIntermediate): number[] {
if (data.dataIndex < length / 2) {
return [point[0], 80];
} else {
return [point[0] - 300, 80];
}
},
formatter: function (data: TooltipForIntermediate): React.ReactNode {
formatter: function(data: TooltipForIntermediate): React.ReactNode {
const trialId = data.seriesName;
let obj = {};
const temp = trialIntermediate.find(key => key.name === trialId);
if (temp !== undefined) {
obj = temp.hyperPara;
}
return '<div class="tooldetailAccuracy">' +
'<div>Trial ID: ' + trialId + '</div>' +
'<div>Intermediate: ' + data.data + '</div>' +
return (
'<div class="tooldetailAccuracy">' +
'<div>Trial ID: ' +
trialId +
'</div>' +
'<div>Intermediate: ' +
data.data +
'</div>' +
'<div>Parameters: ' +
'<pre>' + JSON.stringify(obj, null, 4) + '</pre>' +
'<pre>' +
JSON.stringify(obj, null, 4) +
'</pre>' +
'</div>' +
'</div>';
'</div>'
);
}
},
grid: {
......@@ -121,7 +130,7 @@ class Intermediate extends React.Component<IntermediateProps, IntermediateState>
yAxis: {
type: 'value',
name: 'Metric',
scale: true,
scale: true
},
dataZoom: [
{
......@@ -147,7 +156,7 @@ class Intermediate extends React.Component<IntermediateProps, IntermediateState>
},
xAxis: {
type: 'category',
boundaryGap: false,
boundaryGap: false
},
yAxis: {
type: 'value',
......@@ -156,7 +165,7 @@ class Intermediate extends React.Component<IntermediateProps, IntermediateState>
};
this.setState({ interSource: nullData });
}
}
};
// confirm btn function [filter data]
filterLines = (): void => {
......@@ -198,14 +207,14 @@ class Intermediate extends React.Component<IntermediateProps, IntermediateState>
const counts = this.state.clickCounts + 1;
this.setState({ isLoadconfirmBtn: false, clickCounts: counts });
});
}
};
switchTurn = (ev: React.MouseEvent<HTMLElement>, checked?: boolean): void => {
this.setState({ isFilter: checked });
if (checked === false) {
this.drawIntermediate(this.props.source);
}
}
};
componentDidMount(): void {
const { source } = this.props;
......@@ -235,56 +244,47 @@ class Intermediate extends React.Component<IntermediateProps, IntermediateState>
render(): React.ReactNode {
const { interSource, isLoadconfirmBtn, isFilter } = this.state;
const IntermediateEvents = { 'dataZoom': this.intermediateDataZoom };
const IntermediateEvents = { dataZoom: this.intermediateDataZoom };
return (
<div>
{/* style in para.scss */}
<Stack horizontal horizontalAlign="end" tokens={stackTokens} className="meline intermediate">
{
isFilter
?
<div>
<span className="filter-x"># Intermediate result</span>
<input
// placeholder="point"
ref={(input): any => this.pointInput = input}
className="strange"
/>
<span>Metric range</span>
<input
// placeholder="range"
ref={(input): any => this.minValInput = input}
/>
<span className="hyphen">-</span>
<input
// placeholder="range"
ref={(input): any => this.maxValInput = input}
/>
<PrimaryButton
text="Confirm"
onClick={this.filterLines}
disabled={isLoadconfirmBtn}
/>
</div>
:
null
}
<Stack horizontal horizontalAlign='end' tokens={stackTokens} className='meline intermediate'>
{isFilter ? (
<div>
<span className='filter-x'># Intermediate result</span>
<input
// placeholder="point"
ref={(input): any => (this.pointInput = input)}
className='strange'
/>
<span>Metric range</span>
<input
// placeholder="range"
ref={(input): any => (this.minValInput = input)}
/>
<span className='hyphen'>-</span>
<input
// placeholder="range"
ref={(input): any => (this.maxValInput = input)}
/>
<PrimaryButton text='Confirm' onClick={this.filterLines} disabled={isLoadconfirmBtn} />
</div>
) : null}
{/* filter message */}
<Stack horizontal className="filter-toggle">
<Stack horizontal className='filter-toggle'>
<span>Filter</span>
<Toggle onChange={this.switchTurn} />
</Stack>
</Stack>
<div className="intermediate-graph">
<div className='intermediate-graph'>
<ReactEcharts
option={interSource}
style={{ width: '100%', height: 400, margin: '0 auto' }}
notMerge={true} // update now
onEvents={IntermediateEvents}
/>
<div className="xAxis"># Intermediate result</div>
<div className='xAxis'># Intermediate result</div>
</div>
</div>
);
......@@ -293,11 +293,11 @@ class Intermediate extends React.Component<IntermediateProps, IntermediateState>
private intermediateDataZoom = (e: EventMap): void => {
if (e.batch !== undefined) {
this.setState(() => ({
startMediaY: (e.batch[0].start !== null ? e.batch[0].start : 0),
endMediaY: (e.batch[0].end !== null ? e.batch[0].end : 100)
startMediaY: e.batch[0].start !== null ? e.batch[0].start : 0,
endMediaY: e.batch[0].end !== null ? e.batch[0].end : 100
}));
}
}
};
}
export default Intermediate;
......@@ -24,7 +24,6 @@ interface ParaProps {
}
class Para extends React.Component<ParaProps, ParaState> {
private paraRef = React.createRef<HTMLDivElement>();
private pcs: any;
......@@ -57,7 +56,7 @@ class Para extends React.Component<ParaProps, ParaState> {
this.renderParallelCoordinates();
});
}
}
};
// select all final keys
updateEntries = (event: React.FormEvent<HTMLDivElement>, item: any): void => {
......@@ -66,7 +65,7 @@ class Para extends React.Component<ParaProps, ParaState> {
this.renderParallelCoordinates();
});
}
}
};
componentDidMount(): void {
this.renderParallelCoordinates();
......@@ -86,8 +85,8 @@ class Para extends React.Component<ParaProps, ParaState> {
const { selectedPercent, noChart } = this.state;
return (
<div className="parameter">
<Stack horizontal className="para-filter" horizontalAlign="end">
<div className='parameter'>
<Stack horizontal className='para-filter' horizontalAlign='end'>
<Dropdown
selectedKey={selectedPercent}
onChange={this.percentNum}
......@@ -95,15 +94,15 @@ class Para extends React.Component<ParaProps, ParaState> {
{ key: '0.01', text: 'Top 1%' },
{ key: '0.05', text: 'Top 5%' },
{ key: '0.2', text: 'Top 20%' },
{ key: '1', text: 'Top 100%' },
{ key: '1', text: 'Top 100%' }
]}
styles={{ dropdown: { width: 120 } }}
className="para-filter-percent"
className='para-filter-percent'
/>
{this.finalKeysDropdown()}
</Stack>
<div className="parcoords" style={this.chartMulineStyle} ref={this.paraRef}/>
{noChart && <div className="nodata">No data</div>}
<div className='parcoords' style={this.chartMulineStyle} ref={this.paraRef} />
{noChart && <div className='nodata'>No data</div>}
</div>
);
}
......@@ -116,18 +115,19 @@ class Para extends React.Component<ParaProps, ParaState> {
const finalKeysDropdown: any = [];
TRIALS.finalKeys().forEach(item => {
finalKeysDropdown.push({
key: item, text: item
key: item,
text: item
});
});
return (
<div>
<span className="para-filter-text para-filter-middle">Metrics</span>
<span className='para-filter-text para-filter-middle'>Metrics</span>
<Dropdown
selectedKey={primaryMetricKey}
options={finalKeysDropdown}
onChange={this.updateEntries}
styles={{ root: { width: 150, display: 'inline-block' } }}
className="para-filter-percent"
className='para-filter-percent'
/>
</div>
);
......@@ -150,13 +150,17 @@ class Para extends React.Component<ParaProps, ParaState> {
let convertedTrials = this.getTrialsAsObjectList(inferredSearchSpace, inferredMetricSpace);
const dimensions: [any, any][] = [];
let colorDim: string | undefined = undefined, colorScale: any = undefined;
let colorDim: string | undefined = undefined,
colorScale: any = undefined;
// treat every axis as numeric to fit for brush
for (const [k, v] of inferredSearchSpace.axes) {
dimensions.push([k, {
type: 'number',
yscale: this.convertToD3Scale(v)
}]);
dimensions.push([
k,
{
type: 'number',
yscale: this.convertToD3Scale(v)
}
]);
}
for (const [k, v] of inferredMetricSpace.axes) {
const scale = this.convertToD3Scale(v);
......@@ -164,7 +168,7 @@ class Para extends React.Component<ParaProps, ParaState> {
// set color for primary metrics
// `colorScale` is used to produce a color range, while `scale` is to produce a pixel range
colorScale = this.convertToD3Scale(v, false);
convertedTrials.sort((a, b) => EXPERIMENT.optimizeMode === 'minimize' ? a[k] - b[k] : b[k] - a[k]);
convertedTrials.sort((a, b) => (EXPERIMENT.optimizeMode === 'minimize' ? a[k] - b[k] : b[k] - a[k]));
// filter top trials
if (percent != 1) {
const keptTrialNum = Math.max(Math.ceil(convertedTrials.length * percent), 1);
......@@ -179,20 +183,24 @@ class Para extends React.Component<ParaProps, ParaState> {
// reverse the converted trials to show the top ones upfront
convertedTrials.reverse();
const assignColors = (scale: any): void => {
scale.range([0, 1]); // fake a range to perform invert
scale.range([0, 1]); // fake a range to perform invert
const [scaleMin, scaleMax] = scale.domain();
const pivot = scale.invert(0.5);
scale.domain([scaleMin, pivot, scaleMax])
scale
.domain([scaleMin, pivot, scaleMax])
.range(['#90EE90', '#FFC400', '#CA0000'])
.interpolate(d3.interpolateHsl);
};
assignColors(colorScale);
colorDim = k;
}
dimensions.push([k, {
type: 'number',
yscale: scale
}]);
dimensions.push([
k,
{
type: 'number',
yscale: scale
}
]);
}
if (convertedTrials.length === 0 || dimensions.length <= 1) {
......@@ -203,13 +211,15 @@ class Para extends React.Component<ParaProps, ParaState> {
if (firstRun) {
this.pcs = ParCoords()(this.paraRef.current);
}
this.pcs.data(convertedTrials)
this.pcs
.data(convertedTrials)
.dimensions(dimensions.reduce((obj, entry) => ({ ...obj, [entry[0]]: entry[1] }), {}));
if (firstRun) {
this.pcs.margin(this.innerChartMargins)
this.pcs
.margin(this.innerChartMargins)
.alphaOnBrushed(0.2)
.smoothness(0.1)
.brushMode("1D-axes")
.brushMode('1D-axes')
.reorderable()
.interactive();
}
......@@ -228,7 +238,7 @@ class Para extends React.Component<ParaProps, ParaState> {
return succeededTrials.map(s => {
const entries = Array.from(s.parameters(inferredSearchSpace).entries());
entries.push(...(Array.from(s.metrics(inferredMetricSpace).entries())));
entries.push(...Array.from(s.metrics(inferredMetricSpace).entries()));
const ret = {};
for (const [k, v] of entries) {
ret[k.fullName] = v;
......@@ -247,20 +257,26 @@ class Para extends React.Component<ParaProps, ParaState> {
private convertToD3Scale(axis: SingleAxis, initRange: boolean = true): any {
const padLinear = ([x0, x1], k = 0.1): [number, number] => {
const dx = (x1 - x0) * k / 2;
const dx = ((x1 - x0) * k) / 2;
return [x0 - dx, x1 + dx];
};
const padLog = ([x0, x1], k = 0.1): [number, number] => {
const [y0, y1] = padLinear([Math.log(x0), Math.log(x1)], k);
return [Math.exp(y0), Math.exp(y1)];
}
};
let scaleInst: any = undefined;
if (axis.scale === 'ordinal') {
if (axis.nested) {
// TODO: handle nested entries
scaleInst = d3.scalePoint().domain(Array.from(axis.domain.keys())).padding(0.2);
scaleInst = d3
.scalePoint()
.domain(Array.from(axis.domain.keys()))
.padding(0.2);
} else {
scaleInst = d3.scalePoint().domain(axis.domain).padding(0.2);
scaleInst = d3
.scalePoint()
.domain(axis.domain)
.padding(0.2);
}
} else if (axis.scale === 'log') {
scaleInst = d3.scaleLog().domain(padLog(axis.domain));
......
......@@ -2,8 +2,20 @@ import React, { lazy } from 'react';
import axios from 'axios';
import ReactEcharts from 'echarts-for-react';
import {
Stack, Dropdown, DetailsList, IDetailsListProps, DetailsListLayoutMode,
PrimaryButton, Modal, IDropdownOption, IColumn, Selection, SelectionMode, IconButton, TooltipHost, IStackTokens
Stack,
Dropdown,
DetailsList,
IDetailsListProps,
DetailsListLayoutMode,
PrimaryButton,
Modal,
IDropdownOption,
IColumn,
Selection,
SelectionMode,
IconButton,
TooltipHost,
IStackTokens
} from '@fluentui/react';
import ReactPaginate from 'react-paginate';
import { LineChart, blocked, copy } from '../buttons/Icon';
......@@ -81,7 +93,6 @@ interface TableListState {
}
class TableList extends React.Component<TableListProps, TableListState> {
public intervalTrialLog = 10;
public trialId!: string;
......@@ -129,17 +140,20 @@ class TableList extends React.Component<TableListProps, TableListState> {
currColumn.isSorted = true;
} else {
newCol.isSorted = false;
newCol.isSortedDescending = true;
newCol.isSortedDescending = true;
}
});
this.setState({
tableColumns: newColumns,
sortMessage: { field: getColumn.key, isDescend: currColumn.isSortedDescending }
}, () => {
this.updateData();
});
}
this.setState(
{
tableColumns: newColumns,
sortMessage: { field: getColumn.key, isDescend: currColumn.isSortedDescending }
},
() => {
this.updateData();
}
);
};
AccuracyColumnConfig: any = {
name: 'Default metric',
......@@ -151,9 +165,11 @@ class TableList extends React.Component<TableListProps, TableListState> {
isResizable: true,
data: 'number',
onColumnClick: this.onColumnClick,
onRender: (item): React.ReactNode => <TooltipHost content={item.formattedLatestAccuracy}>
<div className="ellipsis">{item.formattedLatestAccuracy}</div>
</TooltipHost>
onRender: (item): React.ReactNode => (
<TooltipHost content={item.formattedLatestAccuracy}>
<div className='ellipsis'>{item.formattedLatestAccuracy}</div>
</TooltipHost>
)
};
SequenceIdColumnConfig: any = {
......@@ -164,7 +180,7 @@ class TableList extends React.Component<TableListProps, TableListState> {
maxWidth: 240,
className: 'tableHead',
data: 'number',
onColumnClick: this.onColumnClick,
onColumnClick: this.onColumnClick
};
IdColumnConfig: any = {
......@@ -188,9 +204,7 @@ class TableList extends React.Component<TableListProps, TableListState> {
isResizable: true,
data: 'number',
onColumnClick: this.onColumnClick,
onRender: (record): React.ReactNode => (
<span>{formatTimestamp(record.startTime)}</span>
)
onRender: (record): React.ReactNode => <span>{formatTimestamp(record.startTime)}</span>
};
EndTimeColumnConfig: any = {
......@@ -202,9 +216,7 @@ class TableList extends React.Component<TableListProps, TableListState> {
isResizable: true,
data: 'number',
onColumnClick: this.onColumnClick,
onRender: (record): React.ReactNode => (
<span>{formatTimestamp(record.endTime, '--')}</span>
)
onRender: (record): React.ReactNode => <span>{formatTimestamp(record.endTime, '--')}</span>
};
DurationColumnConfig: any = {
......@@ -216,9 +228,7 @@ class TableList extends React.Component<TableListProps, TableListState> {
isResizable: true,
data: 'number',
onColumnClick: this.onColumnClick,
onRender: (record): React.ReactNode => (
<span className="durationsty">{convertDuration(record.duration)}</span>
)
onRender: (record): React.ReactNode => <span className='durationsty'>{convertDuration(record.duration)}</span>
};
StatusColumnConfig: any = {
......@@ -231,9 +241,7 @@ class TableList extends React.Component<TableListProps, TableListState> {
isResizable: true,
data: 'string',
onColumnClick: this.onColumnClick,
onRender: (record): React.ReactNode => (
<span className={`${record.status} commonStyle`}>{record.status}</span>
),
onRender: (record): React.ReactNode => <span className={`${record.status} commonStyle`}>{record.status}</span>
};
IntermediateCountColumnConfig: any = {
......@@ -245,9 +253,7 @@ class TableList extends React.Component<TableListProps, TableListState> {
isResizable: true,
data: 'number',
onColumnClick: this.onColumnClick,
onRender: (record): React.ReactNode => (
<span>{`#${record.intermediateCount}`}</span>
)
onRender: (record): React.ReactNode => <span>{`#${record.intermediateCount}`}</span>
};
showIntermediateModal = async (record: TrialJobInfo, event: React.SyntheticEvent<EventTarget>): Promise<void> => {
......@@ -273,7 +279,6 @@ class TableList extends React.Component<TableListProps, TableListState> {
}
// intermediateArr just store default val
metricDatas.map(item => {
if (item.type === 'PERIODICAL') {
const temp = parseMetrics(item.data);
if (typeof temp === 'object') {
......@@ -292,7 +297,7 @@ class TableList extends React.Component<TableListProps, TableListState> {
});
}
this.setState({ modalVisible: true });
}
};
// intermediate button click -> intermediate graph for each trial
// support intermediate is dict
......@@ -329,45 +334,43 @@ class TableList extends React.Component<TableListProps, TableListState> {
intermediateOption: intermediate
});
}
}
};
hideIntermediateModal = (): void => {
this.setState({
modalVisible: false
});
}
};
hideShowColumnModal = (): void => {
this.setState(() => ({ isShowColumn: false }));
}
};
// click add column btn, just show the modal of addcolumn
addColumn = (): void => {
// show user select check button
this.setState(() => ({ isShowColumn: true }));
}
};
fillSelectedRowsTostate = (selected: number[] | string[], selectedRows: Array<TableRecord>): void => {
this.setState({ selectRows: selectedRows, selectedRowKeys: selected });
}
};
// open Compare-modal
compareBtn = (): void => {
const { selectRows } = this.state;
if (selectRows.length === 0) {
alert('Please select datas you want to compare!');
} else {
this.setState({ isShowCompareModal: true });
}
}
};
// close Compare-modal
hideCompareModal = (): void => {
// close modal. clear select rows data, clear selected track
this.setState({ isShowCompareModal: false, selectedRowKeys: [], selectRows: [] });
}
};
// open customized trial modal
private setCustomizedTrial = (trialId: string, event: React.SyntheticEvent<EventTarget>): void => {
......@@ -377,21 +380,21 @@ class TableList extends React.Component<TableListProps, TableListState> {
isShowCustomizedModal: true,
copyTrialId: trialId
});
}
};
private closeCustomizedTrial = (): void => {
this.setState({
isShowCustomizedModal: false,
copyTrialId: ''
});
}
};
private onWindowResize = (): void => {
this.setState(() => ({
modalIntermediateHeight: window.innerHeight,
modalIntermediateWidth: window.innerWidth
}));
}
};
private onRenderRow: IDetailsListProps['onRenderRow'] = props => {
if (props) {
......@@ -423,8 +426,8 @@ class TableList extends React.Component<TableListProps, TableListState> {
}
// concat trial all final keys and remove dup "default" val, return list
const finalKeysList = TRIALS.finalKeys().filter(item => item !== 'default');
return (COLUMNPro.concat(parameterStr)).concat(finalKeysList);
}
return COLUMNPro.concat(parameterStr).concat(finalKeysList);
};
// get IColumn[]
// when user click [Add Column] need to use the function
......@@ -474,31 +477,29 @@ class TableList extends React.Component<TableListProps, TableListState> {
className: 'detail-table',
onRender: (record: any) => {
const trialStatus = record.status;
const flag: boolean = (trialStatus === 'RUNNING' || trialStatus === 'UNKNOWN') ? false : true;
const flag: boolean = trialStatus === 'RUNNING' || trialStatus === 'UNKNOWN' ? false : true;
return (
<Stack className="detail-button" horizontal>
<Stack className='detail-button' horizontal>
{/* see intermediate result graph */}
<PrimaryButton
className="detail-button-operation"
title="Intermediate"
className='detail-button-operation'
title='Intermediate'
onClick={this.showIntermediateModal.bind(this, record)}
>
{LineChart}
</PrimaryButton>
{/* kill job */}
{
flag
?
<PrimaryButton className="detail-button-operation" disabled={true} title="kill">
{blocked}
</PrimaryButton>
:
<KillJob trial={record} />
}
{flag ? (
<PrimaryButton className='detail-button-operation' disabled={true} title='kill'>
{blocked}
</PrimaryButton>
) : (
<KillJob trial={record} />
)}
{/* Add a new trial-customized trial */}
<PrimaryButton
className="detail-button-operation"
title="Customized trial"
className='detail-button-operation'
title='Customized trial'
onClick={this.setCustomizedTrial.bind(this, record.id)}
disabled={disabledAddCustomizedTrial}
>
......@@ -506,10 +507,10 @@ class TableList extends React.Component<TableListProps, TableListState> {
</PrimaryButton>
</Stack>
);
},
}
});
break;
case (result):
case result:
// remove SEARCH_SPACE title
// const realItem = item.replace(' (search space)', '');
showColumn.push({
......@@ -519,10 +520,8 @@ class TableList extends React.Component<TableListProps, TableListState> {
minWidth: 150,
onRender: (record: TableRecord) => {
const eachTrial = TRIALS.getTrial(record.id);
return (
<span>{eachTrial.description.parameters[item.replace(' (search space)', '')]}</span>
);
},
return <span>{eachTrial.description.parameters[item.replace(' (search space)', '')]}</span>;
}
});
break;
default:
......@@ -539,7 +538,7 @@ class TableList extends React.Component<TableListProps, TableListState> {
}
return (
<TooltipHost content={other}>
<div className="ellipsis">{other}</div>
<div className='ellipsis'>{other}</div>
</TooltipHost>
);
}
......@@ -547,7 +546,7 @@ class TableList extends React.Component<TableListProps, TableListState> {
}
}
return showColumn;
}
};
componentDidMount(): void {
window.addEventListener('resize', this.onWindowResize);
......@@ -555,14 +554,21 @@ class TableList extends React.Component<TableListProps, TableListState> {
}
componentDidUpdate(prevProps: TableListProps): void {
if (this.props.columnList !== prevProps.columnList || this.props.tableSource !== prevProps.tableSource || prevProps.trialsUpdateBroadcast !== this.props.trialsUpdateBroadcast) {
if (
this.props.columnList !== prevProps.columnList ||
this.props.tableSource !== prevProps.tableSource ||
prevProps.trialsUpdateBroadcast !== this.props.trialsUpdateBroadcast
) {
const { columnList } = this.props;
this.setState({
tableColumns: this.initTableColumnList(columnList),
allColumnList: this.getAllColumnKeys()
}, () => {
this.setState(
{
tableColumns: this.initTableColumnList(columnList),
allColumnList: this.getAllColumnKeys()
},
() => {
this.updateData();
});
}
);
}
}
......@@ -571,111 +577,153 @@ class TableList extends React.Component<TableListProps, TableListState> {
const tableSource: Array<TableRecord> = this.props.tableSource;
const { offset, perPage, sortMessage } = this.state;
if (sortMessage.field !== '') {
tableSource.sort(function (a, b): any {
if (a[sortMessage.field] === undefined || Object.is(a[sortMessage.field], NaN) || Object.is(a[sortMessage.field], Infinity) || Object.is(a[sortMessage.field], -Infinity) || typeof a[sortMessage.field] === 'object' ) {
return 1;
}
if (b[sortMessage.field] === undefined || Object.is(b[sortMessage.field], NaN) || Object.is(b[sortMessage.field], Infinity) || Object.is(b[sortMessage.field], -Infinity) || typeof b[sortMessage.field] === 'object' ) {
return -1;
}
return (sortMessage.isDescend ? a[sortMessage.field] < b[sortMessage.field] : a[sortMessage.field] > b[sortMessage.field]) ? 1 : -1;
});
if (sortMessage.field !== '') {
tableSource.sort(function(a, b): any {
if (
a[sortMessage.field] === undefined ||
Object.is(a[sortMessage.field], NaN) ||
Object.is(a[sortMessage.field], Infinity) ||
Object.is(a[sortMessage.field], -Infinity) ||
typeof a[sortMessage.field] === 'object'
) {
return 1;
}
if (
b[sortMessage.field] === undefined ||
Object.is(b[sortMessage.field], NaN) ||
Object.is(b[sortMessage.field], Infinity) ||
Object.is(b[sortMessage.field], -Infinity) ||
typeof b[sortMessage.field] === 'object'
) {
return -1;
}
return (sortMessage.isDescend
? a[sortMessage.field] < b[sortMessage.field]
: a[sortMessage.field] > b[sortMessage.field])
? 1
: -1;
});
}
const tableSlice = tableSource.slice(offset, offset + perPage)
const curPageCount = Math.ceil(tableSource.length / perPage)
const tableSlice = tableSource.slice(offset, offset + perPage);
const curPageCount = Math.ceil(tableSource.length / perPage);
this.setState({
tablePerPage: tableSlice,
pageCount: curPageCount,
pageCount: curPageCount
});
}
// update data when click the page index of pagination
handlePageClick = (evt: any): void => {
const selectedPage = evt.selected;
const offset = selectedPage * this.state.perPage;
this.setState({
currentPage: selectedPage,
offset: offset
}, () => {
this.updateData();
});
}
this.setState(
{
currentPage: selectedPage,
offset: offset
},
() => {
this.updateData();
}
);
};
// update per page items when click the dropdown of pagination
updatePerPage = (event: React.FormEvent<HTMLDivElement>, item: IDropdownOption | undefined): void => {
const { pageCount } = this.state;
if (item !== undefined) {
const currentPerPage = item.key === 'all' ? this.props.tableSource.length: Number(item.key)
const currentPageCount = this.props.tableSource.length <= currentPerPage ? 1 : pageCount
this.setState({
perPage: currentPerPage,
offset: 0,
currentPage: 0,
pageCount: currentPageCount
}, () => {
this.updateData();
});
const currentPerPage = item.key === 'all' ? this.props.tableSource.length : Number(item.key);
const currentPageCount = this.props.tableSource.length <= currentPerPage ? 1 : pageCount;
this.setState(
{
perPage: currentPerPage,
offset: 0,
currentPage: 0,
pageCount: currentPageCount
},
() => {
this.updateData();
}
);
}
}
};
render(): React.ReactNode {
const { intermediateKey, modalIntermediateWidth, modalIntermediateHeight,
tableColumns, allColumnList, isShowColumn, modalVisible,
selectRows, isShowCompareModal, intermediateOtherKeys,
isShowCustomizedModal, copyTrialId, intermediateOption,
const {
intermediateKey,
modalIntermediateWidth,
modalIntermediateHeight,
tableColumns,
allColumnList,
isShowColumn,
modalVisible,
selectRows,
isShowCompareModal,
intermediateOtherKeys,
isShowCustomizedModal,
copyTrialId,
intermediateOption,
tablePerPage
} = this.state;
const { columnList } = this.props;
const perPageOptions = [
{ key: '10', text: '10 items per page'},
{ key: '20', text: '20 items per page'},
{ key: '50', text: '50 items per page'},
{ key: 'all', text: 'All items'},
{ key: '10', text: '10 items per page' },
{ key: '20', text: '20 items per page' },
{ key: '50', text: '50 items per page' },
{ key: 'all', text: 'All items' }
];
return (
<Stack>
<div id="tableList">
<div id='tableList'>
<DetailsList
columns={tableColumns}
items={tablePerPage}
setKey="set"
setKey='set'
compact={true}
onRenderRow={this.onRenderRow}
layoutMode={DetailsListLayoutMode.justified}
selectionMode={SelectionMode.multiple}
selection={this.getSelectedRows}
/>
<Stack horizontal horizontalAlign="end" verticalAlign="baseline" styles={{root:{padding:10}}} tokens={horizontalGapStackTokens}>
<Stack
horizontal
horizontalAlign='end'
verticalAlign='baseline'
styles={{ root: { padding: 10 } }}
tokens={horizontalGapStackTokens}
>
<Dropdown
selectedKey={this.state.perPage === this.props.tableSource.length ? 'all':String(this.state.perPage)}
options={perPageOptions}
onChange={this.updatePerPage}
styles={{dropdown: { width: 150}}}/>
selectedKey={
this.state.perPage === this.props.tableSource.length
? 'all'
: String(this.state.perPage)
}
options={perPageOptions}
onChange={this.updatePerPage}
styles={{ dropdown: { width: 150 } }}
/>
<ReactPaginate
previousLabel={"<"}
nextLabel={">"}
breakLabel={"..."}
breakClassName={"break"}
pageCount={this.state.pageCount}
marginPagesDisplayed={2}
pageRangeDisplayed={2}
onPageChange={this.handlePageClick}
containerClassName={(this.props.tableSource.length == 0 ? "pagination hidden" : "pagination" )}
subContainerClassName={"pages pagination"}
disableInitialCallback={false}
activeClassName={"active"}
forcePage={this.state.currentPage}
previousLabel={'<'}
nextLabel={'>'}
breakLabel={'...'}
breakClassName={'break'}
pageCount={this.state.pageCount}
marginPagesDisplayed={2}
pageRangeDisplayed={2}
onPageChange={this.handlePageClick}
containerClassName={this.props.tableSource.length == 0 ? 'pagination hidden' : 'pagination'}
subContainerClassName={'pages pagination'}
disableInitialCallback={false}
activeClassName={'active'}
forcePage={this.state.currentPage}
/>
</Stack>
</div>
{/* Intermediate Result Modal */}
<Modal
......@@ -688,31 +736,26 @@ class TableList extends React.Component<TableListProps, TableListState> {
<IconButton
styles={iconButtonStyles}
iconProps={{ iconName: 'Cancel' }}
ariaLabel="Close popup modal"
ariaLabel='Close popup modal'
onClick={this.hideIntermediateModal as any}
/>
</div>
{
intermediateOtherKeys.length > 1
?
<Stack horizontalAlign="end" className="selectKeys">
<Dropdown
className="select"
selectedKey={intermediateKey}
options={
intermediateOtherKeys.map((key, item) => {
return {
key: key, text: intermediateOtherKeys[item]
};
})
}
onChange={this.selectOtherKeys}
/>
</Stack>
:
null
}
<div className="intermediate-graph">
{intermediateOtherKeys.length > 1 ? (
<Stack horizontalAlign='end' className='selectKeys'>
<Dropdown
className='select'
selectedKey={intermediateKey}
options={intermediateOtherKeys.map((key, item) => {
return {
key: key,
text: intermediateOtherKeys[item]
};
})}
onChange={this.selectOtherKeys}
/>
</Stack>
) : null}
<div className='intermediate-graph'>
<ReactEcharts
option={intermediateOption}
style={{
......@@ -721,14 +764,13 @@ class TableList extends React.Component<TableListProps, TableListState> {
maxHeight: 534,
padding: 20
}}
theme="my_theme"
theme='my_theme'
/>
<div className="xAxis">#Intermediate result</div>
<div className='xAxis'>#Intermediate result</div>
</div>
</Modal>
{/* Add Column Modal */}
{
isShowColumn &&
{isShowColumn && (
<ChangeColumnComponent
hideShowColumnDialog={this.hideShowColumnModal}
isHideDialog={!isShowColumn}
......@@ -736,7 +778,7 @@ class TableList extends React.Component<TableListProps, TableListState> {
selectedColumn={columnList}
changeColumn={this.props.changeColumn}
/>
}
)}
{/* compare trials based message */}
{isShowCompareModal && <Compare compareStacks={selectRows} cancelFunc={this.hideCompareModal} />}
{/* clone trial parameters and could submit a customized trial */}
......
body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
line-height: 1;
margin: 0;
padding: 0;
width: 100%;
height: 100%;
font-family:
-apple-system,
BlinkMacSystemFont,
'Segoe UI',
'Roboto',
'Oxygen',
'Ubuntu',
'Cantarell',
'Fira Sans',
'Droid Sans',
'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
line-height: 1;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
font-family:
source-code-pro,
Menlo,
Monaco,
Consolas,
'Courier New',
monospace;
}
/* http://meyerweb.com/eric/tools/css/reset/
......@@ -21,46 +35,135 @@ code {
License: none (public domain)
*/
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
main, menu, nav, output, ruby, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font: inherit;
font-size: 100%;
html,
body,
div,
span,
applet,
object,
iframe,
h1,
h2,
h3,
h4,
h5,
h6,
p,
blockquote,
pre,
a,
abbr,
acronym,
address,
big,
cite,
code,
del,
dfn,
em,
img,
ins,
kbd,
q,
s,
samp,
small,
strike,
strong,
sub,
sup,
tt,
var,
b,
u,
i,
center,
dl,
dt,
dd,
ol,
ul,
li,
fieldset,
form,
label,
legend,
table,
caption,
tbody,
tfoot,
thead,
tr,
th,
td,
article,
aside,
canvas,
details,
embed,
figure,
figcaption,
footer,
header,
hgroup,
main,
menu,
nav,
output,
ruby,
section,
summary,
time,
mark,
audio,
video {
margin: 0;
padding: 0;
border: 0;
font: inherit;
font-size: 100%;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, main, menu, nav, section {
display: block;
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
main,
menu,
nav,
section {
display: block;
}
/* HTML5 hidden-attribute fix for newer browsers */
*[hidden] {
display: none;
}
ol, ul {
list-style: none;
ol,
ul {
list-style: none;
}
blockquote, q {
quotes: none;
blockquote,
q {
quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none;
blockquote::before,
blockquote::after,
q::before,
q::after {
content: '';
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
border-collapse: collapse;
border-spacing: 0;
}
......@@ -8,21 +8,19 @@ import './index.css';
import * as serviceWorker from './serviceWorker';
ReactDOM.render(
(
<Router>
<App>
<Switch>
<Suspense fallback={null}>
<Route path="/oview" component={Overview} />
<Route path="/detail" component={TrialsDetail} />
<Route path="/" render={(): React.ReactNode => <Redirect to={{ pathname: '/oview' }} />} />
</Suspense>
</Switch>
</App>
</Router>
<App>
<Switch>
<Suspense fallback={null}>
<Route path='/oview' component={Overview} />
<Route path='/detail' component={TrialsDetail} />
<Route path='/' render={(): React.ReactNode => <Redirect to={{ pathname: '/oview' }} />} />
</Suspense>
</Switch>
</App>
</Router>,
),
document.getElementById('root')
document.getElementById('root')
);
// If you want your app to work offline and load faster, you can change
......
declare namespace NodeJS {
interface ProcessEnv {
readonly NODE_ENV: 'development' | 'production' | 'test';
readonly PUBLIC_URL: string;
}
interface ProcessEnv {
readonly NODE_ENV: 'development' | 'production' | 'test';
readonly PUBLIC_URL: string;
}
}
declare module '*.bmp' {
const src: string;
export default src;
const src: string;
export default src;
}
declare module '*.gif' {
const src: string;
export default src;
const src: string;
export default src;
}
declare module '*.jpg' {
const src: string;
export default src;
const src: string;
export default src;
}
declare module '*.jpeg' {
const src: string;
export default src;
const src: string;
export default src;
}
declare module '*.png' {
const src: string;
export default src;
const src: string;
export default src;
}
declare module '*.webp' {
......@@ -36,25 +36,25 @@ declare module '*.webp' {
}
declare module '*.svg' {
import * as React from 'react';
import * as React from 'react';
export const ReactComponent: React.FunctionComponent<React.SVGProps<SVGSVGElement>>;
export const ReactComponent: React.FunctionComponent<React.SVGProps<SVGSVGElement>>;
const src: string;
export default src;
const src: string;
export default src;
}
declare module '*.module.css' {
const classes: { [key: string]: string };
export default classes;
const classes: { [key: string]: string };
export default classes;
}
declare module '*.module.scss' {
const classes: { [key: string]: string };
export default classes;
const classes: { [key: string]: string };
export default classes;
}
declare module '*.module.sass' {
const classes: { [key: string]: string };
export default classes;
const classes: { [key: string]: string };
export default classes;
}
......@@ -15,15 +15,11 @@ const trialJobStatus = [
'SYS_CANCELED',
'EARLY_STOPPED'
];
const CONTROLTYPE = [
'SEARCH_SPACE',
'TRIAL_CONCURRENCY',
'MAX_EXEC_DURATION'
];
const CONTROLTYPE = ['SEARCH_SPACE', 'TRIAL_CONCURRENCY', 'MAX_EXEC_DURATION'];
const MONACO = {
readOnly: true,
automaticLayout: true,
scrollBeyondLastLine: false,
scrollBeyondLastLine: false
};
const DRAWEROPTION = {
minimap: { enabled: false },
......@@ -34,8 +30,17 @@ const OPERATION = 'Operation';
// defatult selected column
const COLUMN = ['Trial No.', 'ID', 'Duration', 'Status', 'Default', OPERATION];
// all choice column !dictory final
const COLUMNPro = ['Trial No.', 'ID', 'Start time', 'End time', 'Duration', 'Status',
'Intermediate result', 'Default', OPERATION];
const COLUMNPro = [
'Trial No.',
'ID',
'Start time',
'End time',
'Duration',
'Status',
'Intermediate result',
'Default',
OPERATION
];
const CONCURRENCYTOOLTIP = 'Trial concurrency is the number of trials running concurrently.';
const SUPPORTED_SEARCH_SPACE_TYPE = [
'choice',
......@@ -53,8 +58,18 @@ const SUPPORTED_SEARCH_SPACE_TYPE = [
];
export {
MANAGER_IP, DOWNLOAD_IP, trialJobStatus, COLUMNPro, WEBUIDOC,
CONTROLTYPE, MONACO, COLUMN, DRAWEROPTION, OPERATION,
METRIC_GROUP_UPDATE_THRESHOLD, METRIC_GROUP_UPDATE_SIZE, CONCURRENCYTOOLTIP,
MANAGER_IP,
DOWNLOAD_IP,
trialJobStatus,
COLUMNPro,
WEBUIDOC,
CONTROLTYPE,
MONACO,
COLUMN,
DRAWEROPTION,
OPERATION,
METRIC_GROUP_UPDATE_THRESHOLD,
METRIC_GROUP_UPDATE_SIZE,
CONCURRENCYTOOLTIP,
SUPPORTED_SEARCH_SPACE_TYPE
};
......@@ -24,7 +24,7 @@ const convertTime = (num: number): string => {
return num / 3600 + 'h';
} else {
const hour = Math.floor(num / 3600);
const min = Math.floor(num / 60 % 60);
const min = Math.floor((num / 60) % 60);
return hour > 0 ? `${hour}h ${min}min` : `${min}min`;
}
};
......@@ -35,7 +35,7 @@ const convertDuration = (num: number): string => {
return '0s';
}
const hour = Math.floor(num / 3600);
const minute = Math.floor(num / 60 % 60);
const minute = Math.floor((num / 60) % 60);
const second = Math.floor(num % 60);
const result: string[] = [];
if (hour > 0) {
......@@ -60,7 +60,7 @@ function parseMetrics(metricData: string): any {
const isArrayType = (list: any): boolean | undefined => {
return Array.isArray(list);
}
};
// get final result value
// draw Accuracy point graph
......@@ -69,11 +69,11 @@ const getFinalResult = (final?: MetricDataRecord[]): number => {
let showDefault = 0;
if (final) {
acc = parseMetrics(final[final.length - 1].data);
if (typeof (acc) === 'object' && !isArrayType(acc)) {
if (typeof acc === 'object' && !isArrayType(acc)) {
if (acc.default) {
showDefault = acc.default;
}
} else if (typeof (acc) === 'number') {
} else if (typeof acc === 'number') {
showDefault = acc;
} else {
showDefault = NaN;
......@@ -94,7 +94,7 @@ const getFinal = (final?: MetricDataRecord[]): FinalType | undefined => {
if (final) {
showDefault = parseMetrics(final[final.length - 1].data);
if (typeof showDefault === 'number') {
if(!isNaNorInfinity(showDefault)){
if (!isNaNorInfinity(showDefault)) {
return { default: showDefault };
}
} else if (isArrayType(showDefault)) {
......@@ -121,7 +121,7 @@ const intermediateGraphOption = (intermediateArr: number[], id: string): any =>
left: 'center',
textStyle: {
fontSize: 16,
color: '#333',
color: '#333'
}
},
tooltip: {
......@@ -137,11 +137,13 @@ const intermediateGraphOption = (intermediateArr: number[], id: string): any =>
data: intermediateArr,
scale: true
},
series: [{
symbolSize: 6,
type: 'scatter',
data: intermediateArr
}]
series: [
{
symbolSize: 6,
type: 'scatter',
data: intermediateArr
}
]
};
};
......@@ -159,7 +161,7 @@ const killJob = (key: number, id: string, status: string, updateList?: Function)
alert('Cancel the job successfully');
// render the table
if (updateList) {
updateList(); // FIXME
updateList(); // FIXME
}
} else {
alert('fail to cancel the job');
......@@ -180,7 +182,7 @@ const filterByStatus = (item: TableObj): boolean => {
return item.status === 'SUCCEEDED';
};
// a waittiong trial may havn't start time
// a waittiong trial may havn't start time
const filterDuration = (item: TableObj): boolean => {
return item.status !== 'WAITING';
};
......@@ -197,7 +199,7 @@ const downFile = (content: string, fileName: string): void => {
}
if (navigator.userAgent.indexOf('Firefox') > -1) {
const downTag = document.createElement('a');
downTag.addEventListener('click', function () {
downTag.addEventListener('click', function() {
downTag.download = fileName;
downTag.href = URL.createObjectURL(file);
});
......@@ -227,7 +229,10 @@ function metricAccuracy(metric: MetricDataRecord): number {
function formatAccuracy(accuracy: number): string {
// TODO: how to format NaN?
return accuracy.toFixed(6).replace(/0+$/, '').replace(/\.$/, '');
return accuracy
.toFixed(6)
.replace(/0+$/, '')
.replace(/\.$/, '');
}
function formatComplexTypeValue(value: any): string | number {
......@@ -239,8 +244,21 @@ function formatComplexTypeValue(value: any): string | number {
}
export {
convertTime, convertDuration, getFinalResult, getFinal, downFile,
intermediateGraphOption, killJob, filterByStatus, filterDuration,
formatAccuracy, formatTimestamp, metricAccuracy, parseMetrics,
isArrayType, requestAxios, isNaNorInfinity, formatComplexTypeValue
convertTime,
convertDuration,
getFinalResult,
getFinal,
downFile,
intermediateGraphOption,
killJob,
filterByStatus,
filterDuration,
formatAccuracy,
formatTimestamp,
metricAccuracy,
parseMetrics,
isArrayType,
requestAxios,
isNaNorInfinity,
formatComplexTypeValue
};
......@@ -33,7 +33,7 @@ interface TableObj {
color?: string;
startTime?: number;
endTime?: number;
parameters(axes: MultipleAxes): Map<SingleAxis, any>;
parameters(axes: MultipleAxes): Map<SingleAxis, any>;
metrics(axes: MultipleAxes): Map<SingleAxis, any>;
}
......@@ -208,8 +208,25 @@ interface EventMap {
}
export {
TableObj, TableRecord, SearchSpace, FinalType, ErrorParameter, Parameters,
AccurPoint, DetailAccurPoint, TooltipForIntermediate, TooltipForAccuracy,
Dimobj, ParaObj, Intermedia, MetricDataRecord, TrialJobInfo, ExperimentParams,
ExperimentProfile, NNIManagerStatus, EventMap, SingleAxis, MultipleAxes
TableObj,
TableRecord,
SearchSpace,
FinalType,
ErrorParameter,
Parameters,
AccurPoint,
DetailAccurPoint,
TooltipForIntermediate,
TooltipForAccuracy,
Dimobj,
ParaObj,
Intermedia,
MetricDataRecord,
TrialJobInfo,
ExperimentParams,
ExperimentProfile,
NNIManagerStatus,
EventMap,
SingleAxis,
MultipleAxes
};
......@@ -87,17 +87,33 @@ class Experiment {
// set initProfile to prevent page broken
const initProfile = {
data: {
"id": "", "revision": 0, "execDuration": 0,
"logDir": "", "nextSequenceId": 0,
"params": {
"authorName": "", "experimentName": "", "trialConcurrency": 0, "maxExecDuration": 0, "maxTrialNum": 0, "searchSpace": "null",
"trainingServicePlatform": "", "tuner": {
"builtinTunerName": "TPE",
"classArgs": { "optimize_mode": "" }, "checkpointDir": ""
id: '',
revision: 0,
execDuration: 0,
logDir: '',
nextSequenceId: 0,
params: {
authorName: '',
experimentName: '',
trialConcurrency: 0,
maxExecDuration: 0,
maxTrialNum: 0,
searchSpace: 'null',
trainingServicePlatform: '',
tuner: {
builtinTunerName: 'TPE',
// eslint-disable-next-line @typescript-eslint/camelcase
classArgs: { optimize_mode: '' },
checkpointDir: ''
},
"versionCheck": true, "clusterMetaData": [{ "key": "", "value": "" },
{ "key": "", "value": "" }]
}, "startTime": 0, "endTime": 0
versionCheck: true,
clusterMetaData: [
{ key: '', value: '' },
{ key: '', value: '' }
]
},
startTime: 0,
endTime: 0
}
};
this.profileField = initProfile.data as any;
......@@ -112,7 +128,7 @@ class Experiment {
get optimizeMode(): string {
const tuner = this.profile.params.tuner;
return (tuner && tuner.classArgs && tuner.classArgs.optimize_mode) ? tuner.classArgs.optimize_mode : 'unknown';
return tuner && tuner.classArgs && tuner.classArgs.optimize_mode ? tuner.classArgs.optimize_mode : 'unknown';
}
get trainingServicePlatform(): string {
......
......@@ -3,7 +3,7 @@ import { SUPPORTED_SEARCH_SPACE_TYPE } from '../const';
import { formatComplexTypeValue } from '../function';
function fullNameJoin(prefix: string, name: string): string {
return prefix ? (prefix + '/' + name) : name;
return prefix ? prefix + '/' + name : name;
}
class NumericAxis implements SingleAxis {
......@@ -86,17 +86,24 @@ export class SearchSpace implements MultipleAxes {
if (searchSpaceSpec === undefined) {
return;
}
Object.entries(searchSpaceSpec).forEach((item) => {
const key = item[0], spec = item[1] as any;
Object.entries(searchSpaceSpec).forEach(item => {
const key = item[0],
spec = item[1] as any;
if (key === '_name') {
return;
} else if (['choice', 'layer_choice', 'input_choice'].includes(spec._type)) {
// ordinal types
if (spec._value && typeof spec._value[0] === 'object') {
// nested dimension
this.axes.set(key, new NestedOrdinalAxis(key, fullNameJoin(fullName, key), spec._type, spec._value));
this.axes.set(
key,
new NestedOrdinalAxis(key, fullNameJoin(fullName, key), spec._type, spec._value)
);
} else {
this.axes.set(key, new SimpleOrdinalAxis(key, fullNameJoin(fullName, key), spec._type, spec._value));
this.axes.set(
key,
new SimpleOrdinalAxis(key, fullNameJoin(fullName, key), spec._type, spec._value)
);
}
} else if (SUPPORTED_SEARCH_SPACE_TYPE.includes(spec._type)) {
this.axes.set(key, new NumericAxis(key, fullName + key, spec._type, spec._value));
......@@ -129,7 +136,10 @@ export class SearchSpace implements MultipleAxes {
}
addingColumns.forEach((value, key) => {
if (value.every(v => typeof v === 'number')) {
newSearchSpace.axes.set(key, new NumericAxis(key, key, 'uniform', [Math.min(...value), Math.max(...value)]));
newSearchSpace.axes.set(
key,
new NumericAxis(key, key, 'uniform', [Math.min(...value), Math.max(...value)])
);
} else {
newSearchSpace.axes.set(key, new SimpleOrdinalAxis(key, key, 'choice', new Set(value).values()));
}
......
import * as JSON5 from 'json5';
import { MetricDataRecord, TrialJobInfo, TableObj, TableRecord, Parameters, FinalType, MultipleAxes, SingleAxis } from '../interface';
import { getFinal, formatAccuracy, metricAccuracy, parseMetrics, isArrayType, isNaNorInfinity, formatComplexTypeValue } from '../function';
import {
MetricDataRecord,
TrialJobInfo,
TableObj,
TableRecord,
Parameters,
FinalType,
MultipleAxes,
SingleAxis
} from '../interface';
import {
getFinal,
formatAccuracy,
metricAccuracy,
parseMetrics,
isArrayType,
isNaNorInfinity,
formatComplexTypeValue
} from '../function';
/**
* Get a structured representation of parameters
......@@ -9,14 +26,17 @@ import { getFinal, formatAccuracy, metricAccuracy, parseMetrics, isArrayType, is
* @param prefix Current namespace (to make full name for unexpected entries)
* @returns Parsed structured parameters and unexpected entries
*/
function inferTrialParameters(paramObj: object, space: MultipleAxes, prefix: string = ''): [Map<SingleAxis, any>, Map<string, any>] {
function inferTrialParameters(
paramObj: object,
space: MultipleAxes,
prefix: string = ''
): [Map<SingleAxis, any>, Map<string, any>] {
const parameters = new Map<SingleAxis, any>();
const unexpectedEntries = new Map<string, any>();
for (const [k, v] of Object.entries(paramObj)) {
// prefix can be a good fallback when corresponding item is not found in namespace
const axisKey = space.axes.get(k);
if (prefix && k === '_name')
continue;
if (prefix && k === '_name') continue;
if (axisKey !== undefined) {
if (typeof v === 'object' && v._name !== undefined && axisKey.nested) {
// nested entry
......@@ -93,7 +113,10 @@ class Trial implements TableObj {
if (temp !== undefined) {
if (isArrayType(parseMetrics(temp.data))) {
return undefined;
} else if (typeof parseMetrics(temp.data) === 'object' && parseMetrics(temp.data).hasOwnProperty('default')) {
} else if (
typeof parseMetrics(temp.data) === 'object' &&
parseMetrics(temp.data).hasOwnProperty('default')
) {
return parseMetrics(temp.data).default;
} else if (typeof parseMetrics(temp.data) === 'number') {
return parseMetrics(temp.data);
......@@ -182,7 +205,7 @@ class Trial implements TableObj {
ret.parameters = getPara;
}
} else {
ret.parameters = { error: 'This trial\'s parameters are not available.' };
ret.parameters = { error: "This trial's parameters are not available." };
}
if (this.info.logPath !== undefined) {
ret.logPath = this.info.logPath;
......@@ -294,7 +317,7 @@ class Trial implements TableObj {
}
public updateTrialJobInfo(trialJobInfo: TrialJobInfo): boolean {
const same = (this.infoField && this.infoField.status === trialJobInfo.status);
const same = this.infoField && this.infoField.status === trialJobInfo.status;
this.infoField = trialJobInfo;
if (trialJobInfo.finalMetricData) {
this.final = trialJobInfo.finalMetricData[trialJobInfo.finalMetricData.length - 1];
......@@ -312,7 +335,6 @@ class Trial implements TableObj {
return `${formatAccuracy(val)} (LATEST)`;
} else {
return `${formatAccuracy(val)} (FINAL)`;
}
}
} else {
......@@ -321,9 +343,10 @@ class Trial implements TableObj {
}
}
public formatLatestAccuracy(): string { // TODO: this should be private
public formatLatestAccuracy(): string {
// TODO: this should be private
if (this.status === 'SUCCEEDED') {
return (this.accuracy === undefined ? '--' : this.renderNumber(this.accuracy));
return this.accuracy === undefined ? '--' : this.renderNumber(this.accuracy);
} else {
if (this.accuracy !== undefined) {
return this.renderNumber(this.accuracy);
......@@ -335,7 +358,6 @@ class Trial implements TableObj {
return this.renderNumber(metricAccuracy(latest));
}
}
}
}
......
......@@ -11,7 +11,7 @@ function groupMetricsByTrial(metrics: MetricDataRecord[]): Map<string, MetricDat
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
ret.get(metric.trialJobId)!.push(metric);
} else {
ret.set(metric.trialJobId, [ metric ]);
ret.set(metric.trialJobId, [metric]);
}
}
return ret;
......@@ -91,7 +91,7 @@ class TrialManager {
if (succeedTrialsList !== undefined && succeedTrialsList[0] !== undefined) {
return succeedTrialsList[0].finalKeys();
} else {
return ["default"];
return ['default'];
}
}
......@@ -109,7 +109,7 @@ class TrialManager {
['FAILED', 0],
['USER_CANCELED', 0],
['SYS_CANCELED', 0],
['EARLY_STOPPED', 0],
['EARLY_STOPPED', 0]
]);
for (const trial of this.trials.values()) {
if (trial.initialized()) {
......@@ -168,7 +168,6 @@ class TrialManager {
}
private async updateInfo(): Promise<boolean> {
let updated = false;
requestAxios(`${MANAGER_IP}/trial-jobs`)
.then(data => {
......@@ -235,7 +234,11 @@ class TrialManager {
return;
}
this.doingBatchUpdate = true;
for (let i = 0; i < this.maxSequenceId && this.isMetricdataRangeError === false; i += METRIC_GROUP_UPDATE_SIZE) {
for (
let i = 0;
i < this.maxSequenceId && this.isMetricdataRangeError === false;
i += METRIC_GROUP_UPDATE_SIZE
) {
requestAxios(`${MANAGER_IP}/metric-data-range/${i}/${i + METRIC_GROUP_UPDATE_SIZE}`)
.then(data => {
const updated = this.doUpdateMetrics(data as any, false);
......
$btnBgcolor: #0071bc;
Button.tableButton{
button.tableButton {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: $btnBgcolor;
background: $btnBgcolor;
border-color: $btnBgcolor;
height: 26px;
font-size: 14px;
......@@ -11,17 +11,19 @@ Button.tableButton{
}
/* kill btn style: delete its own hover style */
/* after button click the color not change */
Button.tableButton:hover, Button.tableButton:focus{
button.tableButton:hover,
button.tableButton:focus {
background-color: $btnBgcolor;
border-color: $btnBgcolor;
}
Button.changeBtu{
button.changeBtu {
height: 32px;
}
.ant-input, .ant-select-selection{
.ant-input,
.ant-select-selection {
border-radius: 0 !important;
}
.compare-modal{
.compare-modal {
/* decide modal size */
.ms-Dialog-main{
.ms-Dialog-main {
width: 50%;
min-width: 450px;
}
.ms-Modal-scrollableContent{
.ms-Modal-scrollableContent {
overflow-x: hidden;
}
/* compare-md: table style */
.flex{
.flex {
display: flex;
}
&-table{
&-table {
width: 92%;
margin: 0 auto;
margin-bottom: 20px;
border: 1px solid transparent;
overflow: auto hidden;
color: #333;
tr{
tr {
line-height: 30px;
}
tr:nth-of-type(even){
tr:nth-of-type(even) {
background-color: gainsboro;
}
.column{
.column {
max-width: 124px;
padding-left: 18px;
font-weight: 600;
}
.value{
.value {
max-width: 152px;
padding-right: 18px;
text-align: left;
}
.idList{
.idList {
font-weight: 600;
}
}
/* compare-md: intermediate graph style */
&-intermediate{
&-intermediate {
position: relative;
.compare-yAxis{
.compare-yAxis {
color: #333;
position: absolute;
top: 87%;
left: 45%;
}
}
}
\ No newline at end of file
}
$color: #f2f2f2;
.formatStr{
.formatStr {
border: 1px solid #8f8f8f;
color: #333;
padding: 5px 10px;
background-color: #fff;
background-color: #fff;
}
.format {
.ant-modal-header{
.ant-modal-header {
background-color: $color;
border-bottom: none;
}
.ant-modal-footer{
.ant-modal-footer {
background-color: $color;
border-top: none;
}
.ant-modal-body{
.ant-modal-body {
background-color: $color;
padding: 10px 24px !important;
}
......
.iconButtons{
.iconButtons {
margin-top: 12px;
i{
i {
font-size: 16px;
color: #fff;
}
}
.docIcon{
i{
.docIcon {
i {
font-size: 28px;
}
}
\ No newline at end of file
}
.download {
button,
button:active,
button:hover {
color: #fff;
border: none;
}
button,
button:active,
button:hover {
color: #fff;
border: none;
}
}
.log-tab-body {
.refresh {
margin-left: 10px;
display: none;
}
.refresh {
margin-left: 10px;
display: none;
}
}
/* office-fabric-ui */
.ms-Panel-main {
width: 55%;
background: #fff;
width: 55%;
background: #fff;
}
/* log drawer download & close button's row */
.buttons {
margin-top: 16px;
.close {
text-align: right;
}
margin-top: 16px;
.close {
text-align: right;
}
}
.logpath{
.logpath {
margin-left: 10px;
font-size: 14px;
.logName{
color: #268BD2;
.logName {
color: #268bd2;
margin-right: 5px;
}
.logContent{
.logContent {
color: #333;
}
.error{
color: #CB4B16;
.error {
color: #cb4b16;
}
}
.logHref:hover{
.logHref:hover {
color: blue;
text-decoration: underline;
}
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