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

Multiple Experiments Management UX (#3127)



* first update

* add click event

* add manager exp nav

* first demo, fix some comments

* use api to dev

* refactor code

* fix lint

* fix test met issue and adjust column width

* add /experiments-info  error status

* no declare platform list, get this from api result filter

* fix compare tooltip issue

* fix tooltip location

* fix some comments

* delete datestring

* fix clickable style and clickable column ID rather than name
Co-authored-by: default avatarLijiao <Lijiaoa@outlook.com>
parent af198888
...@@ -4,14 +4,13 @@ import App from './App'; ...@@ -4,14 +4,13 @@ import App from './App';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'; import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
const Overview = lazy(() => import('./components/Overview')); const Overview = lazy(() => import('./components/Overview'));
const TrialsDetail = lazy(() => import('./components/TrialsDetail')); const TrialsDetail = lazy(() => import('./components/TrialsDetail'));
const Experiment = lazy(() => import('./components/managementExp/ExperimentManager'));
import './index.css'; import './index.css';
import './static/style/loading.scss'; import './static/style/loading.scss';
import * as serviceWorker from './serviceWorker'; import * as serviceWorker from './serviceWorker';
ReactDOM.render( ReactDOM.render(
<Router> <Router>
<App>
<Switch>
<Suspense <Suspense
fallback={ fallback={
<div className='loading'> <div className='loading'>
...@@ -19,12 +18,15 @@ ReactDOM.render( ...@@ -19,12 +18,15 @@ ReactDOM.render(
</div> </div>
} }
> >
<Route path='/experiment' component={Experiment} exact />
<Switch>
<App>
<Route path='/' component={Overview} exact /> <Route path='/' component={Overview} exact />
<Route path='/oview' component={Overview} /> <Route path='/oview' component={Overview} />
<Route path='/detail' component={TrialsDetail} /> <Route path='/detail' component={TrialsDetail} />
</Suspense>
</Switch>
</App> </App>
</Switch>
</Suspense>
</Router>, </Router>,
document.getElementById('root') document.getElementById('root')
......
...@@ -15,6 +15,16 @@ const trialJobStatus = [ ...@@ -15,6 +15,16 @@ const trialJobStatus = [
'SYS_CANCELED', 'SYS_CANCELED',
'EARLY_STOPPED' 'EARLY_STOPPED'
]; ];
const EXPERIMENTSTATUS = [
'INITIALIZED',
'RUNNING',
'ERROR',
'STOPPING',
'STOPPED',
'DONE',
'NO_MORE_TRIAL',
'TUNER_NO_MORE_TRIAL'
];
const CONTROLTYPE = ['MAX_EXEC_DURATION', 'MAX_TRIAL_NUM', 'TRIAL_CONCURRENCY', 'SEARCH_SPACE']; const CONTROLTYPE = ['MAX_EXEC_DURATION', 'MAX_TRIAL_NUM', 'TRIAL_CONCURRENCY', 'SEARCH_SPACE'];
const MONACO = { const MONACO = {
readOnly: true, readOnly: true,
...@@ -64,6 +74,7 @@ export { ...@@ -64,6 +74,7 @@ export {
MANAGER_IP, MANAGER_IP,
DOWNLOAD_IP, DOWNLOAD_IP,
trialJobStatus, trialJobStatus,
EXPERIMENTSTATUS,
COLUMNPro, COLUMNPro,
WEBUIDOC, WEBUIDOC,
CONTROLTYPE, CONTROLTYPE,
......
...@@ -236,6 +236,14 @@ function formatTimestamp(timestamp?: number, placeholder?: string): string { ...@@ -236,6 +236,14 @@ function formatTimestamp(timestamp?: number, placeholder?: string): string {
return timestamp ? new Date(timestamp).toLocaleString('en-US') : placeholder; return timestamp ? new Date(timestamp).toLocaleString('en-US') : placeholder;
} }
function expformatTimestamp(timestamp: number | string): string {
if (typeof timestamp === 'number') {
return new Date(timestamp).toLocaleString('en-US');
} else {
return 'N/A';
}
}
function metricAccuracy(metric: MetricDataRecord): number { function metricAccuracy(metric: MetricDataRecord): number {
const data = parseMetrics(metric.data); const data = parseMetrics(metric.data);
// return typeof data === 'number' ? data : NaN; // return typeof data === 'number' ? data : NaN;
...@@ -262,6 +270,10 @@ function formatComplexTypeValue(value: any): string | number { ...@@ -262,6 +270,10 @@ function formatComplexTypeValue(value: any): string | number {
} }
} }
function isManagerExperimentPage(): boolean {
return location.pathname.indexOf('experiment') === -1 ? false : true;
}
function caclMonacoEditorHeight(height): number { function caclMonacoEditorHeight(height): number {
// [Search space 56px] + [marginBottom 18px] + // [Search space 56px] + [marginBottom 18px] +
// button[height: 32px, marginTop: 45px, marginBottom: 7px] // button[height: 32px, marginTop: 45px, marginBottom: 7px]
...@@ -306,12 +318,14 @@ export { ...@@ -306,12 +318,14 @@ export {
filterDuration, filterDuration,
formatAccuracy, formatAccuracy,
formatTimestamp, formatTimestamp,
expformatTimestamp,
metricAccuracy, metricAccuracy,
parseMetrics, parseMetrics,
isArrayType, isArrayType,
requestAxios, requestAxios,
isNaNorInfinity, isNaNorInfinity,
formatComplexTypeValue, formatComplexTypeValue,
isManagerExperimentPage,
caclMonacoEditorHeight, caclMonacoEditorHeight,
copyAndSort copyAndSort
}; };
...@@ -120,6 +120,7 @@ interface Intermedia { ...@@ -120,6 +120,7 @@ interface Intermedia {
type: string; type: string;
data: Array<number | object>; // intermediate data data: Array<number | object>; // intermediate data
hyperPara: object; // each trial hyperpara value hyperPara: object; // each trial hyperpara value
trialNum: number;
} }
interface MetricDataRecord { interface MetricDataRecord {
...@@ -218,6 +219,19 @@ interface SortInfo { ...@@ -218,6 +219,19 @@ interface SortInfo {
isDescend?: boolean; isDescend?: boolean;
} }
interface AllExperimentList {
id: string;
experimentName: string;
port: number;
status: string;
platform: string;
startTime: number;
endTime: number;
tag: string[];
pid: number;
webuiUrl: string[];
logDir: string[];
}
export { export {
TableObj, TableObj,
TableRecord, TableRecord,
...@@ -240,5 +254,6 @@ export { ...@@ -240,5 +254,6 @@ export {
EventMap, EventMap,
SingleAxis, SingleAxis,
MultipleAxes, MultipleAxes,
SortInfo SortInfo,
AllExperimentList
}; };
import { MANAGER_IP } from '../const';
import { AllExperimentList } from '../interface';
import { requestAxios } from '../function';
class ExperimentsManager {
private experimentList: AllExperimentList[] = [];
private platform: string[] = [];
private errorMessage: string = '';
public getExperimentList(): AllExperimentList[] {
return this.experimentList;
}
public getPlatformList(): string[] {
return this.platform;
}
public getExpErrorMessage(): string {
return this.errorMessage;
}
public async init(): Promise<void> {
await requestAxios(`${MANAGER_IP}/experiments-info`)
.then(data => {
const platforms: Set<string> = new Set();
for (const item of data) {
if (item.port !== undefined) {
if (typeof item.port === 'string') {
item.port = JSON.parse(item.port);
}
}
platforms.add(item.platform);
}
this.experimentList = data;
this.platform = Array.from(platforms);
})
.catch(error => {
this.errorMessage = error.message;
});
}
}
export { ExperimentsManager };
.cursor{ .ellipsis {
&:hover, & i:hover{ overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.link {
outline: none;
color: #333 !important;
text-decoration: none;
&:hover {
color: #0071bc !important;
text-decoration: underline;
}
&:active,
&:visited {
color: #0071bc;
}
}
.cursor {
&:hover,
& i:hover {
cursor: pointer; cursor: pointer;
} }
} }
.expBackground {
background: #f2f2f2;
height: 100%;
.content {
background: #fff;
}
}
.experimentList {
padding: 42px;
.box {
.search {
width: 90%;
&-input {
width: 330px;
}
}
.filter {
width: 10%;
text-align: right;
&-button-open {
background: #f3f2f1;
}
}
}
.filter-condition {
margin-top: 26px;
.reset {
width: 80px;
position: relative;
top: 29px;
}
&-status {
width: 194px;
}
&-platform {
width: 150px;
}
}
.hidden {
display: none;
}
.margin {
margin-left: 10px;
}
.tagContainer {
width: 100%;
.tag {
font-weight: 500;
background: #f2f2f2;
margin: 0 4px;
padding: 0 6px;
}
}
}
...@@ -31,9 +31,17 @@ $barHeight: 56px; ...@@ -31,9 +31,17 @@ $barHeight: 56px;
background-color: transparent; background-color: transparent;
} }
} }
.experiment {
position: relative;
top: 3px;
text-decoration: none;
}
} }
.nav { .nav {
width: 100%;
min-width: 1200px;
height: $barHeight; height: $barHeight;
line-height: $barHeight; line-height: $barHeight;
...@@ -43,6 +51,11 @@ $barHeight: 56px; ...@@ -43,6 +51,11 @@ $barHeight: 56px;
top: 6px; top: 6px;
} }
.logoTitle {
font-size: 18px;
color: #fff;
}
&-refresh { &-refresh {
position: relative; position: relative;
display: flex; display: flex;
...@@ -81,3 +94,19 @@ a.common-tabs { ...@@ -81,3 +94,19 @@ a.common-tabs {
margin-left: 20px; margin-left: 20px;
margin-right: 20px; margin-right: 20px;
} }
.expNavTitle {
span {
color: #fff;
text-decoration: none;
position: relative;
top: -4px;
}
i {
color: #fff;
font-size: 12px;
position: relative;
top: -3px;
padding-left: 4px;
}
}
.status {
color: #0573bc;
font-size: 20px;
font-weight: 600;
.status-text {
display: inline-block;
}
.color {
color: #333;
}
}
.inputBox {
height: 32px;
margin-top: 5px;
}
/* office-fabric-ui progressIndicator */
.ms-ProgressIndicator-itemProgress {
padding: 0;
}
...@@ -58,6 +58,7 @@ $margin: 24px; ...@@ -58,6 +58,7 @@ $margin: 24px;
.concurrency { .concurrency {
.editparam { .editparam {
margin-top: 5px; margin-top: 5px;
position: relative;
} }
} }
.editparam { .editparam {
...@@ -124,5 +125,6 @@ $margin: 24px; ...@@ -124,5 +125,6 @@ $margin: 24px;
.info { .info {
position: absolute; position: absolute;
z-index: 999; z-index: 999;
left: 0; left: -50%;
width: 270%;
} }
...@@ -65,9 +65,8 @@ $boxGapPadding: 10px; ...@@ -65,9 +65,8 @@ $boxGapPadding: 10px;
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif; font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
p { p {
color: #8f8f8f;
font-size: 14px; font-size: 14px;
font-weight: normal; color: #8f8f8f;
span { span {
color: #484848; color: #484848;
...@@ -80,11 +79,6 @@ $boxGapPadding: 10px; ...@@ -80,11 +79,6 @@ $boxGapPadding: 10px;
color: #484848; color: #484848;
} }
.nowrap {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
} }
.overviewBestMetric { .overviewBestMetric {
......
.status {
color: #0573bc;
font-size: 20px;
font-weight: 600;
.status-text {
display: inline-block;
}
.color {
color: #333;
}
}
.probar {
width: 100%;
height: 34px;
margin-top: 15px;
.showProgress {
width: 300px;
height: 30px;
}
.name {
width: 178px;
box-sizing: border-box;
line-height: 30px;
text-align: center;
color: #fff;
background-color: #999;
border: 2px solid #e6e6e6;
border-top-left-radius: 12px;
border-bottom-left-radius: 12px;
}
.boundary {
width: 100%;
line-height: 24px;
font-size: 12px;
color: #212121;
.right {
text-align: right;
}
}
.description {
line-height: 34px;
margin-left: 6px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
.inputBox {
height: 32px;
margin-top: 5px;
}
/* office-fabric-ui progressIndicator */
.ms-ProgressIndicator-itemProgress {
padding: 0;
}
...@@ -13,12 +13,6 @@ ...@@ -13,12 +13,6 @@
} }
} }
.ellipsis {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
#succeTable, #succeTable,
#tableList { #tableList {
.commonTableStyle .leftTitle div { .commonTableStyle .leftTitle div {
......
This diff is collapsed.
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