Commit 77e3a6c7 authored by Lijiao's avatar Lijiao Committed by fishyds
Browse files

[WebUI] Close timer in 'DONE | STOPPED | ERROR' status and add edit concurrency btn (#466)

* [WebUI]Close timer

* Add edit concurrency btn

* fix bug
parent d8e55165
......@@ -170,10 +170,10 @@ class Overview extends React.Component<{}, OverviewState> {
}
}
});
}
showTrials = () => {
this.isOffInterval();
axios(`${MANAGER_IP}/trial-jobs`, {
method: 'GET'
})
......@@ -219,7 +219,12 @@ class Overview extends React.Component<{}, OverviewState> {
const acc = getFinalResult(tableData[item].finalMetricData);
// if hyperparameters is undefine, show error message, else, show parameters value
if (tableData[item].hyperParameters) {
desJobDetail.parameters = JSON.parse(tableData[item].hyperParameters).parameters;
const parameters = JSON.parse(tableData[item].hyperParameters).parameters;
if (typeof parameters === 'string') {
desJobDetail.parameters = JSON.parse(parameters);
} else {
desJobDetail.parameters = parameters;
}
} else {
desJobDetail.parameters = { error: 'This trial\'s parameters are not available.' };
}
......@@ -253,6 +258,7 @@ class Overview extends React.Component<{}, OverviewState> {
trialNumber: profile
});
}
this.checkStatus();
// draw accuracy
this.drawPointGraph();
}
......@@ -371,6 +377,19 @@ class Overview extends React.Component<{}, OverviewState> {
});
}
isOffInterval = () => {
const { status } = this.state;
switch (status) {
case 'DONE':
case 'ERROR':
case 'STOPPED':
window.clearInterval(this.intervalID);
window.clearInterval(this.intervalProfile);
break;
default:
}
}
componentDidMount() {
this._isMounted = true;
this.showSessionPro();
......@@ -430,6 +449,7 @@ class Overview extends React.Component<{}, OverviewState> {
bestAccuracy={bestAccuracy}
status={status}
errors={errorStr}
updateFile={this.showSessionPro}
/>
</Col>
{/* experiment parameters search space tuner assessor... */}
......
......@@ -18,6 +18,7 @@ interface TrialDetailState {
accNodata: string;
tableListSource: Array<TableObj>;
tableBaseSource: Array<TableObj>;
experimentStatus: string;
}
class TrialsDetail extends React.Component<{}, TrialDetailState> {
......@@ -33,7 +34,8 @@ class TrialsDetail extends React.Component<{}, TrialDetailState> {
accSource: {},
accNodata: '',
tableListSource: [],
tableBaseSource: []
tableBaseSource: [],
experimentStatus: ''
};
}
// trial accuracy graph
......@@ -120,7 +122,7 @@ class TrialsDetail extends React.Component<{}, TrialDetailState> {
}
drawTableList = () => {
this.isOffIntervals();
axios.get(`${MANAGER_IP}/trial-jobs`)
.then(res => {
if (res.status === 200) {
......@@ -148,7 +150,12 @@ class TrialsDetail extends React.Component<{}, TrialDetailState> {
}
}
if (trialJobs[item].hyperParameters !== undefined) {
desc.parameters = JSON.parse(trialJobs[item].hyperParameters).parameters;
const getPara = JSON.parse(trialJobs[item].hyperParameters).parameters;
if (typeof getPara === 'string') {
desc.parameters = JSON.parse(getPara);
} else {
desc.parameters = getPara;
}
} else {
desc.parameters = { error: 'This trial\'s parameters are not available.' };
}
......@@ -187,11 +194,13 @@ class TrialsDetail extends React.Component<{}, TrialDetailState> {
break;
case '2':
this.isOffIntervals();
window.clearInterval(this.interAccuracy);
window.clearInterval(Duration.intervalDuration);
break;
case '3':
this.isOffIntervals();
window.clearInterval(this.interAccuracy);
window.clearInterval(Para.intervalIDPara);
break;
......@@ -226,6 +235,28 @@ class TrialsDetail extends React.Component<{}, TrialDetailState> {
this.drawTableList();
this.interTableList = window.setInterval(this.drawTableList, 10000);
}
isOffIntervals = () => {
axios(`${MANAGER_IP}/check-status`, {
method: 'GET'
})
.then(res => {
if (res.status === 200 && this._isMounted) {
switch (res.data.status) {
case 'DONE':
case 'ERROR':
case 'STOPPED':
window.clearInterval(this.interAccuracy);
window.clearInterval(this.interTableList);
window.clearInterval(Duration.intervalDuration);
window.clearInterval(Para.intervalIDPara);
break;
default:
}
}
});
}
componentDidMount() {
this._isMounted = true;
......
......@@ -31,8 +31,6 @@ class BasicInfo extends React.Component<BasicInfoProps, {}> {
<div>{trialProfile.id}</div>
</Col>
<Col span={8} className="padItem basic">
<Row>
<Col span={18}>
<p>Start Time</p>
<div className="nowrap">
{new Date(trialProfile.startTime).toLocaleString('en-US')}
......@@ -48,8 +46,6 @@ class BasicInfo extends React.Component<BasicInfoProps, {}> {
}
</div>
</Col>
</Row>
</Col>
<Col span={8} className="padItem basic">
<p>LogPath</p>
<div className="nowrap">
......
......@@ -2,8 +2,12 @@ import * as React from 'react';
import {
Row,
Col,
Popover
Popover,
Button,
message
} from 'antd';
import axios from 'axios';
import { MANAGER_IP, CONTROLTYPE } from '../../static/const';
import { Experiment, TrialNumber } from '../../static/interface';
import { convertTime } from '../../static/function';
import ProgressBar from './ProgressItem';
......@@ -16,19 +20,130 @@ interface ProgressProps {
bestAccuracy: string;
status: string;
errors: string;
updateFile: Function;
}
class Progressed extends React.Component<ProgressProps, {}> {
interface ProgressState {
btnName: string;
isEnable: boolean;
userInputVal?: string; // get user input
cancelSty: string;
}
class Progressed extends React.Component<ProgressProps, ProgressState> {
public conInput: HTMLInputElement | null;
constructor(props: ProgressProps) {
super(props);
this.state = {
btnName: 'Edit',
isEnable: true,
cancelSty: 'none'
};
}
editTrialConcurrency = () => {
const { btnName } = this.state;
if (btnName === 'Edit') {
this.setState(() => ({
isEnable: false,
btnName: 'Save',
cancelSty: 'inline-block'
}));
} else {
axios(`${MANAGER_IP}/experiment`, {
method: 'GET'
})
.then(rese => {
if (rese.status === 200) {
const { userInputVal } = this.state;
const experimentFile = rese.data;
const trialConcurrency = experimentFile.params.trialConcurrency;
if (userInputVal !== undefined) {
if (userInputVal === trialConcurrency.toString() || userInputVal === '') {
message.info(
`trialConcurrency's value is ${trialConcurrency}, you did not modify it`, 2);
} else {
experimentFile.params.trialConcurrency = parseInt(userInputVal, 10);
// rest api, modify trial concurrency value
axios(`${MANAGER_IP}/experiment`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json;charset=utf-8'
},
data: experimentFile,
params: {
update_type: CONTROLTYPE[1]
}
}).then(res => {
if (res.status === 200) {
message.success(`Update ${CONTROLTYPE[1].toLocaleLowerCase()} successfully`);
// rerender trial profile message
const { updateFile } = this.props;
updateFile();
}
})
.catch(error => {
if (error.response.status === 500) {
if (error.response.data.error) {
message.error(error.response.data.error);
} else {
message.error(`Update ${CONTROLTYPE[1].toLocaleLowerCase()} failed`);
}
}
});
// btn -> edit
this.setState(() => ({
btnName: 'Edit',
isEnable: true,
cancelSty: 'none'
}));
}
}
}
});
}
}
cancelFunction = () => {
const { trialProfile } = this.props;
this.setState(
() => ({
btnName: 'Edit',
isEnable: true,
cancelSty: 'none',
}));
if (this.conInput !== null) {
this.conInput.value = trialProfile.runConcurren.toString();
}
}
getUserTrialConcurrency = (event: React.ChangeEvent<HTMLInputElement>) => {
const value = event.target.value;
if (value.match(/^[1-9]\d*$/) || value === '') {
this.setState(() => ({
userInputVal: value
}));
} else {
message.error('Please enter a positive integer!', 2);
if (this.conInput !== null) {
const { trialProfile } = this.props;
this.conInput.value = trialProfile.runConcurren.toString();
}
}
}
componentWillReceiveProps() {
const { trialProfile } = this.props;
if (this.conInput !== null) {
this.conInput.value = trialProfile.runConcurren.toString();
}
}
render() {
const { trialProfile,
trialNumber, bestAccuracy,
status, errors
} = this.props;
const { trialProfile, trialNumber, bestAccuracy, status, errors } = this.props;
const { isEnable, btnName, cancelSty } = this.state;
const bar2 = trialNumber.totalCurrentTrial - trialNumber.waitTrial - trialNumber.unknowTrial;
const bar2Percent = (bar2 / trialProfile.MaxTrialNum) * 100;
const percent = (trialProfile.execDuration / trialProfile.maxDuration) * 100;
......@@ -70,6 +185,31 @@ class Progressed extends React.Component<ProgressProps, {}> {
</div>
</Row>
{/* modify concurrency */}
<Row className="inputBox">
<span className="title">Concurrency:</span>
<input
type="number"
disabled={isEnable}
onChange={this.getUserTrialConcurrency}
className="concurrencyInput"
ref={(input) => this.conInput = input}
/>
<Button
type="primary"
className="tableButton editStyle"
onClick={this.editTrialConcurrency}
>{btnName}
</Button>
<Button
type="primary"
onClick={this.cancelFunction}
style={{ display: cancelSty, marginLeft: 1 }}
className="tableButton editStyle"
>
Cancel
</Button>
</Row>
<ProgressBar
who="Duration"
percent={percent}
......
......@@ -19,7 +19,7 @@ class SearchSpace extends React.Component<SearchspaceProps, {}> {
<div className="searchSpace">
<MonacoEditor
width="100%"
height="380"
height="398"
language="json"
theme="vs-light"
value={JSON.stringify(searchSpace, null, 2)}
......
......@@ -29,7 +29,7 @@ class TrialInfo extends React.Component<TrialInfoProps, {}> {
<div className="profile">
<MonacoEditor
width="100%"
height="386"
height="396"
language="json"
theme="vs-light"
value={JSON.stringify(showProInfo[0], null, 2)}
......
import { FinalResult } from './interface';
export const convertTime = (num: number) => {
const convertTime = (num: number) => {
if (num % 3600 === 0) {
return num / 3600 + 'h';
} else {
......@@ -11,7 +11,7 @@ export const convertTime = (num: number) => {
};
// trial's duration, accurate to seconds for example 10min 30s
export const convertDuration = (num: number) => {
const convertDuration = (num: number) => {
const hour = Math.floor(num / 3600);
const min = Math.floor(num / 60 % 60);
const second = Math.floor(num % 60);
......@@ -28,7 +28,7 @@ export const convertDuration = (num: number) => {
};
// get final result value
export const getFinalResult = (final: FinalResult) => {
const getFinalResult = (final: FinalResult) => {
let acc;
let showDefault = 0;
if (final) {
......@@ -45,3 +45,7 @@ export const getFinalResult = (final: FinalResult) => {
return 0;
}
};
export {
convertTime, convertDuration, getFinalResult
};
$btnBgcolor: #3c8dbc;
$btnBgcolor: #0071bc;
Button.tableButton{
background: $btnBgcolor;
......
/* new style */
.overMessage{
height: 424px;
height: 456px;
}
.overGraph{
height: 362px;
......
......@@ -5,8 +5,6 @@
color: #0573bc;
font-size: 20px;
font-weight: bold;
border-bottom: 1px solid #ccc;
padding-bottom: 12px;
}
.probar{
......@@ -54,7 +52,7 @@
}
div{
font-size: 16px;
color: #8c8c8c;
color: #333;
}
}
......@@ -72,4 +70,25 @@
min-width: 300px;
}
*/
.inputBox{
height: 30px;
margin-top: 8px;
border-bottom: 1px solid #ccc;
padding-bottom: 44px;
.title{
font-size: 14px;
margin-right: 10px;
}
.concurrencyInput{
width: 25%;
height: 32px;
outline: none;
border: 1px solid #ccc;
}
.editStyle{
height: 32px;
}
}
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