Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
OpenDAS
nni
Commits
1cd7ad5f
"...models/git@developer.sourcefind.cn:OpenDAS/vision.git" did not exist on "5ba95873d66e7eabb2576f9fca15b453c3fbaf5b"
Unverified
Commit
1cd7ad5f
authored
Oct 12, 2020
by
Lijiaoa
Committed by
GitHub
Oct 12, 2020
Browse files
[webui] refactor overview page (#2924)
parent
0a6c234a
Changes
67
Hide whitespace changes
Inline
Side-by-side
Showing
20 changed files
with
603 additions
and
710 deletions
+603
-710
src/webui/src/App.scss
src/webui/src/App.scss
+2
-2
src/webui/src/App.tsx
src/webui/src/App.tsx
+14
-30
src/webui/src/components/Overview.tsx
src/webui/src/components/Overview.tsx
+118
-126
src/webui/src/components/TrialsDetail.tsx
src/webui/src/components/TrialsDetail.tsx
+8
-5
src/webui/src/components/buttons/Icon.tsx
src/webui/src/components/buttons/Icon.tsx
+8
-2
src/webui/src/components/overview/Accuracy.tsx
src/webui/src/components/overview/Accuracy.tsx
+1
-1
src/webui/src/components/overview/BasicInfo.tsx
src/webui/src/components/overview/BasicInfo.tsx
+0
-43
src/webui/src/components/overview/NumInput.tsx
src/webui/src/components/overview/NumInput.tsx
+0
-65
src/webui/src/components/overview/Progress.tsx
src/webui/src/components/overview/Progress.tsx
+0
-315
src/webui/src/components/overview/ProgressItem.tsx
src/webui/src/components/overview/ProgressItem.tsx
+0
-42
src/webui/src/components/overview/SearchSpace.tsx
src/webui/src/components/overview/SearchSpace.tsx
+0
-30
src/webui/src/components/overview/Title.tsx
src/webui/src/components/overview/Title.tsx
+16
-0
src/webui/src/components/overview/TitleContext.tsx
src/webui/src/components/overview/TitleContext.tsx
+6
-0
src/webui/src/components/overview/TrialProfile.tsx
src/webui/src/components/overview/TrialProfile.tsx
+0
-49
src/webui/src/components/overview/count/EditExperimentParam.tsx
...bui/src/components/overview/count/EditExperimentParam.tsx
+147
-0
src/webui/src/components/overview/count/ExpDuration.tsx
src/webui/src/components/overview/count/ExpDuration.tsx
+54
-0
src/webui/src/components/overview/count/ExpDurationContext.tsx
...ebui/src/components/overview/count/ExpDurationContext.tsx
+7
-0
src/webui/src/components/overview/count/TrialCount.tsx
src/webui/src/components/overview/count/TrialCount.tsx
+107
-0
src/webui/src/components/overview/count/context.tsx
src/webui/src/components/overview/count/context.tsx
+17
-0
src/webui/src/components/overview/experiment/BasicInfo.tsx
src/webui/src/components/overview/experiment/BasicInfo.tsx
+98
-0
No files found.
src/webui/src/App.scss
View file @
1cd7ad5f
...
@@ -26,9 +26,9 @@
...
@@ -26,9 +26,9 @@
}
}
.content
{
.content
{
width
:
89%
;
width
:
87%
;
min-width
:
1024px
;
margin
:
0
auto
;
margin
:
0
auto
;
min-width
:
1200px
;
margin-top
:
74px
;
margin-top
:
74px
;
margin-bottom
:
30px
;
margin-bottom
:
30px
;
}
}
...
...
src/webui/src/App.tsx
View file @
1cd7ad5f
...
@@ -4,6 +4,7 @@ import { COLUMN } from './static/const';
...
@@ -4,6 +4,7 @@ import { COLUMN } from './static/const';
import
{
EXPERIMENT
,
TRIALS
}
from
'
./static/datamodel
'
;
import
{
EXPERIMENT
,
TRIALS
}
from
'
./static/datamodel
'
;
import
NavCon
from
'
./components/NavCon
'
;
import
NavCon
from
'
./components/NavCon
'
;
import
MessageInfo
from
'
./components/modals/MessageInfo
'
;
import
MessageInfo
from
'
./components/modals/MessageInfo
'
;
import
{
TrialConfigButton
}
from
'
./components/public-child/config/TrialConfigButton
'
;
import
'
./App.scss
'
;
import
'
./App.scss
'
;
interface
AppState
{
interface
AppState
{
...
@@ -30,12 +31,13 @@ export const AppContext = React.createContext({
...
@@ -30,12 +31,13 @@ export const AppContext = React.createContext({
// eslint-disable-next-line @typescript-eslint/no-empty-function, @typescript-eslint/no-unused-vars
// eslint-disable-next-line @typescript-eslint/no-empty-function, @typescript-eslint/no-unused-vars
changeMetricGraphMode
:
(
val
:
'
max
'
|
'
min
'
)
=>
{},
changeMetricGraphMode
:
(
val
:
'
max
'
|
'
min
'
)
=>
{},
// eslint-disable-next-line @typescript-eslint/no-empty-function, @typescript-eslint/no-unused-vars
// eslint-disable-next-line @typescript-eslint/no-empty-function, @typescript-eslint/no-unused-vars
changeEntries
:
(
val
:
string
)
=>
{}
changeEntries
:
(
val
:
string
)
=>
{},
// eslint-disable-next-line @typescript-eslint/no-empty-function, @typescript-eslint/no-unused-vars
updateOverviewPage
:
()
=>
{}
});
});
class
App
extends
React
.
Component
<
{},
AppState
>
{
class
App
extends
React
.
Component
<
{},
AppState
>
{
private
timerId
!
:
number
|
undefined
;
private
timerId
!
:
number
|
undefined
;
private
dataFormatimer
!
:
number
;
private
firstLoad
:
boolean
=
false
;
// when click refresh selector options
private
firstLoad
:
boolean
=
false
;
// when click refresh selector options
constructor
(
props
:
{})
{
constructor
(
props
:
{})
{
super
(
props
);
super
(
props
);
...
@@ -60,35 +62,8 @@ class App extends React.Component<{}, AppState> {
...
@@ -60,35 +62,8 @@ class App extends React.Component<{}, AppState> {
metricGraphMode
:
EXPERIMENT
.
optimizeMode
===
'
minimize
'
?
'
min
'
:
'
max
'
metricGraphMode
:
EXPERIMENT
.
optimizeMode
===
'
minimize
'
?
'
min
'
:
'
max
'
}));
}));
this
.
timerId
=
window
.
setTimeout
(
this
.
refresh
,
this
.
state
.
interval
*
100
);
this
.
timerId
=
window
.
setTimeout
(
this
.
refresh
,
this
.
state
.
interval
*
100
);
// final result is legal
// get a succeed trial,see final result data's format
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
this
.
dataFormatimer
=
window
.
setInterval
(
this
.
getFinalDataFormat
,
this
.
state
.
interval
*
1000
);
}
}
getFinalDataFormat
=
():
void
=>
{
for
(
let
i
=
0
;
this
.
state
.
isillegalFinal
===
false
;
i
++
)
{
if
(
TRIALS
.
succeededTrials
()[
0
]
!==
undefined
&&
TRIALS
.
succeededTrials
()[
0
].
final
!==
undefined
)
{
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const
oneSucceedTrial
=
JSON
.
parse
(
JSON
.
parse
(
TRIALS
.
succeededTrials
()[
0
].
final
!
.
data
));
if
(
typeof
oneSucceedTrial
===
'
number
'
||
oneSucceedTrial
.
hasOwnProperty
(
'
default
'
))
{
window
.
clearInterval
(
this
.
dataFormatimer
);
break
;
}
else
{
// illegal final data
this
.
setState
(()
=>
({
isillegalFinal
:
true
,
expWarningMessage
:
'
WebUI support final result as number and dictornary includes default keys, your experiment final result is illegal, please check your data.
'
}));
window
.
clearInterval
(
this
.
dataFormatimer
);
}
}
else
{
break
;
}
}
};
changeInterval
=
(
interval
:
number
):
void
=>
{
changeInterval
=
(
interval
:
number
):
void
=>
{
window
.
clearTimeout
(
this
.
timerId
);
window
.
clearTimeout
(
this
.
timerId
);
if
(
interval
===
0
)
{
if
(
interval
===
0
)
{
...
@@ -116,6 +91,12 @@ class App extends React.Component<{}, AppState> {
...
@@ -116,6 +91,12 @@ class App extends React.Component<{}, AppState> {
this
.
setState
({
bestTrialEntries
:
entries
});
this
.
setState
({
bestTrialEntries
:
entries
});
};
};
updateOverviewPage
=
():
void
=>
{
this
.
setState
(
state
=>
({
experimentUpdateBroadcast
:
state
.
experimentUpdateBroadcast
+
1
}));
};
shouldComponentUpdate
(
nextProps
:
any
,
nextState
:
AppState
):
boolean
{
shouldComponentUpdate
(
nextProps
:
any
,
nextState
:
AppState
):
boolean
{
if
(
!
(
nextState
.
isUpdate
||
nextState
.
isUpdate
===
undefined
))
{
if
(
!
(
nextState
.
isUpdate
||
nextState
.
isUpdate
===
undefined
))
{
nextState
.
isUpdate
=
true
;
nextState
.
isUpdate
=
true
;
...
@@ -155,6 +136,8 @@ class App extends React.Component<{}, AppState> {
...
@@ -155,6 +136,8 @@ class App extends React.Component<{}, AppState> {
</
div
>
</
div
>
<
Stack
className
=
'contentBox'
>
<
Stack
className
=
'contentBox'
>
<
Stack
className
=
'content'
>
<
Stack
className
=
'content'
>
{
/* search space & config */
}
<
TrialConfigButton
/>
{
/* if api has error field, show error message */
}
{
/* if api has error field, show error message */
}
{
errorList
.
map
(
{
errorList
.
map
(
(
item
,
key
)
=>
(
item
,
key
)
=>
...
@@ -179,7 +162,8 @@ class App extends React.Component<{}, AppState> {
...
@@ -179,7 +162,8 @@ class App extends React.Component<{}, AppState> {
metricGraphMode
,
metricGraphMode
,
changeMetricGraphMode
:
this
.
changeMetricGraphMode
,
changeMetricGraphMode
:
this
.
changeMetricGraphMode
,
bestTrialEntries
,
bestTrialEntries
,
changeEntries
:
this
.
changeEntries
changeEntries
:
this
.
changeEntries
,
updateOverviewPage
:
this
.
updateOverviewPage
}
}
}
}
>
>
{
this
.
props
.
children
}
{
this
.
props
.
children
}
...
...
src/webui/src/components/Overview.tsx
View file @
1cd7ad5f
import
*
as
React
from
'
react
'
;
import
*
as
React
from
'
react
'
;
import
{
Stack
,
I
StackTokens
,
Dropdow
n
}
from
'
@fluentui/react
'
;
import
{
Stack
,
I
con
,
Dropdown
,
DefaultButto
n
}
from
'
@fluentui/react
'
;
import
{
EXPERIMENT
,
TRIALS
}
from
'
../static/datamodel
'
;
import
{
EXPERIMENT
,
TRIALS
}
from
'
../static/datamodel
'
;
import
{
Trial
}
from
'
../static/model/trial
'
;
import
{
Trial
}
from
'
../static/model/trial
'
;
import
{
AppContext
}
from
'
../App
'
;
import
{
AppContext
}
from
'
../App
'
;
import
{
Title1
}
from
'
./overview/Title1
'
;
import
{
Title
}
from
'
./overview/Title
'
;
import
SuccessTable
from
'
./overview/SuccessTable
'
;
import
SuccessTable
from
'
./overview/table/SuccessTable
'
;
import
Progressed
from
'
./overview/Progress
'
;
import
Accuracy
from
'
./overview/Accuracy
'
;
import
Accuracy
from
'
./overview/Accuracy
'
;
import
SearchSpace
from
'
./overview/SearchSpace
'
;
import
{
ReBasicInfo
}
from
'
./overview/experiment/BasicInfo
'
;
import
{
BasicInfo
}
from
'
./overview/BasicInfo
'
;
import
{
ExpDuration
}
from
'
./overview/count/ExpDuration
'
;
import
TrialInfo
from
'
./overview/TrialProfile
'
;
import
{
ExpDurationContext
}
from
'
./overview/count/ExpDurationContext
'
;
import
'
../static/style/overview.scss
'
;
import
{
TrialCount
}
from
'
./overview/count/TrialCount
'
;
import
{
Command
}
from
'
./overview/experiment/Command
'
;
import
{
TitleContext
}
from
'
./overview/TitleContext
'
;
import
{
itemStyle1
,
itemStyleSucceed
,
itemStyle2
,
entriesOption
}
from
'
./overview/overviewConst
'
;
import
'
../static/style/overview/overview.scss
'
;
import
'
../static/style/logPath.scss
'
;
import
'
../static/style/logPath.scss
'
;
const
stackTokens
:
IStackTokens
=
{
childrenGap
:
30
};
const
entriesOption
=
[
{
key
:
'
10
'
,
text
:
'
Display top 10 trials
'
},
{
key
:
'
20
'
,
text
:
'
Display top 20 trials
'
},
{
key
:
'
30
'
,
text
:
'
Display top 30 trials
'
},
{
key
:
'
50
'
,
text
:
'
Display top 50 trials
'
},
{
key
:
'
100
'
,
text
:
'
Display top 100 trials
'
}
];
interface
OverviewState
{
interface
OverviewState
{
trialConcurrency
:
number
;
trialConcurrency
:
number
;
}
}
export
const
TitleContext
=
React
.
createContext
({
export
const
BestMetricContext
=
React
.
createContext
({
text
:
''
,
bestAccuracy
:
0
icon
:
''
,
fontColor
:
''
});
});
class
Overview
extends
React
.
Component
<
{},
OverviewState
>
{
class
Overview
extends
React
.
Component
<
{},
OverviewState
>
{
static
contextType
=
AppContext
;
static
contextType
=
AppContext
;
context
!
:
React
.
ContextType
<
typeof
AppContext
>
;
constructor
(
props
)
{
constructor
(
props
)
{
super
(
props
);
super
(
props
);
...
@@ -58,11 +48,6 @@ class Overview extends React.Component<{}, OverviewState> {
...
@@ -58,11 +48,6 @@ class Overview extends React.Component<{}, OverviewState> {
changeMetricGraphMode
(
'
min
'
);
changeMetricGraphMode
(
'
min
'
);
};
};
changeConcurrency
=
(
val
:
number
):
void
=>
{
this
.
setState
({
trialConcurrency
:
val
});
};
// updateEntries = (event: React.FormEvent<HTMLDivElement>, item: IDropdownOption | undefined): void => {
updateEntries
=
(
event
:
React
.
FormEvent
<
HTMLDivElement
>
,
item
:
any
):
void
=>
{
updateEntries
=
(
event
:
React
.
FormEvent
<
HTMLDivElement
>
,
item
:
any
):
void
=>
{
if
(
item
!==
undefined
)
{
if
(
item
!==
undefined
)
{
this
.
context
.
changeEntries
(
item
.
key
);
this
.
context
.
changeEntries
(
item
.
key
);
...
@@ -70,118 +55,125 @@ class Overview extends React.Component<{}, OverviewState> {
...
@@ -70,118 +55,125 @@ class Overview extends React.Component<{}, OverviewState> {
};
};
render
():
React
.
ReactNode
{
render
():
React
.
ReactNode
{
const
{
trialConcurrency
}
=
this
.
state
;
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
accuracyGraphData
=
this
.
generateAccuracyGraph
(
bestTrials
);
const
noDataMessage
=
bestTrials
.
length
>
0
?
''
:
'
No data
'
;
const
noDataMessage
=
bestTrials
.
length
>
0
?
''
:
'
No data
'
;
const
maxExecDuration
=
EXPERIMENT
.
profile
.
params
.
maxExecDuration
;
const
execDuration
=
EXPERIMENT
.
profile
.
execDuration
;
return
(
return
(
<
AppContext
.
Consumer
>
<
AppContext
.
Consumer
>
{
(
value
):
React
.
ReactNode
=>
{
{
(
value
):
React
.
ReactNode
=>
{
const
{
experimentUpdateBroadcast
,
metricGraphMode
,
bestTrialEntries
}
=
value
;
const
{
metricGraphMode
,
bestTrialEntries
,
updateOverviewPage
}
=
value
;
const
titleMaxbgcolor
=
metricGraphMode
===
'
max
'
?
'
#333
'
:
'
#b3b3b3
'
;
const
maxActive
=
metricGraphMode
===
'
max
'
?
'
active
'
:
'
'
;
const
titleMinbgcolor
=
metricGraphMode
===
'
min
'
?
'
#333
'
:
'
#b3b3b3
'
;
const
minActive
=
metricGraphMode
===
'
min
'
?
'
active
'
:
'
'
;
return
(
return
(
<
div
className
=
'overview'
>
<
div
className
=
'overview'
>
{
/* status and experiment block */
}
<
div
className
=
'wrapper'
>
<
Stack
className
=
'bottomDiv bgNNI'
>
{
/* exp params */
}
<
TitleContext
.
Provider
value
=
{
{
text
:
'
Experiment
'
,
icon
:
'
11.png
'
,
fontColor
:
''
}
}
>
<
div
className
=
'overviewBasicInfo'
>
<
Title1
/>
<
TitleContext
.
Provider
value
=
{
{
text
:
'
Experiment
'
,
icon
:
'
AutoRacing
'
}
}
>
</
TitleContext
.
Provider
>
<
Title
/>
<
BasicInfo
/>
</
Stack
>
<
Stack
horizontal
className
=
'overMessage bottomDiv'
>
{
/* status block */
}
<
Stack
.
Item
grow
className
=
'prograph bgNNI borderRight'
>
<
TitleContext
.
Provider
value
=
{
{
text
:
'
Status
'
,
icon
:
'
5.png
'
,
fontColor
:
''
}
}
>
<
Title1
/>
</
TitleContext
.
Provider
>
<
Progressed
bestAccuracy
=
{
bestAccuracy
}
concurrency
=
{
trialConcurrency
}
changeConcurrency
=
{
this
.
changeConcurrency
}
experimentUpdateBroadcast
=
{
experimentUpdateBroadcast
}
/>
</
Stack
.
Item
>
{
/* experiment parameters search space tuner assessor... */
}
<
Stack
.
Item
grow
styles
=
{
{
root
:
{
width
:
450
}
}
}
className
=
'overviewBoder borderRight bgNNI'
>
<
TitleContext
.
Provider
value
=
{
{
text
:
'
Search space
'
,
icon
:
'
10.png
'
,
fontColor
:
''
}
}
>
<
Title1
/>
</
TitleContext
.
Provider
>
</
TitleContext
.
Provider
>
<
Stack
className
=
'experiment'
>
<
BestMetricContext
.
Provider
value
=
{
{
bestAccuracy
:
bestAccuracy
}
}
>
<
SearchSpace
searchSpace
=
{
EXPERIMENT
.
searchSpace
}
/>
<
ReBasicInfo
/>
</
Stack
>
</
BestMetricContext
.
Provider
>
</
Stack
.
Item
>
</
div
>
<
Stack
.
Item
grow
styles
=
{
{
root
:
{
width
:
450
}
}
}
className
=
'bgNNI'
>
{
/* duration & trial numbers */
}
<
TitleContext
.
Provider
value
=
{
{
text
:
'
Config
'
,
icon
:
'
4.png
'
,
fontColor
:
''
}
}
>
<
div
className
=
'overviewProgress'
>
<
Title1
/>
<
div
className
=
'duration'
>
</
TitleContext
.
Provider
>
<
TitleContext
.
Provider
value
=
{
{
text
:
'
Duration
'
,
icon
:
'
Timer
'
}
}
>
<
Stack
className
=
'experiment'
>
<
Title
/>
{
/* the scroll bar all the trial profile in the searchSpace div*/
}
<
div
className
=
'experiment searchSpace'
>
<
TrialInfo
experimentUpdateBroadcast
=
{
experimentUpdateBroadcast
}
concurrency
=
{
trialConcurrency
}
/>
</
div
>
</
Stack
>
</
Stack
.
Item
>
</
Stack
>
<
Stack
style
=
{
{
backgroundColor
:
'
#fff
'
}
}
>
<
Stack
horizontal
className
=
'top10bg'
style
=
{
{
position
:
'
relative
'
,
height
:
42
}
}
>
<
div
className
=
'title'
onClick
=
{
this
.
clickMaxTop
}
>
<
TitleContext
.
Provider
value
=
{
{
text
:
'
Top maximal trials
'
,
icon
:
'
max.png
'
,
fontColor
:
titleMaxbgcolor
}
}
>
<
Title1
/>
</
TitleContext
.
Provider
>
</
TitleContext
.
Provider
>
</
div
>
<
ExpDurationContext
.
Provider
<
div
className
=
'title minTitle'
onClick
=
{
this
.
clickMinTop
}
>
value
=
{
{
maxExecDuration
,
execDuration
,
updateOverviewPage
}
}
<
TitleContext
.
Provider
value
=
{
{
text
:
'
Top minimal trials
'
,
icon
:
'
min.png
'
,
fontColor
:
titleMinbgcolor
}
}
>
>
<
Title1
/>
<
ExpDuration
/>
</
TitleContext
.
Provider
>
</
ExpDurationContext
.
Provider
>
</
div
>
<
div
style
=
{
{
position
:
'
absolute
'
,
right
:
'
2%
'
,
top
:
8
}
}
>
<
Dropdown
selectedKey
=
{
bestTrialEntries
}
options
=
{
entriesOption
}
onChange
=
{
this
.
updateEntries
}
styles
=
{
{
root
:
{
width
:
170
}
}
}
/>
</
div
>
</
div
>
</
Stack
>
<
div
className
=
'empty'
/>
<
Stack
horizontal
tokens
=
{
stackTokens
}
>
<
div
className
=
'trialCount'
>
<
div
style
=
{
{
width
:
'
40%
'
,
position
:
'
relative
'
}
}
>
<
TitleContext
.
Provider
value
=
{
{
text
:
'
Trial numbers
'
,
icon
:
'
NumberSymbol
'
}
}
>
<
Accuracy
<
Title
/>
accuracyData
=
{
accuracyGraphData
}
</
TitleContext
.
Provider
>
accNodata
=
{
noDataMessage
}
<
ExpDurationContext
.
Provider
height
=
{
404
}
value
=
{
{
maxExecDuration
,
execDuration
,
updateOverviewPage
}
}
/>
>
</
div
>
<
TrialCount
/>
<
div
style
=
{
{
width
:
'
60%
'
}
}
>
</
ExpDurationContext
.
Provider
>
<
SuccessTable
trialIds
=
{
bestTrials
.
map
(
trial
=>
trial
.
info
.
id
)
}
/>
</
div
>
</
div
>
</
Stack
>
</
div
>
</
Stack
>
{
/* table */
}
<
div
className
=
'overviewTable'
>
<
Stack
horizontal
>
<
div
style
=
{
itemStyleSucceed
}
>
<
TitleContext
.
Provider
value
=
{
{
text
:
'
Top trials
'
,
icon
:
'
BulletedList
'
}
}
>
<
Title
/>
</
TitleContext
.
Provider
>
</
div
>
<
div
className
=
'topTrialTitle'
>
{
/* <Stack horizontal horizontalAlign='space-between'> */
}
<
Stack
horizontal
horizontalAlign
=
'end'
>
<
DefaultButton
onClick
=
{
this
.
clickMaxTop
}
className
=
{
maxActive
}
styles
=
{
{
root
:
{
minWidth
:
70
,
padding
:
0
}
}
}
>
<
Icon
iconName
=
'Market'
/>
<
span
className
=
'max'
>
Max
</
span
>
</
DefaultButton
>
<
div
className
=
'mincenter'
>
<
DefaultButton
onClick
=
{
this
.
clickMinTop
}
className
=
{
minActive
}
styles
=
{
{
root
:
{
minWidth
:
70
,
padding
:
0
}
}
}
>
<
Icon
iconName
=
'MarketDown'
/>
<
span
className
=
'max'
>
Min
</
span
>
</
DefaultButton
>
</
div
>
<
div
>
<
Stack
horizontal
>
<
div
className
=
'chooseEntry'
>
Display top
</
div
>
<
div
>
<
Dropdown
selectedKey
=
{
bestTrialEntries
}
options
=
{
entriesOption
}
onChange
=
{
this
.
updateEntries
}
styles
=
{
{
root
:
{
width
:
70
}
}
}
/>
</
div
>
</
Stack
>
</
div
>
</
Stack
>
</
div
>
</
Stack
>
<
SuccessTable
trialIds
=
{
bestTrials
.
map
(
trial
=>
trial
.
info
.
id
)
}
/>
</
div
>
<
div
className
=
'overviewCommand'
>
<
Command
/>
</
div
>
<
div
className
=
'overviewChart'
>
<
Stack
horizontal
>
<
div
style
=
{
itemStyle1
}
>
<
TitleContext
.
Provider
value
=
{
{
text
:
'
Trial metric chart
'
,
icon
:
'
HomeGroup
'
}
}
>
<
Title
/>
</
TitleContext
.
Provider
>
</
div
>
<
div
style
=
{
itemStyle2
}
>
<
Stack
className
=
'maxmin'
horizontal
>
<
div
className
=
'circle'
/>
<
div
>
{
`Top
${
this
.
context
.
metricGraphMode
}
imal trials`
}
</
div
>
</
Stack
>
</
div
>
</
Stack
>
<
Accuracy
accuracyData
=
{
accuracyGraphData
}
accNodata
=
{
noDataMessage
}
height
=
{
380
}
/>
</
div
>
</
div
>
</
div
>
</
div
>
);
);
}
}
}
}
...
...
src/webui/src/components/TrialsDetail.tsx
View file @
1cd7ad5f
...
@@ -3,7 +3,8 @@ import { Stack, StackItem, Pivot, PivotItem, Dropdown, IDropdownOption, DefaultB
...
@@ -3,7 +3,8 @@ import { Stack, StackItem, Pivot, PivotItem, Dropdown, IDropdownOption, DefaultB
import
{
EXPERIMENT
,
TRIALS
}
from
'
../static/datamodel
'
;
import
{
EXPERIMENT
,
TRIALS
}
from
'
../static/datamodel
'
;
import
{
Trial
}
from
'
../static/model/trial
'
;
import
{
Trial
}
from
'
../static/model/trial
'
;
import
{
AppContext
}
from
'
../App
'
;
import
{
AppContext
}
from
'
../App
'
;
import
{
tableListIcon
}
from
'
./buttons/Icon
'
;
import
{
Title
}
from
'
./overview/Title
'
;
import
{
TitleContext
}
from
'
./overview/TitleContext
'
;
import
DefaultPoint
from
'
./trial-detail/DefaultMetricPoint
'
;
import
DefaultPoint
from
'
./trial-detail/DefaultMetricPoint
'
;
import
Duration
from
'
./trial-detail/Duration
'
;
import
Duration
from
'
./trial-detail/Duration
'
;
import
Para
from
'
./trial-detail/Para
'
;
import
Para
from
'
./trial-detail/Para
'
;
...
@@ -28,6 +29,7 @@ interface TrialDetailState {
...
@@ -28,6 +29,7 @@ interface TrialDetailState {
class
TrialsDetail
extends
React
.
Component
<
{},
TrialDetailState
>
{
class
TrialsDetail
extends
React
.
Component
<
{},
TrialDetailState
>
{
static
contextType
=
AppContext
;
static
contextType
=
AppContext
;
context
!
:
React
.
ContextType
<
typeof
AppContext
>
;
public
interAccuracy
=
0
;
public
interAccuracy
=
0
;
public
interAllTableList
=
2
;
public
interAllTableList
=
2
;
...
@@ -142,10 +144,11 @@ class TrialsDetail extends React.Component<{}, TrialDetailState> {
...
@@ -142,10 +144,11 @@ class TrialsDetail extends React.Component<{}, TrialDetailState> {
</
Pivot
>
</
Pivot
>
</
div
>
</
div
>
{
/* trial table list */
}
{
/* trial table list */
}
<
div
style
=
{
{
backgroundColor
:
'
#fff
'
}
}
>
<
div
className
=
'bulletedList'
style
=
{
{
marginTop
:
18
}
}
>
<
Stack
horizontal
className
=
'panelTitle'
style
=
{
{
marginTop
:
10
}
}
>
<
Stack
className
=
'title'
>
<
span
style
=
{
{
marginRight
:
12
}
}
>
{
tableListIcon
}
</
span
>
<
TitleContext
.
Provider
value
=
{
{
text
:
'
Trial jobs
'
,
icon
:
'
BulletedList
'
}
}
>
<
span
>
Trial jobs
</
span
>
<
Title
/>
</
TitleContext
.
Provider
>
</
Stack
>
</
Stack
>
<
Stack
horizontal
className
=
'allList'
>
<
Stack
horizontal
className
=
'allList'
>
<
StackItem
grow
=
{
50
}
>
<
StackItem
grow
=
{
50
}
>
...
...
src/webui/src/components/buttons/Icon.tsx
View file @
1cd7ad5f
...
@@ -11,11 +11,14 @@ const copy = <Icon iconName='Copy' />;
...
@@ -11,11 +11,14 @@ const copy = <Icon iconName='Copy' />;
const
tableListIcon
=
<
Icon
iconName
=
'BulletedList'
/>;
const
tableListIcon
=
<
Icon
iconName
=
'BulletedList'
/>;
const
downLoadIcon
=
{
iconName
:
'
Download
'
};
const
downLoadIcon
=
{
iconName
:
'
Download
'
};
const
infoIconAbout
=
{
iconName
:
'
info
'
};
const
infoIconAbout
=
{
iconName
:
'
info
'
};
const
timeIcon
=
{
iconName
:
'
Re
fresh
'
};
const
timeIcon
=
{
iconName
:
'
Re
minderTime
'
};
const
disableUpdates
=
{
iconName
:
'
DisableUpdates
'
};
const
disableUpdates
=
{
iconName
:
'
DisableUpdates
'
};
const
requency
=
{
iconName
:
'
Timer
'
};
const
requency
=
{
iconName
:
'
Timer
'
};
const
closeTimer
=
{
iconName
:
'
Blocked2
'
};
const
closeTimer
=
{
iconName
:
'
Blocked2
'
};
const
LineChart
=
<
Icon
iconName
=
'LineChart'
/>;
const
LineChart
=
<
Icon
iconName
=
'LineChart'
/>;
const
Edit
=
<
Icon
iconName
=
'Edit'
/>;
const
CheckMark
=
<
Icon
iconName
=
'CheckMark'
/>;
const
Cancel
=
<
Icon
iconName
=
'Cancel'
/>;
export
{
export
{
infoIcon
,
infoIcon
,
...
@@ -31,5 +34,8 @@ export {
...
@@ -31,5 +34,8 @@ export {
disableUpdates
,
disableUpdates
,
requency
,
requency
,
closeTimer
,
closeTimer
,
LineChart
LineChart
,
Edit
,
CheckMark
,
Cancel
};
};
src/webui/src/components/overview/Accuracy.tsx
View file @
1cd7ad5f
...
@@ -22,7 +22,7 @@ class Accuracy extends React.Component<AccuracyProps, {}> {
...
@@ -22,7 +22,7 @@ class Accuracy extends React.Component<AccuracyProps, {}> {
render
():
React
.
ReactNode
{
render
():
React
.
ReactNode
{
const
{
accNodata
,
accuracyData
,
height
}
=
this
.
props
;
const
{
accNodata
,
accuracyData
,
height
}
=
this
.
props
;
return
(
return
(
<
div
>
<
div
style
=
{
{
position
:
'
relative
'
}
}
>
<
ReactEcharts
<
ReactEcharts
option
=
{
accuracyData
}
option
=
{
accuracyData
}
style
=
{
{
style
=
{
{
...
...
src/webui/src/components/overview/BasicInfo.tsx
deleted
100644 → 0
View file @
0a6c234a
import
React
from
'
react
'
;
import
{
Stack
,
TooltipHost
}
from
'
@fluentui/react
'
;
import
{
EXPERIMENT
}
from
'
../../static/datamodel
'
;
import
{
formatTimestamp
}
from
'
../../static/function
'
;
export
const
BasicInfo
=
():
any
=>
(
<
Stack
horizontal
horizontalAlign
=
'space-between'
className
=
'main'
>
<
Stack
.
Item
grow
=
{
3
}
className
=
'padItem basic'
>
<
p
>
Name
</
p
>
<
div
>
{
EXPERIMENT
.
profile
.
params
.
experimentName
}
</
div
>
</
Stack
.
Item
>
<
Stack
.
Item
grow
=
{
3
}
className
=
'padItem basic'
>
<
p
>
ID
</
p
>
<
div
>
{
EXPERIMENT
.
profile
.
id
}
</
div
>
</
Stack
.
Item
>
<
Stack
.
Item
grow
=
{
3
}
className
=
'padItem basic'
>
<
p
>
Start time
</
p
>
<
div
className
=
'nowrap'
>
{
formatTimestamp
(
EXPERIMENT
.
profile
.
startTime
)
}
</
div
>
</
Stack
.
Item
>
<
Stack
.
Item
grow
=
{
3
}
className
=
'padItem basic'
>
<
p
>
End time
</
p
>
<
div
className
=
'nowrap'
>
{
formatTimestamp
(
EXPERIMENT
.
profile
.
endTime
)
}
</
div
>
</
Stack
.
Item
>
<
Stack
.
Item
className
=
'padItem basic'
>
<
p
>
Log directory
</
p
>
<
div
className
=
'nowrap'
>
<
TooltipHost
// Tooltip message content
content
=
{
EXPERIMENT
.
profile
.
logDir
||
'
unknown
'
}
calloutProps
=
{
{
gapSpace
:
0
}
}
styles
=
{
{
root
:
{
display
:
'
inline-block
'
}
}
}
>
{
/* show logDir */
}
{
EXPERIMENT
.
profile
.
logDir
||
'
unknown
'
}
</
TooltipHost
>
</
div
>
</
Stack
.
Item
>
<
Stack
.
Item
className
=
'padItem basic'
>
<
p
>
Training platform
</
p
>
<
div
className
=
'nowrap'
>
{
EXPERIMENT
.
profile
.
params
.
trainingServicePlatform
}
</
div
>
</
Stack
.
Item
>
</
Stack
>
);
src/webui/src/components/overview/NumInput.tsx
deleted
100644 → 0
View file @
0a6c234a
import
*
as
React
from
'
react
'
;
import
{
Stack
,
PrimaryButton
}
from
'
@fluentui/react
'
;
interface
ConcurrencyInputProps
{
value
:
number
;
updateValue
:
(
val
:
string
)
=>
void
;
}
interface
ConcurrencyInputStates
{
editting
:
boolean
;
}
class
ConcurrencyInput
extends
React
.
Component
<
ConcurrencyInputProps
,
ConcurrencyInputStates
>
{
private
input
=
React
.
createRef
<
HTMLInputElement
>
();
constructor
(
props
:
ConcurrencyInputProps
)
{
super
(
props
);
this
.
state
=
{
editting
:
false
};
}
save
=
():
void
=>
{
if
(
this
.
input
.
current
!==
null
)
{
this
.
props
.
updateValue
(
this
.
input
.
current
.
value
);
this
.
setState
({
editting
:
false
});
}
};
cancel
=
():
void
=>
{
this
.
setState
({
editting
:
false
});
};
edit
=
():
void
=>
{
this
.
setState
({
editting
:
true
});
};
render
():
React
.
ReactNode
{
if
(
this
.
state
.
editting
)
{
return
(
<
Stack
horizontal
className
=
'inputBox'
>
<
input
type
=
'number'
className
=
'concurrencyInput'
defaultValue
=
{
this
.
props
.
value
.
toString
()
}
ref
=
{
this
.
input
}
/>
<
PrimaryButton
text
=
'Save'
onClick
=
{
this
.
save
}
/>
<
PrimaryButton
text
=
'Cancel'
style
=
{
{
display
:
'
inline-block
'
,
marginLeft
:
1
}
}
onClick
=
{
this
.
cancel
}
/>
</
Stack
>
);
}
else
{
return
(
<
Stack
horizontal
className
=
'inputBox'
>
<
input
type
=
'number'
className
=
'concurrencyInput'
disabled
=
{
true
}
value
=
{
this
.
props
.
value
}
/>
<
PrimaryButton
text
=
'Edit'
onClick
=
{
this
.
edit
}
/>
</
Stack
>
);
}
}
}
export
default
ConcurrencyInput
;
src/webui/src/components/overview/Progress.tsx
deleted
100644 → 0
View file @
0a6c234a
import
*
as
React
from
'
react
'
;
import
{
Stack
,
Callout
,
Link
,
IconButton
,
FontWeights
,
mergeStyleSets
,
getId
,
getTheme
,
StackItem
,
TooltipHost
}
from
'
@fluentui/react
'
;
import
axios
from
'
axios
'
;
import
{
MANAGER_IP
,
CONCURRENCYTOOLTIP
}
from
'
../../static/const
'
;
import
{
EXPERIMENT
,
TRIALS
}
from
'
../../static/datamodel
'
;
import
{
convertTime
}
from
'
../../static/function
'
;
import
ConcurrencyInput
from
'
./NumInput
'
;
import
ProgressBar
from
'
./ProgressItem
'
;
import
LogDrawer
from
'
../modals/LogPanel
'
;
import
MessageInfo
from
'
../modals/MessageInfo
'
;
import
{
infoIcon
}
from
'
../buttons/Icon
'
;
import
'
../../static/style/progress.scss
'
;
import
'
../../static/style/probar.scss
'
;
interface
ProgressProps
{
concurrency
:
number
;
bestAccuracy
:
number
;
changeConcurrency
:
(
val
:
number
)
=>
void
;
experimentUpdateBroadcast
:
number
;
}
interface
ProgressState
{
isShowLogDrawer
:
boolean
;
isCalloutVisible
?:
boolean
;
isShowSucceedInfo
:
boolean
;
info
:
string
;
typeInfo
:
string
;
}
const
itemStyles
:
React
.
CSSProperties
=
{
height
:
50
,
width
:
100
};
const
theme
=
getTheme
();
const
styles
=
mergeStyleSets
({
buttonArea
:
{
verticalAlign
:
'
top
'
,
display
:
'
inline-block
'
,
textAlign
:
'
center
'
,
// margin: '0 100px',
minWidth
:
30
,
height
:
30
},
callout
:
{
maxWidth
:
300
},
header
:
{
padding
:
'
18px 24px 12px
'
},
title
:
[
theme
.
fonts
.
xLarge
,
{
margin
:
0
,
color
:
theme
.
palette
.
neutralPrimary
,
fontWeight
:
FontWeights
.
semilight
}
],
inner
:
{
height
:
'
100%
'
,
padding
:
'
0 24px 20px
'
},
actions
:
{
position
:
'
relative
'
,
marginTop
:
20
,
width
:
'
100%
'
,
whiteSpace
:
'
nowrap
'
},
subtext
:
[
theme
.
fonts
.
small
,
{
margin
:
0
,
color
:
theme
.
palette
.
neutralPrimary
,
fontWeight
:
FontWeights
.
semilight
}
],
link
:
[
theme
.
fonts
.
medium
,
{
color
:
theme
.
palette
.
neutralPrimary
}
]
});
class
Progressed
extends
React
.
Component
<
ProgressProps
,
ProgressState
>
{
private
menuButtonElement
!
:
HTMLDivElement
|
null
;
private
labelId
:
string
=
getId
(
'
callout-label
'
);
private
descriptionId
:
string
=
getId
(
'
callout-description
'
);
constructor
(
props
:
ProgressProps
)
{
super
(
props
);
this
.
state
=
{
isShowLogDrawer
:
false
,
isCalloutVisible
:
false
,
isShowSucceedInfo
:
false
,
info
:
''
,
typeInfo
:
'
success
'
};
}
hideSucceedInfo
=
():
void
=>
{
this
.
setState
(()
=>
({
isShowSucceedInfo
:
false
}));
};
/**
* info: message content
* typeInfo: message type: success | error...
* continuousTime: show time, 2000ms
*/
showMessageInfo
=
(
info
:
string
,
typeInfo
:
string
):
void
=>
{
this
.
setState
(()
=>
({
info
,
typeInfo
,
isShowSucceedInfo
:
true
}));
setTimeout
(
this
.
hideSucceedInfo
,
2000
);
};
editTrialConcurrency
=
async
(
userInput
:
string
):
Promise
<
void
>
=>
{
if
(
!
userInput
.
match
(
/^
[
1-9
]\d
*$/
))
{
this
.
showMessageInfo
(
'
Please enter a positive integer!
'
,
'
error
'
);
return
;
}
const
newConcurrency
=
parseInt
(
userInput
,
10
);
if
(
newConcurrency
===
this
.
props
.
concurrency
)
{
this
.
showMessageInfo
(
'
Trial concurrency has not changed
'
,
'
error
'
);
return
;
}
const
newProfile
=
Object
.
assign
({},
EXPERIMENT
.
profile
);
newProfile
.
params
.
trialConcurrency
=
newConcurrency
;
// rest api, modify trial concurrency value
try
{
const
res
=
await
axios
.
put
(
`
${
MANAGER_IP
}
/experiment`
,
newProfile
,
{
// eslint-disable-next-line @typescript-eslint/camelcase
params
:
{
update_type
:
'
TRIAL_CONCURRENCY
'
}
});
if
(
res
.
status
===
200
)
{
this
.
showMessageInfo
(
'
Successfully updated trial concurrency
'
,
'
success
'
);
// NOTE: should we do this earlier in favor of poor networks?
this
.
props
.
changeConcurrency
(
newConcurrency
);
}
}
catch
(
error
)
{
if
(
error
.
response
&&
error
.
response
.
data
.
error
)
{
this
.
showMessageInfo
(
`Failed to update trial concurrency\n
${
error
.
response
.
data
.
error
}
`
,
'
error
'
);
}
else
if
(
error
.
response
)
{
this
.
showMessageInfo
(
`Failed to update trial concurrency\nServer responsed
${
error
.
response
.
status
}
`
,
'
error
'
);
}
else
if
(
error
.
message
)
{
this
.
showMessageInfo
(
`Failed to update trial concurrency\n
${
error
.
message
}
`
,
'
error
'
);
}
else
{
this
.
showMessageInfo
(
`Failed to update trial concurrency\nUnknown error`
,
'
error
'
);
}
}
};
isShowDrawer
=
():
void
=>
{
this
.
setState
({
isShowLogDrawer
:
true
});
};
closeDrawer
=
():
void
=>
{
this
.
setState
({
isShowLogDrawer
:
false
});
};
onDismiss
=
():
void
=>
{
this
.
setState
({
isCalloutVisible
:
false
});
};
onShow
=
():
void
=>
{
this
.
setState
({
isCalloutVisible
:
true
});
};
render
():
React
.
ReactNode
{
const
{
bestAccuracy
}
=
this
.
props
;
const
{
isShowLogDrawer
,
isCalloutVisible
,
isShowSucceedInfo
,
info
,
typeInfo
}
=
this
.
state
;
const
count
=
TRIALS
.
countStatus
();
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const
stoppedCount
=
count
.
get
(
'
USER_CANCELED
'
)
!
+
count
.
get
(
'
SYS_CANCELED
'
)
!
+
count
.
get
(
'
EARLY_STOPPED
'
)
!
;
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const
bar2
=
count
.
get
(
'
RUNNING
'
)
!
+
count
.
get
(
'
SUCCEEDED
'
)
!
+
count
.
get
(
'
FAILED
'
)
!
+
stoppedCount
;
// support type [0, 1], not 98%
const
bar2Percent
=
bar2
/
EXPERIMENT
.
profile
.
params
.
maxTrialNum
;
const
percent
=
EXPERIMENT
.
profile
.
execDuration
/
EXPERIMENT
.
profile
.
params
.
maxExecDuration
;
const
remaining
=
convertTime
(
EXPERIMENT
.
profile
.
params
.
maxExecDuration
-
EXPERIMENT
.
profile
.
execDuration
);
const
maxDuration
=
convertTime
(
EXPERIMENT
.
profile
.
params
.
maxExecDuration
);
const
maxTrialNum
=
EXPERIMENT
.
profile
.
params
.
maxTrialNum
;
const
execDuration
=
convertTime
(
EXPERIMENT
.
profile
.
execDuration
);
return
(
<
Stack
className
=
'progress'
id
=
'barBack'
>
<
Stack
className
=
'basic lineBasic'
>
<
p
>
Status
</
p
>
<
Stack
horizontal
className
=
'status'
>
<
span
className
=
{
`
${
EXPERIMENT
.
status
}
status-text`
}
>
{
EXPERIMENT
.
status
}
</
span
>
{
EXPERIMENT
.
status
===
'
ERROR
'
?
(
<
div
>
<
div
className
=
{
styles
.
buttonArea
}
ref
=
{
(
val
):
any
=>
(
this
.
menuButtonElement
=
val
)
}
>
<
IconButton
iconProps
=
{
{
iconName
:
'
info
'
}
}
onClick
=
{
isCalloutVisible
?
this
.
onDismiss
:
this
.
onShow
}
/>
</
div
>
{
isCalloutVisible
&&
(
<
Callout
className
=
{
styles
.
callout
}
ariaLabelledBy
=
{
this
.
labelId
}
ariaDescribedBy
=
{
this
.
descriptionId
}
role
=
'alertdialog'
gapSpace
=
{
0
}
target
=
{
this
.
menuButtonElement
}
onDismiss
=
{
this
.
onDismiss
}
setInitialFocus
=
{
true
}
>
<
div
className
=
{
styles
.
header
}
>
<
p
className
=
{
styles
.
title
}
id
=
{
this
.
labelId
}
>
Error
</
p
>
</
div
>
<
div
className
=
{
styles
.
inner
}
>
<
p
className
=
{
styles
.
subtext
}
id
=
{
this
.
descriptionId
}
>
{
EXPERIMENT
.
error
}
</
p
>
<
div
className
=
{
styles
.
actions
}
>
<
Link
className
=
{
styles
.
link
}
onClick
=
{
this
.
isShowDrawer
}
>
Learn about
</
Link
>
</
div
>
</
div
>
</
Callout
>
)
}
</
div
>
)
:
null
}
</
Stack
>
</
Stack
>
<
ProgressBar
who
=
'Duration'
percent
=
{
percent
}
description
=
{
execDuration
}
bgclass
=
{
EXPERIMENT
.
status
}
maxString
=
{
`Max duration:
${
maxDuration
}
`
}
/>
<
ProgressBar
who
=
'Trial numbers'
percent
=
{
bar2Percent
}
description
=
{
bar2
.
toString
()
}
bgclass
=
{
EXPERIMENT
.
status
}
maxString
=
{
`Max trial number:
${
maxTrialNum
}
`
}
/>
<
Stack
className
=
'basic colorOfbasic mess'
horizontal
>
<
StackItem
grow
=
{
50
}
>
<
p
>
Best metric
</
p
>
<
div
>
{
isNaN
(
bestAccuracy
)
?
'
N/A
'
:
bestAccuracy
.
toFixed
(
6
)
}
</
div
>
</
StackItem
>
<
StackItem
>
{
isShowSucceedInfo
&&
<
MessageInfo
className
=
'info'
typeInfo
=
{
typeInfo
}
info
=
{
info
}
/>
}
</
StackItem
>
</
Stack
>
<
Stack
horizontal
horizontalAlign
=
'space-between'
className
=
'mess'
>
<
span
style
=
{
itemStyles
}
className
=
'basic colorOfbasic'
>
<
p
>
Spent
</
p
>
<
div
>
{
execDuration
}
</
div
>
</
span
>
<
span
style
=
{
itemStyles
}
className
=
'basic colorOfbasic'
>
<
p
>
Remaining
</
p
>
<
div
className
=
'time'
>
{
remaining
}
</
div
>
</
span
>
<
span
style
=
{
itemStyles
}
>
{
/* modify concurrency */
}
<
TooltipHost
content
=
{
CONCURRENCYTOOLTIP
}
>
<
p
className
=
'cursor'
>
Concurrency
<
span
className
=
'progress-info'
>
{
infoIcon
}
</
span
>
</
p
>
</
TooltipHost
>
<
ConcurrencyInput
value
=
{
this
.
props
.
concurrency
}
updateValue
=
{
this
.
editTrialConcurrency
}
/>
</
span
>
<
span
style
=
{
itemStyles
}
className
=
'basic colorOfbasic'
></
span
>
</
Stack
>
<
Stack
horizontal
horizontalAlign
=
'space-between'
className
=
'mess'
>
<
div
style
=
{
itemStyles
}
className
=
'basic colorOfbasic'
>
<
p
>
Running
</
p
>
<
div
>
{
count
.
get
(
'
RUNNING
'
)
}
</
div
>
</
div
>
<
div
style
=
{
itemStyles
}
className
=
'basic colorOfbasic'
>
<
p
>
Succeeded
</
p
>
<
div
>
{
count
.
get
(
'
SUCCEEDED
'
)
}
</
div
>
</
div
>
<
div
style
=
{
itemStyles
}
className
=
'basic'
>
<
p
>
Stopped
</
p
>
<
div
>
{
stoppedCount
}
</
div
>
</
div
>
<
div
style
=
{
itemStyles
}
className
=
'basic'
>
<
p
>
Failed
</
p
>
<
div
>
{
count
.
get
(
'
FAILED
'
)
}
</
div
>
</
div
>
</
Stack
>
{
/* learn about click -> default active key is dispatcher. */
}
{
isShowLogDrawer
?
<
LogDrawer
closeDrawer
=
{
this
.
closeDrawer
}
activeTab
=
'dispatcher'
/>
:
null
}
</
Stack
>
);
}
}
export
default
Progressed
;
src/webui/src/components/overview/ProgressItem.tsx
deleted
100644 → 0
View file @
0a6c234a
import
*
as
React
from
'
react
'
;
import
{
Stack
,
StackItem
,
ProgressIndicator
}
from
'
@fluentui/react
'
;
interface
ProItemProps
{
who
:
string
;
percent
:
number
;
description
:
string
;
maxString
:
string
;
bgclass
:
string
;
}
class
ProgressBar
extends
React
.
Component
<
ProItemProps
,
{}
>
{
constructor
(
props
:
ProItemProps
)
{
super
(
props
);
}
render
():
React
.
ReactNode
{
const
{
who
,
percent
,
description
,
maxString
,
bgclass
}
=
this
.
props
;
return
(
<
div
>
<
Stack
horizontal
className
=
{
`probar
${
bgclass
}
`
}
>
<
div
className
=
'name'
>
{
who
}
</
div
>
<
div
className
=
'showProgress'
style
=
{
{
width
:
'
78%
'
}
}
>
<
ProgressIndicator
barHeight
=
{
30
}
percentComplete
=
{
percent
}
/>
<
Stack
horizontal
className
=
'boundary'
>
<
StackItem
grow
=
{
30
}
>
0
</
StackItem
>
<
StackItem
className
=
'right'
grow
=
{
70
}
>
{
maxString
}
</
StackItem
>
</
Stack
>
</
div
>
<
div
className
=
'description'
style
=
{
{
width
:
'
22%
'
}
}
>
{
description
}
</
div
>
</
Stack
>
<
br
/>
</
div
>
);
}
}
export
default
ProgressBar
;
src/webui/src/components/overview/SearchSpace.tsx
deleted
100644 → 0
View file @
0a6c234a
import
*
as
React
from
'
react
'
;
import
MonacoEditor
from
'
react-monaco-editor
'
;
import
{
MONACO
}
from
'
../../static/const
'
;
interface
SearchspaceProps
{
searchSpace
:
object
;
}
class
SearchSpace
extends
React
.
Component
<
SearchspaceProps
,
{}
>
{
constructor
(
props
:
SearchspaceProps
)
{
super
(
props
);
}
render
():
React
.
ReactNode
{
const
{
searchSpace
}
=
this
.
props
;
return
(
<
div
className
=
'searchSpace'
>
<
MonacoEditor
height
=
'361'
language
=
'json'
theme
=
'vs-light'
value
=
{
JSON
.
stringify
(
searchSpace
,
null
,
2
)
}
options
=
{
MONACO
}
/>
</
div
>
);
}
}
export
default
SearchSpace
;
src/webui/src/components/overview/Title
1
.tsx
→
src/webui/src/components/overview/Title.tsx
View file @
1cd7ad5f
import
*
as
React
from
'
react
'
;
import
React
from
'
react
'
;
import
{
Stack
}
from
'
@fluentui/react
'
;
import
{
Stack
,
Icon
,
initializeIcons
}
from
'
@fluentui/react
'
;
import
{
TitleContext
}
from
'
../Overview
'
;
import
{
TitleContext
}
from
'
./TitleContext
'
;
import
'
../../static/style/overviewTitle.scss
'
;
import
'
../../static/style/overview/overviewTitle.scss
'
;
initializeIcons
();
export
const
Title
1
=
():
any
=>
(
export
const
Title
=
():
any
=>
(
<
TitleContext
.
Consumer
>
<
TitleContext
.
Consumer
>
{
(
value
):
React
.
ReactNode
=>
(
{
(
value
):
React
.
ReactNode
=>
(
<
Stack
horizontal
className
=
'panelTitle'
>
<
Stack
horizontal
className
=
'panelTitle'
>
<
img
src
=
{
require
(
`../../static/img/icon/
${
value
.
icon
}
`
)
}
alt
=
'icon'
/>
<
Icon
iconName
=
{
value
.
icon
}
/>
<
span
style
=
{
{
color
:
value
.
fontColor
}
}
>
{
value
.
text
}
</
span
>
<
span
>
{
value
.
text
}
</
span
>
</
Stack
>
</
Stack
>
)
}
)
}
</
TitleContext
.
Consumer
>
</
TitleContext
.
Consumer
>
...
...
src/webui/src/components/overview/TitleContext.tsx
0 → 100644
View file @
1cd7ad5f
import
*
as
React
from
'
react
'
;
export
const
TitleContext
=
React
.
createContext
({
text
:
''
,
icon
:
''
});
src/webui/src/components/overview/TrialProfile.tsx
deleted
100644 → 0
View file @
0a6c234a
import
*
as
React
from
'
react
'
;
import
MonacoEditor
from
'
react-monaco-editor
'
;
import
{
MONACO
}
from
'
../../static/const
'
;
import
{
EXPERIMENT
}
from
'
../../static/datamodel
'
;
interface
TrialInfoProps
{
experimentUpdateBroadcast
:
number
;
concurrency
:
number
;
}
class
TrialInfo
extends
React
.
Component
<
TrialInfoProps
,
{}
>
{
constructor
(
props
:
TrialInfoProps
)
{
super
(
props
);
}
render
():
React
.
ReactNode
{
const
blacklist
=
[
'
id
'
,
'
logDir
'
,
'
startTime
'
,
'
endTime
'
,
'
experimentName
'
,
'
searchSpace
'
,
'
trainingServicePlatform
'
];
const
filter
=
(
key
:
string
,
val
:
any
):
any
=>
{
if
(
key
===
'
trialConcurrency
'
)
{
return
this
.
props
.
concurrency
;
}
return
blacklist
.
includes
(
key
)
?
undefined
:
val
;
};
const
profile
=
JSON
.
stringify
(
EXPERIMENT
.
profile
,
filter
,
2
);
return
(
<
div
className
=
'profile'
>
<
MonacoEditor
width
=
'100%'
height
=
'361'
language
=
'json'
theme
=
'vs-light'
value
=
{
profile
}
options
=
{
MONACO
}
/>
</
div
>
);
}
}
export
default
TrialInfo
;
src/webui/src/components/overview/count/EditExperimentParam.tsx
0 → 100644
View file @
1cd7ad5f
import
React
,
{
useState
,
useCallback
,
useContext
}
from
'
react
'
;
import
axios
from
'
axios
'
;
import
{
EXPERIMENT
}
from
'
../../../static/datamodel
'
;
import
{
EditExpeParamContext
}
from
'
./context
'
;
import
{
MANAGER_IP
}
from
'
../../../static/const
'
;
import
{
convertTimeToSecond
}
from
'
../../../static/function
'
;
import
{
Edit
,
CheckMark
,
Cancel
}
from
'
../../buttons/Icon
'
;
import
MessageInfo
from
'
../../modals/MessageInfo
'
;
import
'
../../../static/style/overview/count.scss
'
;
const
DurationInputRef
=
React
.
createRef
<
HTMLInputElement
>
();
export
const
EditExperimentParam
=
():
any
=>
{
const
[
isShowPencil
,
setShowPencil
]
=
useState
(
true
);
const
[
isShowSucceedInfo
,
setShowSucceedInfo
]
=
useState
(
false
);
const
[
typeInfo
,
setTypeInfo
]
=
useState
(
''
);
const
[
info
,
setInfo
]
=
useState
(
''
);
const
showPencil
=
useCallback
(()
=>
{
setShowPencil
(
true
);
},
[]);
const
hidePencil
=
useCallback
(()
=>
{
setShowPencil
(
false
);
},
[]);
const
showSucceedInfo
=
useCallback
(()
=>
setShowSucceedInfo
(
true
),
[]);
const
hideSucceedInfo
=
useCallback
(()
=>
{
setShowSucceedInfo
(
false
);
},
[]);
const
{
title
,
field
,
editType
,
maxExecDuration
,
maxTrialNum
,
trialConcurrency
,
updateOverviewPage
}
=
useContext
(
EditExpeParamContext
);
let
defaultVal
=
''
;
let
editVal
=
''
;
if
(
title
===
'
Max duration
'
)
{
defaultVal
=
maxExecDuration
;
editVal
=
maxExecDuration
;
}
else
if
(
title
===
'
Max trial numbers
'
)
{
defaultVal
=
maxTrialNum
.
toString
();
editVal
=
maxTrialNum
.
toString
();
}
else
{
defaultVal
=
trialConcurrency
.
toString
();
editVal
=
trialConcurrency
.
toString
();
}
const
[
editInputVal
,
setEditValInput
]
=
useState
(
editVal
);
function
setInputVal
(
event
:
any
):
void
{
setEditValInput
(
event
.
target
.
value
);
}
function
cancelEdit
():
void
{
setEditValInput
(
defaultVal
);
showPencil
();
}
async
function
confirmEdit
():
Promise
<
void
>
{
const
isMaxDuration
=
title
===
'
Max duration
'
;
const
newProfile
=
Object
.
assign
({},
EXPERIMENT
.
profile
);
let
beforeParam
=
''
;
if
(
!
isMaxDuration
&&
!
editInputVal
.
match
(
/^
[
1-9
]\d
*$/
))
{
showMessageInfo
(
'
Please enter a positive integer!
'
,
'
error
'
);
return
;
}
if
(
isMaxDuration
)
{
beforeParam
=
maxExecDuration
;
}
else
if
(
title
===
'
Max trial numbers
'
)
{
beforeParam
=
maxTrialNum
.
toString
();
}
else
{
beforeParam
=
trialConcurrency
.
toString
();
}
if
(
editInputVal
===
beforeParam
)
{
showMessageInfo
(
`Trial
${
field
}
has not changed`
,
'
error
'
);
return
;
}
if
(
isMaxDuration
)
{
newProfile
.
params
[
field
]
=
convertTimeToSecond
(
editInputVal
);
}
else
{
newProfile
.
params
[
field
]
=
parseInt
(
editInputVal
,
10
);
}
// rest api, modify trial concurrency value
try
{
const
res
=
await
axios
.
put
(
`
${
MANAGER_IP
}
/experiment`
,
newProfile
,
{
// eslint-disable-next-line @typescript-eslint/camelcase
params
:
{
update_type
:
editType
}
});
if
(
res
.
status
===
200
)
{
showMessageInfo
(
`Successfully updated
${
field
}
`
,
'
success
'
);
}
}
catch
(
error
)
{
if
(
error
.
response
&&
error
.
response
.
data
.
error
)
{
showMessageInfo
(
`Failed to update trial
${
field
}
\n
${
error
.
response
.
data
.
error
}
`
,
'
error
'
);
}
else
if
(
error
.
response
)
{
showMessageInfo
(
`Failed to update trial
${
field
}
\nServer responsed
${
error
.
response
.
status
}
`
,
'
error
'
);
}
else
if
(
error
.
message
)
{
showMessageInfo
(
`Failed to update trial
${
field
}
\n
${
error
.
message
}
`
,
'
error
'
);
}
else
{
showMessageInfo
(
`Failed to update trial
${
field
}
\nUnknown error`
,
'
error
'
);
}
}
showPencil
();
updateOverviewPage
();
}
function
showMessageInfo
(
info
:
string
,
typeInfo
:
string
):
any
{
setInfo
(
info
);
setTypeInfo
(
typeInfo
);
showSucceedInfo
();
setTimeout
(
hideSucceedInfo
,
2000
);
}
return
(
<
EditExpeParamContext
.
Consumer
>
{
(
value
):
React
.
ReactNode
=>
{
return
(
<
React
.
Fragment
>
<
p
>
{
value
.
title
}
</
p
>
<
div
>
<
input
className
=
{
`
${
value
.
field
}
durationInput`
}
ref
=
{
DurationInputRef
}
disabled
=
{
isShowPencil
?
true
:
false
}
value
=
{
editInputVal
}
onChange
=
{
setInputVal
}
/>
{
isShowPencil
&&
(
<
span
className
=
'edit'
onClick
=
{
hidePencil
}
>
{
Edit
}
</
span
>
)
}
{
!
isShowPencil
&&
(
<
span
className
=
'series'
>
<
span
className
=
'confirm'
onClick
=
{
confirmEdit
}
>
{
CheckMark
}
</
span
>
<
span
className
=
'cancel'
onClick
=
{
cancelEdit
}
>
{
Cancel
}
</
span
>
</
span
>
)
}
{
isShowSucceedInfo
&&
<
MessageInfo
className
=
'info'
typeInfo
=
{
typeInfo
}
info
=
{
info
}
/>
}
</
div
>
</
React
.
Fragment
>
);
}
}
</
EditExpeParamContext
.
Consumer
>
);
};
src/webui/src/components/overview/count/ExpDuration.tsx
0 → 100644
View file @
1cd7ad5f
import
React
from
'
react
'
;
import
{
Stack
,
TooltipHost
,
ProgressIndicator
}
from
'
@fluentui/react
'
;
import
{
EXPERIMENT
}
from
'
../../../static/datamodel
'
;
import
{
CONTROLTYPE
}
from
'
../../../static/const
'
;
import
{
convertDuration
}
from
'
../../../static/function
'
;
import
{
EditExperimentParam
}
from
'
./EditExperimentParam
'
;
import
{
ExpDurationContext
}
from
'
./ExpDurationContext
'
;
import
{
EditExpeParamContext
}
from
'
./context
'
;
import
'
../../../static/style/overview/count.scss
'
;
const
itemStyle1
:
React
.
CSSProperties
=
{
width
:
'
62%
'
,
height
:
80
};
const
itemStyle2
:
React
.
CSSProperties
=
{
width
:
'
63%
'
,
height
:
80
,
textAlign
:
'
right
'
};
export
const
ExpDuration
=
():
any
=>
(
<
ExpDurationContext
.
Consumer
>
{
(
value
):
React
.
ReactNode
=>
{
const
{
maxExecDuration
,
execDuration
,
updateOverviewPage
}
=
value
;
const
tooltip
=
maxExecDuration
-
execDuration
;
const
maxExecDurationStr
=
convertDuration
(
maxExecDuration
);
const
percent
=
execDuration
/
maxExecDuration
;
return
(
<
Stack
horizontal
className
=
'ExpDuration'
>
<
div
style
=
{
itemStyle1
}
>
<
TooltipHost
content
=
{
`
${
convertDuration
(
tooltip
)}
remaining`
}
>
<
ProgressIndicator
percentComplete
=
{
percent
}
barHeight
=
{
15
}
/>
</
TooltipHost
>
</
div
>
<
div
style
=
{
itemStyle2
}
>
<
Stack
horizontal
></
Stack
>
<
EditExpeParamContext
.
Provider
value
=
{
{
editType
:
CONTROLTYPE
[
0
],
field
:
'
maxExecDuration
'
,
title
:
'
Max duration
'
,
maxExecDuration
:
maxExecDurationStr
,
maxTrialNum
:
EXPERIMENT
.
profile
.
params
.
maxTrialNum
,
trialConcurrency
:
EXPERIMENT
.
profile
.
params
.
trialConcurrency
,
updateOverviewPage
}
}
>
<
EditExperimentParam
/>
</
EditExpeParamContext
.
Provider
>
</
div
>
</
Stack
>
);
}
}
</
ExpDurationContext
.
Consumer
>
);
src/webui/src/components/overview/count/ExpDurationContext.tsx
0 → 100644
View file @
1cd7ad5f
import
React
from
'
react
'
;
export
const
ExpDurationContext
=
React
.
createContext
({
maxExecDuration
:
0
,
execDuration
:
0
,
// eslint-disable-next-line @typescript-eslint/no-empty-function
updateOverviewPage
:
():
void
=>
{}
});
src/webui/src/components/overview/count/TrialCount.tsx
0 → 100644
View file @
1cd7ad5f
import
*
as
React
from
'
react
'
;
import
{
Stack
,
TooltipHost
,
ProgressIndicator
}
from
'
@fluentui/react
'
;
import
{
EXPERIMENT
,
TRIALS
}
from
'
../../../static/datamodel
'
;
import
{
CONTROLTYPE
}
from
'
../../../static/const
'
;
import
{
EditExperimentParam
}
from
'
./EditExperimentParam
'
;
import
{
EditExpeParamContext
}
from
'
./context
'
;
import
{
ExpDurationContext
}
from
'
./ExpDurationContext
'
;
const
itemStyles
:
React
.
CSSProperties
=
{
width
:
'
62%
'
};
const
itemStyle2
:
React
.
CSSProperties
=
{
width
:
'
63%
'
,
textAlign
:
'
right
'
};
const
itemStyle1
:
React
.
CSSProperties
=
{
width
:
'
30%
'
,
height
:
50
};
const
itemRunning
:
React
.
CSSProperties
=
{
width
:
'
42%
'
,
height
:
56
};
export
const
TrialCount
=
():
any
=>
{
const
count
=
TRIALS
.
countStatus
();
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const
stoppedCount
=
count
.
get
(
'
USER_CANCELED
'
)
!
+
count
.
get
(
'
SYS_CANCELED
'
)
!
+
count
.
get
(
'
EARLY_STOPPED
'
)
!
;
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const
bar2
=
count
.
get
(
'
RUNNING
'
)
!
+
count
.
get
(
'
SUCCEEDED
'
)
!
+
count
.
get
(
'
FAILED
'
)
!
+
stoppedCount
;
// support type [0, 1], not 98%
const
bar2Percent
=
bar2
/
EXPERIMENT
.
profile
.
params
.
maxTrialNum
;
return
(
<
ExpDurationContext
.
Consumer
>
{
(
value
):
React
.
ReactNode
=>
{
const
{
updateOverviewPage
}
=
value
;
return
(
<
React
.
Fragment
>
<
Stack
horizontal
horizontalAlign
=
'space-between'
className
=
'ExpDuration'
>
<
div
style
=
{
itemStyles
}
>
<
TooltipHost
content
=
{
bar2
.
toString
()
}
>
<
ProgressIndicator
percentComplete
=
{
bar2Percent
}
barHeight
=
{
15
}
/>
</
TooltipHost
>
<
Stack
horizontal
className
=
'mess'
>
<
div
style
=
{
itemRunning
}
className
=
'basic'
>
<
p
>
Running
</
p
>
<
div
>
{
count
.
get
(
'
RUNNING
'
)
}
</
div
>
</
div
>
<
div
style
=
{
itemStyle1
}
className
=
'basic'
>
<
p
>
Failed
</
p
>
<
div
>
{
count
.
get
(
'
FAILED
'
)
}
</
div
>
</
div
>
<
div
style
=
{
itemStyle1
}
className
=
'basic'
>
<
p
>
Stopped
</
p
>
<
div
>
{
stoppedCount
}
</
div
>
</
div
>
</
Stack
>
<
Stack
horizontal
horizontalAlign
=
'space-between'
className
=
'mess'
>
<
div
style
=
{
itemStyle1
}
className
=
'basic'
>
<
p
>
Succeeded
</
p
>
<
div
>
{
count
.
get
(
'
SUCCEEDED
'
)
}
</
div
>
</
div
>
<
div
style
=
{
itemStyle1
}
className
=
'basic'
>
<
p
>
Waiting
</
p
>
<
div
>
{
count
.
get
(
'
WAITING
'
)
}
</
div
>
</
div
>
</
Stack
>
</
div
>
<
div
style
=
{
itemStyle2
}
>
<
EditExpeParamContext
.
Provider
value
=
{
{
title
:
'
Max trial numbers
'
,
field
:
'
maxTrialNum
'
,
editType
:
CONTROLTYPE
[
1
],
maxExecDuration
:
''
,
maxTrialNum
:
EXPERIMENT
.
profile
.
params
.
maxTrialNum
,
trialConcurrency
:
EXPERIMENT
.
profile
.
params
.
trialConcurrency
,
updateOverviewPage
}
}
>
<
EditExperimentParam
/>
</
EditExpeParamContext
.
Provider
>
<
EditExpeParamContext
.
Provider
value
=
{
{
title
:
'
Concurrency
'
,
field
:
'
trialConcurrency
'
,
editType
:
CONTROLTYPE
[
2
],
// maxExecDuration: EXPERIMENT.profile.params.maxExecDuration,
maxExecDuration
:
''
,
maxTrialNum
:
EXPERIMENT
.
profile
.
params
.
maxTrialNum
,
trialConcurrency
:
EXPERIMENT
.
profile
.
params
.
trialConcurrency
,
updateOverviewPage
}
}
>
<
EditExperimentParam
/>
</
EditExpeParamContext
.
Provider
>
</
div
>
</
Stack
>
</
React
.
Fragment
>
);
}
}
</
ExpDurationContext
.
Consumer
>
);
};
src/webui/src/components/overview/count/context.tsx
0 → 100644
View file @
1cd7ad5f
import
React
from
'
react
'
;
/***
* const CONTROLTYPE = ['MAX_EXEC_DURATION', 'MAX_TRIAL_NUM', 'TRIAL_CONCURRENCY', 'SEARCH_SPACE'];
* [0], 'MAX_EXEC_DURATION', params.maxExecDuration
* [1], 'MAX_TRIAL_NUM', params.maxTrialNum
* [2], 'TRIAL_CONCURRENCY', params.trialConcurrency
*/
export
const
EditExpeParamContext
=
React
.
createContext
({
editType
:
''
,
field
:
''
,
title
:
''
,
maxExecDuration
:
''
,
maxTrialNum
:
0
,
trialConcurrency
:
0
,
// eslint-disable-next-line @typescript-eslint/no-empty-function
updateOverviewPage
:
():
void
=>
{}
});
src/webui/src/components/overview/experiment/BasicInfo.tsx
0 → 100644
View file @
1cd7ad5f
import
React
,
{
useState
,
useCallback
}
from
'
react
'
;
import
{
Stack
,
Callout
,
Link
,
IconButton
}
from
'
@fluentui/react
'
;
import
LogDrawer
from
'
../../modals/LogPanel
'
;
import
{
EXPERIMENT
}
from
'
../../../static/datamodel
'
;
import
{
formatTimestamp
}
from
'
../../../static/function
'
;
import
{
useId
}
from
'
@uifabric/react-hooks
'
;
import
{
BestMetricContext
}
from
'
../../Overview
'
;
import
{
styles
}
from
'
./basicInfoStyles
'
;
import
'
../../../static/style/progress/progress.scss
'
;
import
'
../../../static/style/progress/probar.scss
'
;
export
const
ReBasicInfo
=
():
any
=>
{
const
labelId
:
string
=
useId
(
'
callout-label
'
);
const
descriptionId
:
string
=
useId
(
'
callout-description
'
);
const
ref
=
React
.
createRef
<
HTMLDivElement
>
();
const
[
isCalloutVisible
,
setCalloutVisible
]
=
useState
(
false
);
const
[
isShowLogDrawer
,
setShowLogDrawer
]
=
useState
(
false
);
const
onDismiss
=
useCallback
(()
=>
setCalloutVisible
(
false
),
[]);
const
showCallout
=
useCallback
(()
=>
setCalloutVisible
(
true
),
[]);
const
closeLogDrawer
=
useCallback
(()
=>
setShowLogDrawer
(
false
),
[]);
const
ShowLogDrawer
=
useCallback
(()
=>
setShowLogDrawer
(
true
),
[]);
return
(
<
div
>
<
div
className
=
'basic'
>
<
p
>
ID:
{
EXPERIMENT
.
profile
.
id
}
</
p
>
<
div
>
{
EXPERIMENT
.
profile
.
params
.
experimentName
}
</
div
>
</
div
>
<
div
className
=
'basic'
>
<
Stack
className
=
'basic'
>
<
p
>
Status
</
p
>
<
Stack
horizontal
className
=
'status'
>
<
span
className
=
{
`
${
EXPERIMENT
.
status
}
status-text`
}
>
{
EXPERIMENT
.
status
}
</
span
>
{
EXPERIMENT
.
status
===
'
ERROR
'
?
(
<
div
>
<
div
className
=
{
styles
.
buttonArea
}
ref
=
{
ref
}
>
<
IconButton
iconProps
=
{
{
iconName
:
'
info
'
}
}
onClick
=
{
isCalloutVisible
?
onDismiss
:
showCallout
}
/>
</
div
>
{
isCalloutVisible
&&
(
<
Callout
className
=
{
styles
.
callout
}
ariaLabelledBy
=
{
labelId
}
ariaDescribedBy
=
{
descriptionId
}
role
=
'alertdialog'
gapSpace
=
{
0
}
target
=
{
ref
}
onDismiss
=
{
onDismiss
}
setInitialFocus
=
{
true
}
>
<
div
className
=
{
styles
.
header
}
>
<
p
className
=
{
styles
.
title
}
id
=
{
labelId
}
>
Error
</
p
>
</
div
>
<
div
className
=
{
styles
.
inner
}
>
<
p
className
=
{
styles
.
subtext
}
id
=
{
descriptionId
}
>
{
EXPERIMENT
.
error
}
</
p
>
<
div
className
=
{
styles
.
actions
}
>
<
Link
className
=
{
styles
.
link
}
onClick
=
{
ShowLogDrawer
}
>
Learn about
</
Link
>
</
div
>
</
div
>
</
Callout
>
)
}
</
div
>
)
:
null
}
</
Stack
>
</
Stack
>
</
div
>
<
div
className
=
'basic'
>
<
BestMetricContext
.
Consumer
>
{
(
value
):
React
.
ReactNode
=>
(
<
Stack
>
<
p
>
Best metric
</
p
>
<
div
>
{
isNaN
(
value
.
bestAccuracy
)
?
'
N/A
'
:
value
.
bestAccuracy
.
toFixed
(
6
)
}
</
div
>
</
Stack
>
)
}
</
BestMetricContext
.
Consumer
>
</
div
>
<
div
className
=
'basic'
>
<
p
>
Start time
</
p
>
<
div
className
=
'nowrap'
>
{
formatTimestamp
(
EXPERIMENT
.
profile
.
startTime
)
}
</
div
>
</
div
>
<
div
className
=
'basic'
>
<
p
>
End time
</
p
>
<
div
className
=
'nowrap'
>
{
formatTimestamp
(
EXPERIMENT
.
profile
.
endTime
)
}
</
div
>
</
div
>
{
/* learn about click -> default active key is dispatcher. */
}
{
isShowLogDrawer
?
<
LogDrawer
closeDrawer
=
{
closeLogDrawer
}
activeTab
=
'dispatcher'
/>
:
null
}
</
div
>
);
};
Prev
1
2
3
4
Next
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment