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
aa316742
Unverified
Commit
aa316742
authored
Feb 21, 2020
by
SparkSnail
Committed by
GitHub
Feb 21, 2020
Browse files
Merge pull request #233 from microsoft/master
merge master
parents
3fe117f0
24fa4619
Changes
285
Hide whitespace changes
Inline
Side-by-side
Showing
20 changed files
with
971 additions
and
804 deletions
+971
-804
src/webui/src/components/trial-detail/Para.tsx
src/webui/src/components/trial-detail/Para.tsx
+90
-103
src/webui/src/components/trial-detail/TableList.tsx
src/webui/src/components/trial-detail/TableList.tsx
+405
-358
src/webui/src/index.css
src/webui/src/index.css
+18
-3
src/webui/src/index.tsx
src/webui/src/index.tsx
+20
-15
src/webui/src/logo.svg
src/webui/src/logo.svg
+7
-0
src/webui/src/react-app-env.d.ts
src/webui/src/react-app-env.d.ts
+60
-0
src/webui/src/serviceWorker.ts
src/webui/src/serviceWorker.ts
+143
-0
src/webui/src/static/const.ts
src/webui/src/static/const.ts
+6
-42
src/webui/src/static/function.ts
src/webui/src/static/function.ts
+32
-22
src/webui/src/static/interface.ts
src/webui/src/static/interface.ts
+8
-7
src/webui/src/static/model/experiment.ts
src/webui/src/static/model/experiment.ts
+1
-0
src/webui/src/static/model/trial.ts
src/webui/src/static/model/trial.ts
+8
-3
src/webui/src/static/style/accuracy.css
src/webui/src/static/style/accuracy.css
+0
-7
src/webui/src/static/style/compare.scss
src/webui/src/static/style/compare.scss
+39
-31
src/webui/src/static/style/control.scss
src/webui/src/static/style/control.scss
+0
-65
src/webui/src/static/style/icon.scss
src/webui/src/static/style/icon.scss
+14
-0
src/webui/src/static/style/logDrawer.scss
src/webui/src/static/style/logDrawer.scss
+19
-103
src/webui/src/static/style/nav/nav.scss
src/webui/src/static/style/nav/nav.scss
+70
-0
src/webui/src/static/style/openRow.scss
src/webui/src/static/style/openRow.scss
+17
-36
src/webui/src/static/style/overview.scss
src/webui/src/static/style/overview.scss
+14
-9
No files found.
src/webui/src/components/trial-detail/Para.tsx
View file @
aa316742
import
*
as
React
from
'
react
'
;
import
ReactEcharts
from
'
echarts-for-react
'
;
import
{
filterByStatus
}
from
'
../../static/function
'
;
import
{
Row
,
Col
,
Select
,
Button
,
message
}
from
'
antd
'
;
import
{
ParaObj
,
Dimobj
,
TableObj
}
from
'
../../static/interface
'
;
const
Option
=
Select
.
Option
;
require
(
'
echarts/lib/chart/parallel
'
);
require
(
'
echarts/lib/component/tooltip
'
);
require
(
'
echarts/lib/component/title
'
);
require
(
'
echarts/lib/component/visualMap
'
);
require
(
'
../../static/style/para.scss
'
);
require
(
'
../../static/style/button.scss
'
);
import
{
Stack
,
PrimaryButton
,
Dropdown
,
IDropdownOption
,
}
from
'
office-ui-fabric-react
'
;
// eslint-disable-line no-unused-vars
import
{
ParaObj
,
Dimobj
,
TableObj
}
from
'
../../static/interface
'
;
// eslint-disable-line no-unused-vars
import
'
echarts/lib/chart/parallel
'
;
import
'
echarts/lib/component/tooltip
'
;
import
'
echarts/lib/component/title
'
;
import
'
echarts/lib/component/visualMap
'
;
import
'
../../static/style/para.scss
'
;
import
'
../../static/style/button.scss
'
;
interface
ParaState
{
// paraSource: Array<TableObj>;
option
:
object
;
paraBack
:
ParaObj
;
dimName
:
Array
<
string
>
;
swapAxisArr
:
Array
<
string
>
;
dimName
:
string
[]
;
swapAxisArr
:
string
[]
;
percent
:
number
;
paraNodata
:
string
;
max
:
number
;
// graph color bar limit
...
...
@@ -25,6 +24,9 @@ interface ParaState {
succeedRenderCount
:
number
;
// all succeed trials number
clickCounts
:
number
;
isLoadConfirm
:
boolean
;
// office-fabric-ui
selectedItem
?:
{
key
:
string
|
number
|
undefined
};
// percent Selector
swapyAxis
?:
string
[];
// yAxis Selector
}
interface
ParaProps
{
...
...
@@ -33,11 +35,6 @@ interface ParaProps {
whichGraph
:
string
;
}
message
.
config
({
top
:
250
,
duration
:
2
,
});
class
Para
extends
React
.
Component
<
ParaProps
,
ParaState
>
{
private
chartMulineStyle
=
{
...
...
@@ -69,14 +66,15 @@ class Para extends React.Component<ParaProps, ParaState> {
sutrialCount
:
10000000
,
succeedRenderCount
:
10000000
,
clickCounts
:
1
,
isLoadConfirm
:
false
isLoadConfirm
:
false
,
swapyAxis
:
[]
};
}
getParallelAxis
=
(
dimName
:
Array
<
string
>
,
parallelAxis
:
Array
<
Dimobj
>
,
accPara
:
number
[],
eachTrialParams
:
Array
<
string
>
,
dimName
:
string
[]
,
parallelAxis
:
Array
<
Dimobj
>
,
accPara
:
number
[],
eachTrialParams
:
string
[]
,
lengthofTrials
:
number
):
void
=>
{
// get data for every lines. if dim is choice type, number -> toString()
...
...
@@ -128,7 +126,7 @@ class Para extends React.Component<ParaProps, ParaState> {
const
lenOfDataSource
:
number
=
dataSource
.
length
;
const
accPara
:
number
[]
=
[];
// specific value array
const
eachTrialParams
:
Array
<
string
>
=
[];
const
eachTrialParams
:
string
[]
=
[];
// experiment interface search space obj
const
searchRange
=
searchSpace
!==
undefined
?
JSON
.
parse
(
searchSpace
)
:
''
;
// nest search space
...
...
@@ -147,8 +145,8 @@ class Para extends React.Component<ParaProps, ParaState> {
let
i
=
0
;
if
(
isNested
===
false
)
{
for
(
i
;
i
<
dimName
.
length
;
i
++
)
{
const
data
:
string
[]
=
[];
const
searchKey
=
searchRange
[
dimName
[
i
]];
const
data
:
Array
<
string
>
=
[];
switch
(
searchKey
.
_type
)
{
case
'
uniform
'
:
case
'
quniform
'
:
...
...
@@ -220,10 +218,11 @@ class Para extends React.Component<ParaProps, ParaState> {
}
else
{
for
(
i
;
i
<
dimName
.
length
;
i
++
)
{
const
searchKey
=
searchRange
[
dimName
[
i
]];
const
data
:
Array
<
string
>
=
[];
const
data
:
string
[]
=
[];
let
j
=
0
;
switch
(
searchKey
.
_type
)
{
case
'
choice
'
:
for
(
let
j
=
0
;
j
<
searchKey
.
_value
.
length
;
j
++
)
{
for
(
j
;
j
<
searchKey
.
_value
.
length
;
j
++
)
{
const
item
=
searchKey
.
_value
[
j
];
Object
.
keys
(
item
).
map
(
key
=>
{
if
(
key
!==
'
_name
'
&&
key
!==
'
_type
'
)
{
...
...
@@ -362,10 +361,15 @@ class Para extends React.Component<ParaProps, ParaState> {
}
// get percent value number
percentNum
=
(
value
:
string
):
void
=>
{
const
vals
=
parseFloat
(
value
);
this
.
setState
({
percent
:
vals
},
()
=>
{
this
.
reInit
();
});
// percentNum = (value: string) => {
percentNum
=
(
event
:
React
.
FormEvent
<
HTMLDivElement
>
,
item
?:
IDropdownOption
):
void
=>
{
// percentNum = (event: React.FormEvent<HTMLDivElement>, item?: ISelectableOption) => {
if
(
item
!==
undefined
)
{
const
vals
=
parseFloat
(
item
!==
undefined
?
item
.
text
:
''
);
this
.
setState
({
percent
:
vals
/
100
,
selectedItem
:
item
},
()
=>
{
this
.
reInit
();
});
}
}
// deal with response data into pic data
...
...
@@ -439,8 +443,24 @@ class Para extends React.Component<ParaProps, ParaState> {
}
// get swap parallel axis
getSwapArr
=
(
value
:
Array
<
string
>
):
void
=>
{
this
.
setState
({
swapAxisArr
:
value
});
getSwapArr
=
(
event
:
React
.
FormEvent
<
HTMLDivElement
>
,
item
?:
IDropdownOption
):
void
=>
{
const
newSelectedItems
=
[...
this
.
state
.
swapyAxis
];
if
(
item
!==
undefined
)
{
if
(
item
.
selected
)
{
// add the option if it's checked
newSelectedItems
.
push
(
item
.
key
as
string
);
}
else
{
// remove the option if it's unchecked
const
currIndex
=
newSelectedItems
.
indexOf
(
item
.
key
as
string
);
if
(
currIndex
>
-
1
)
{
newSelectedItems
.
splice
(
currIndex
,
1
);
}
}
this
.
setState
({
swapAxisArr
:
newSelectedItems
,
swapyAxis
:
newSelectedItems
});
}
}
reInit
=
():
void
=>
{
...
...
@@ -571,80 +591,47 @@ class Para extends React.Component<ParaProps, ParaState> {
}
}
shouldComponentUpdate
(
nextProps
:
ParaProps
,
nextState
:
ParaState
):
boolean
{
const
{
whichGraph
}
=
nextProps
;
const
beforeGraph
=
this
.
props
.
whichGraph
;
if
(
whichGraph
===
'
2
'
)
{
if
(
whichGraph
!==
beforeGraph
)
{
return
true
;
}
const
{
sutrialCount
,
clickCounts
,
succeedRenderCount
}
=
nextState
;
const
beforeCount
=
this
.
state
.
sutrialCount
;
const
beforeClickCount
=
this
.
state
.
clickCounts
;
const
beforeRealRenderCount
=
this
.
state
.
succeedRenderCount
;
if
(
sutrialCount
!==
beforeCount
)
{
return
true
;
}
if
(
succeedRenderCount
!==
beforeRealRenderCount
)
{
return
true
;
}
if
(
clickCounts
!==
beforeClickCount
)
{
return
true
;
}
}
return
false
;
}
render
():
React
.
ReactNode
{
const
{
option
,
paraNodata
,
dimName
,
isLoadConfirm
}
=
this
.
state
;
const
{
option
,
paraNodata
,
dimName
,
isLoadConfirm
,
selectedItem
,
swapyAxis
}
=
this
.
state
;
return
(
<
Row
className
=
"parameter"
>
<
Row
>
<
Col
span
=
{
6
}
/>
<
Col
span
=
{
18
}
>
<
Row
className
=
"meline"
>
<
span
>
Top
</
span
>
<
Select
style
=
{
{
width
:
'
20%
'
,
marginRight
:
10
}
}
placeholder
=
"100%"
optionFilterProp
=
"children"
onSelect
=
{
this
.
percentNum
}
>
<
Option
value
=
"0.2"
>
20%
</
Option
>
<
Option
value
=
"0.5"
>
50%
</
Option
>
<
Option
value
=
"0.8"
>
80%
</
Option
>
<
Option
value
=
"1"
>
100%
</
Option
>
</
Select
>
<
Select
style
=
{
{
width
:
'
60%
'
}
}
mode
=
"multiple"
placeholder
=
"Please select two items to swap"
onChange
=
{
this
.
getSwapArr
}
maxTagCount
=
{
2
}
>
{
dimName
.
map
((
key
,
item
)
=>
{
return
(
<
Option
key
=
{
key
}
value
=
{
dimName
[
item
]
}
>
{
dimName
[
item
]
}
</
Option
>
);
})
}
</
Select
>
<
Button
type
=
"primary"
className
=
"changeBtu tableButton"
onClick
=
{
this
.
swapReInit
}
disabled
=
{
isLoadConfirm
}
>
Confirm
</
Button
>
</
Row
>
</
Col
>
</
Row
>
<
Row
className
=
"searcHyper"
>
<
div
className
=
"parameter"
>
<
Stack
horizontal
className
=
"para-filter"
horizontalAlign
=
"end"
>
<
span
className
=
"para-filter-text"
>
Top
</
span
>
<
Dropdown
selectedKey
=
{
selectedItem
?
selectedItem
.
key
:
undefined
}
onChange
=
{
this
.
percentNum
}
placeholder
=
"100%"
defaultSelectedKeys
=
{
[
0.2
]
}
options
=
{
[
{
key
:
'
0.2
'
,
text
:
'
20%
'
},
{
key
:
'
0.5
'
,
text
:
'
50%
'
},
{
key
:
'
0.8
'
,
text
:
'
80%
'
},
{
key
:
'
1
'
,
text
:
'
100%
'
},
]
}
styles
=
{
{
dropdown
:
{
width
:
300
}
}
}
className
=
"para-filter-percent"
/>
<
Dropdown
placeholder
=
"Select options"
selectedKeys
=
{
swapyAxis
}
onChange
=
{
this
.
getSwapArr
}
multiSelect
options
=
{
dimName
.
map
((
key
,
item
)
=>
{
return
{
key
:
key
,
text
:
dimName
[
item
]
};
})
}
styles
=
{
{
dropdown
:
{
width
:
300
}
}
}
/>
<
PrimaryButton
text
=
"Confirm"
onClick
=
{
this
.
swapReInit
}
disabled
=
{
isLoadConfirm
}
/>
</
Stack
>
<
div
className
=
"searcHyper"
>
<
ReactEcharts
option
=
{
option
}
style
=
{
this
.
chartMulineStyle
}
...
...
@@ -652,8 +639,8 @@ class Para extends React.Component<ParaProps, ParaState> {
notMerge
=
{
true
}
// update now
/>
<
div
className
=
"noneData"
>
{
paraNodata
}
</
div
>
</
Row
>
</
Row
>
</
div
>
</
div
>
);
}
}
...
...
src/webui/src/components/trial-detail/TableList.tsx
View file @
aa316742
import
*
as
React
from
'
react
'
;
import
axios
from
'
axios
'
;
import
ReactEcharts
from
'
echarts-for-react
'
;
import
{
Row
,
Table
,
Button
,
Popconfirm
,
Modal
,
Checkbox
,
Select
,
Icon
}
from
'
antd
'
;
import
{
ColumnProps
}
from
'
antd/lib/table
'
;
const
Option
=
Select
.
Option
;
const
CheckboxGroup
=
Checkbox
.
Group
;
import
{
MANAGER_IP
,
trialJobStatus
,
COLUMN_INDEX
,
COLUMNPro
}
from
'
../../static/const
'
;
import
{
convertDuration
,
formatTimestamp
,
intermediateGraphOption
,
killJob
,
parseMetrics
}
from
'
../../static/function
'
;
import
{
Stack
,
Dropdown
,
DetailsList
,
IDetailsListProps
,
DetailsListLayoutMode
,
PrimaryButton
,
Modal
,
IDropdownOption
,
IColumn
,
Selection
,
SelectionMode
,
IconButton
}
from
'
office-ui-fabric-react
'
;
import
{
LineChart
,
blocked
,
copy
}
from
'
../Buttons/Icon
'
;
import
{
MANAGER_IP
,
COLUMNPro
}
from
'
../../static/const
'
;
import
{
convertDuration
,
formatTimestamp
,
intermediateGraphOption
,
parseMetrics
}
from
'
../../static/function
'
;
import
{
EXPERIMENT
,
TRIALS
}
from
'
../../static/datamodel
'
;
import
{
TableRecord
}
from
'
../../static/interface
'
;
import
OpenRow
from
'
../public-child/OpenRow
'
;
import
Compare
from
'
../Modal/Compare
'
;
import
Customize
from
'
../Modal/CustomizedTrial
'
;
import
Details
from
'
../overview/Details
'
;
import
ChangeColumnComponent
from
'
../Modals/ChangeColumnComponent
'
;
import
Compare
from
'
../Modals/Compare
'
;
import
KillJob
from
'
../Modals/Killjob
'
;
import
Customize
from
'
../Modals/CustomizedTrial
'
;
import
{
contentStyles
,
iconButtonStyles
}
from
'
../Buttons/ModalTheme
'
;
import
'
../../static/style/search.scss
'
;
require
(
'
../../static/style/tableStatus.css
'
)
;
require
(
'
../../static/style/logPath.scss
'
)
;
require
(
'
../../static/style/search.scss
'
)
;
require
(
'
../../static/style/table.scss
'
)
;
require
(
'
../../static/style/button.scss
'
)
;
require
(
'
../../static/style/openRow.scss
'
)
;
import
'
../../static/style/tableStatus.css
'
;
import
'
../../static/style/logPath.scss
'
;
import
'
../../static/style/search.scss
'
;
import
'
../../static/style/table.scss
'
;
import
'
../../static/style/button.scss
'
;
import
'
../../static/style/openRow.scss
'
;
const
echarts
=
require
(
'
echarts/lib/echarts
'
);
require
(
'
echarts/lib/chart/line
'
);
require
(
'
echarts/lib/component/tooltip
'
);
...
...
@@ -27,11 +31,12 @@ echarts.registerTheme('my_theme', {
color
:
'
#3c8dbc
'
});
interface
TableListProps
{
pageSize
:
number
;
tableSource
:
Array
<
TableRecord
>
;
columnList
:
Array
<
string
>
;
// user select columnKeys
changeColumn
:
(
val
:
Array
<
string
>
)
=>
void
;
columnList
:
string
[]
;
// user select columnKeys
changeColumn
:
(
val
:
string
[]
)
=>
void
;
trialsUpdateBroadcast
:
number
;
}
...
...
@@ -40,115 +45,29 @@ interface TableListState {
modalVisible
:
boolean
;
isObjFinal
:
boolean
;
isShowColumn
:
boolean
;
selectRows
:
Array
<
TableRecord
>
;
selectRows
:
Array
<
any
>
;
isShowCompareModal
:
boolean
;
selectedRowKeys
:
string
[]
|
number
[];
intermediateData
:
Array
<
object
>
;
// a trial's intermediate results (include dict)
intermediateId
:
string
;
intermediateOtherKeys
:
Array
<
string
>
;
intermediateOtherKeys
:
string
[]
;
isShowCustomizedModal
:
boolean
;
copyTrialId
:
string
;
// user copy trial to submit a new customized trial
isCalloutVisible
:
boolean
;
// kill job button callout [kill or not kill job window]
intermediateKey
:
string
;
// intermeidate modal: which key is choosed.
isExpand
:
boolean
;
modalIntermediateWidth
:
number
;
modalIntermediateHeight
:
number
;
tableColumns
:
IColumn
[];
allColumnList
:
string
[];
tableSourceForSort
:
Array
<
TableRecord
>
;
}
interface
ColumnIndex
{
name
:
string
;
index
:
number
;
}
const
AccuracyColumnConfig
:
ColumnProps
<
TableRecord
>
=
{
title
:
'
Default metric
'
,
className
:
'
leftTitle
'
,
dataIndex
:
'
accuracy
'
,
sorter
:
(
a
,
b
,
sortOrder
)
=>
{
if
(
a
.
latestAccuracy
===
undefined
)
{
return
sortOrder
===
'
ascend
'
?
1
:
-
1
;
}
else
if
(
b
.
latestAccuracy
===
undefined
)
{
return
sortOrder
===
'
ascend
'
?
-
1
:
1
;
}
else
{
return
a
.
latestAccuracy
-
b
.
latestAccuracy
;
}
},
render
:
(
text
,
record
):
React
.
ReactNode
=>
<
div
>
{
record
.
formattedLatestAccuracy
}
</
div
>
};
const
SequenceIdColumnConfig
:
ColumnProps
<
TableRecord
>
=
{
title
:
'
Trial No.
'
,
dataIndex
:
'
sequenceId
'
,
className
:
'
tableHead
'
,
sorter
:
(
a
,
b
)
=>
a
.
sequenceId
-
b
.
sequenceId
};
const
IdColumnConfig
:
ColumnProps
<
TableRecord
>
=
{
title
:
'
ID
'
,
dataIndex
:
'
id
'
,
className
:
'
tableHead leftTitle
'
,
sorter
:
(
a
,
b
)
=>
a
.
id
.
localeCompare
(
b
.
id
),
render
:
(
text
,
record
):
React
.
ReactNode
=>
(
<
div
>
{
record
.
id
}
</
div
>
)
};
const
StartTimeColumnConfig
:
ColumnProps
<
TableRecord
>
=
{
title
:
'
Start Time
'
,
dataIndex
:
'
startTime
'
,
sorter
:
(
a
,
b
)
=>
a
.
startTime
-
b
.
startTime
,
render
:
(
text
,
record
):
React
.
ReactNode
=>
(
<
span
>
{
formatTimestamp
(
record
.
startTime
)
}
</
span
>
)
};
const
EndTimeColumnConfig
:
ColumnProps
<
TableRecord
>
=
{
title
:
'
End Time
'
,
dataIndex
:
'
endTime
'
,
sorter
:
(
a
,
b
,
sortOrder
)
=>
{
if
(
a
.
endTime
===
undefined
)
{
return
sortOrder
===
'
ascend
'
?
1
:
-
1
;
}
else
if
(
b
.
endTime
===
undefined
)
{
return
sortOrder
===
'
ascend
'
?
-
1
:
1
;
}
else
{
return
a
.
endTime
-
b
.
endTime
;
}
},
render
:
(
text
,
record
):
React
.
ReactNode
=>
(
<
span
>
{
formatTimestamp
(
record
.
endTime
,
'
--
'
)
}
</
span
>
)
};
const
DurationColumnConfig
:
ColumnProps
<
TableRecord
>
=
{
title
:
'
Duration
'
,
dataIndex
:
'
duration
'
,
sorter
:
(
a
,
b
)
=>
a
.
duration
-
b
.
duration
,
render
:
(
text
,
record
):
React
.
ReactNode
=>
(
<
span
className
=
"durationsty"
>
{
convertDuration
(
record
.
duration
)
}
</
span
>
)
};
const
StatusColumnConfig
:
ColumnProps
<
TableRecord
>
=
{
title
:
'
Status
'
,
dataIndex
:
'
status
'
,
className
:
'
tableStatus
'
,
render
:
(
text
,
record
):
React
.
ReactNode
=>
(
<
span
className
=
{
`
${
record
.
status
}
commonStyle`
}
>
{
record
.
status
}
</
span
>
),
sorter
:
(
a
,
b
)
=>
a
.
status
.
localeCompare
(
b
.
status
),
filters
:
trialJobStatus
.
map
(
status
=>
({
text
:
status
,
value
:
status
})),
onFilter
:
(
value
,
record
)
=>
(
record
.
status
===
value
)
};
const
IntermediateCountColumnConfig
:
ColumnProps
<
TableRecord
>
=
{
title
:
'
Intermediate result
'
,
dataIndex
:
'
intermediateCount
'
,
sorter
:
(
a
,
b
)
=>
a
.
intermediateCount
-
b
.
intermediateCount
,
render
:
(
text
,
record
):
React
.
ReactNode
=>
(
<
span
>
{
`#
${
record
.
intermediateCount
}
`
}
</
span
>
)
};
class
TableList
extends
React
.
Component
<
TableListProps
,
TableListState
>
{
public
intervalTrialLog
=
10
;
public
_trialId
:
string
;
public
tables
:
Table
<
TableRecord
>
|
null
;
public
trialId
!
:
string
;
constructor
(
props
:
TableListProps
)
{
super
(
props
);
...
...
@@ -165,18 +84,164 @@ class TableList extends React.Component<TableListProps, TableListState> {
intermediateId
:
''
,
intermediateOtherKeys
:
[],
isShowCustomizedModal
:
false
,
copyTrialId
:
''
isCalloutVisible
:
false
,
copyTrialId
:
''
,
intermediateKey
:
'
default
'
,
isExpand
:
false
,
modalIntermediateWidth
:
window
.
innerWidth
,
modalIntermediateHeight
:
window
.
innerHeight
,
tableColumns
:
this
.
initTableColumnList
(
this
.
props
.
columnList
),
allColumnList
:
this
.
getAllColumnKeys
(),
tableSourceForSort
:
this
.
props
.
tableSource
};
}
showIntermediateModal
=
async
(
id
:
string
):
Promise
<
void
>
=>
{
// sort for table column
onColumnClick
=
(
ev
:
React
.
MouseEvent
<
HTMLElement
>
,
getColumn
:
IColumn
):
void
=>
{
const
{
tableColumns
}
=
this
.
state
;
const
{
tableSource
}
=
this
.
props
;
const
newColumns
:
IColumn
[]
=
tableColumns
.
slice
();
const
currColumn
:
IColumn
=
newColumns
.
filter
(
item
=>
getColumn
.
key
===
item
.
key
)[
0
];
newColumns
.
forEach
((
newCol
:
IColumn
)
=>
{
if
(
newCol
===
currColumn
)
{
currColumn
.
isSortedDescending
=
!
currColumn
.
isSortedDescending
;
currColumn
.
isSorted
=
true
;
}
else
{
newCol
.
isSorted
=
false
;
newCol
.
isSortedDescending
=
true
;
}
});
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const
newItems
=
this
.
copyAndSort
(
tableSource
,
currColumn
.
fieldName
!
,
currColumn
.
isSortedDescending
);
this
.
setState
({
tableColumns
:
newColumns
,
tableSourceForSort
:
newItems
});
};
private
copyAndSort
<
T
>
(
items
:
T
[],
columnKey
:
string
,
isSortedDescending
?:
boolean
):
T
[]
{
const
key
=
columnKey
as
keyof
T
;
return
items
.
slice
(
0
).
sort
((
a
:
T
,
b
:
T
)
=>
((
isSortedDescending
?
a
[
key
]
<
b
[
key
]
:
a
[
key
]
>
b
[
key
])
?
1
:
-
1
));
}
AccuracyColumnConfig
:
any
=
{
name
:
'
Default metric
'
,
className
:
'
leftTitle
'
,
key
:
'
accuracy
'
,
fieldName
:
'
latestAccuracy
'
,
minWidth
:
200
,
maxWidth
:
300
,
isResizable
:
true
,
data
:
'
number
'
,
onColumnClick
:
this
.
onColumnClick
,
onRender
:
(
item
):
React
.
ReactNode
=>
<
div
>
{
item
.
formattedLatestAccuracy
}
</
div
>
};
SequenceIdColumnConfig
:
any
=
{
name
:
'
Trial No.
'
,
key
:
'
sequenceId
'
,
fieldName
:
'
sequenceId
'
,
minWidth
:
80
,
maxWidth
:
120
,
className
:
'
tableHead
'
,
data
:
'
string
'
,
onColumnClick
:
this
.
onColumnClick
,
};
IdColumnConfig
:
any
=
{
name
:
'
ID
'
,
key
:
'
id
'
,
fieldName
:
'
id
'
,
minWidth
:
150
,
maxWidth
:
200
,
isResizable
:
true
,
data
:
'
string
'
,
onColumnClick
:
this
.
onColumnClick
,
className
:
'
tableHead leftTitle
'
};
StartTimeColumnConfig
:
any
=
{
name
:
'
Start Time
'
,
key
:
'
startTime
'
,
fieldName
:
'
startTime
'
,
minWidth
:
150
,
maxWidth
:
200
,
isResizable
:
true
,
data
:
'
number
'
,
onColumnClick
:
this
.
onColumnClick
,
onRender
:
(
record
):
React
.
ReactNode
=>
(
<
span
>
{
formatTimestamp
(
record
.
startTime
)
}
</
span
>
)
};
EndTimeColumnConfig
:
any
=
{
name
:
'
End Time
'
,
key
:
'
endTime
'
,
fieldName
:
'
endTime
'
,
minWidth
:
150
,
maxWidth
:
200
,
isResizable
:
true
,
data
:
'
number
'
,
onColumnClick
:
this
.
onColumnClick
,
onRender
:
(
record
):
React
.
ReactNode
=>
(
<
span
>
{
formatTimestamp
(
record
.
endTime
,
'
--
'
)
}
</
span
>
)
};
DurationColumnConfig
:
any
=
{
name
:
'
Duration
'
,
key
:
'
duration
'
,
fieldName
:
'
duration
'
,
minWidth
:
150
,
maxWidth
:
200
,
isResizable
:
true
,
data
:
'
number
'
,
onColumnClick
:
this
.
onColumnClick
,
onRender
:
(
record
):
React
.
ReactNode
=>
(
<
span
className
=
"durationsty"
>
{
convertDuration
(
record
.
duration
)
}
</
span
>
)
};
StatusColumnConfig
:
any
=
{
name
:
'
Status
'
,
key
:
'
status
'
,
fieldName
:
'
status
'
,
className
:
'
tableStatus
'
,
minWidth
:
150
,
maxWidth
:
200
,
isResizable
:
true
,
data
:
'
string
'
,
onColumnClick
:
this
.
onColumnClick
,
onRender
:
(
record
):
React
.
ReactNode
=>
(
<
span
className
=
{
`
${
record
.
status
}
commonStyle`
}
>
{
record
.
status
}
</
span
>
),
};
IntermediateCountColumnConfig
:
any
=
{
name
:
'
Intermediate result
'
,
dataIndex
:
'
intermediateCount
'
,
fieldName
:
'
intermediateCount
'
,
minWidth
:
150
,
maxWidth
:
200
,
isResizable
:
true
,
data
:
'
number
'
,
onColumnClick
:
this
.
onColumnClick
,
onRender
:
(
record
):
React
.
ReactNode
=>
(
<
span
>
{
`#
${
record
.
intermediateCount
}
`
}
</
span
>
)
};
showIntermediateModal
=
async
(
id
:
string
,
event
:
React
.
SyntheticEvent
<
EventTarget
>
):
Promise
<
void
>
=>
{
event
.
preventDefault
();
event
.
stopPropagation
();
const
res
=
await
axios
.
get
(
`
${
MANAGER_IP
}
/metric-data/
${
id
}
`
);
if
(
res
.
status
===
200
)
{
const
intermediateArr
:
number
[]
=
[];
// support intermediate result is dict because the last intermediate result is
// final result in a succeed trial, it may be a dict.
// get intermediate result dict keys array
let
otherkeys
:
Array
<
string
>
=
[
'
default
'
];
let
otherkeys
:
string
[]
=
[
'
default
'
];
if
(
res
.
data
.
length
!==
0
)
{
otherkeys
=
Object
.
keys
(
parseMetrics
(
res
.
data
[
0
].
data
));
}
...
...
@@ -202,34 +267,37 @@ class TableList extends React.Component<TableListProps, TableListState> {
// intermediate button click -> intermediate graph for each trial
// support intermediate is dict
selectOtherKeys
=
(
value
:
string
):
void
=>
{
const
isShowDefault
:
boolean
=
value
===
'
default
'
?
true
:
false
;
const
{
intermediateData
,
intermediateId
}
=
this
.
state
;
const
intermediateArr
:
number
[]
=
[];
// just watch default key-val
if
(
isShowDefault
===
true
)
{
Object
.
keys
(
intermediateData
).
map
(
item
=>
{
const
temp
=
parseMetrics
(
intermediateData
[
item
].
data
);
if
(
typeof
temp
===
'
object
'
)
{
intermediateArr
.
push
(
temp
[
value
]);
}
else
{
intermediateArr
.
push
(
temp
);
}
});
}
else
{
Object
.
keys
(
intermediateData
).
map
(
item
=>
{
const
temp
=
parseMetrics
(
intermediateData
[
item
].
data
);
if
(
typeof
temp
===
'
object
'
)
{
intermediateArr
.
push
(
temp
[
value
]);
}
selectOtherKeys
=
(
event
:
React
.
FormEvent
<
HTMLDivElement
>
,
item
?:
IDropdownOption
):
void
=>
{
if
(
item
!==
undefined
)
{
const
value
=
item
.
text
;
const
isShowDefault
:
boolean
=
value
===
'
default
'
?
true
:
false
;
const
{
intermediateData
,
intermediateId
}
=
this
.
state
;
const
intermediateArr
:
number
[]
=
[];
// just watch default key-val
if
(
isShowDefault
===
true
)
{
Object
.
keys
(
intermediateData
).
map
(
item
=>
{
const
temp
=
parseMetrics
(
intermediateData
[
item
].
data
);
if
(
typeof
temp
===
'
object
'
)
{
intermediateArr
.
push
(
temp
[
value
]);
}
else
{
intermediateArr
.
push
(
temp
);
}
});
}
else
{
Object
.
keys
(
intermediateData
).
map
(
item
=>
{
const
temp
=
parseMetrics
(
intermediateData
[
item
].
data
);
if
(
typeof
temp
===
'
object
'
)
{
intermediateArr
.
push
(
temp
[
value
]);
}
});
}
const
intermediate
=
intermediateGraphOption
(
intermediateArr
,
intermediateId
);
// re-render
this
.
setState
({
intermediateKey
:
value
,
intermediateOption
:
intermediate
});
}
const
intermediate
=
intermediateGraphOption
(
intermediateArr
,
intermediateId
);
// re-render
this
.
setState
({
intermediateOption
:
intermediate
});
}
hideIntermediateModal
=
():
void
=>
{
...
...
@@ -239,81 +307,20 @@ class TableList extends React.Component<TableListProps, TableListState> {
}
hideShowColumnModal
=
():
void
=>
{
this
.
setState
({
isShowColumn
:
false
});
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
});
}
// checkbox for coloumn
selectedColumn
=
(
checkedValues
:
Array
<
string
>
):
void
=>
{
// 9: because have nine common column,
// [Intermediate count, Start Time, End Time] is hidden by default
let
count
=
9
;
const
want
:
Array
<
object
>
=
[];
const
finalKeys
:
Array
<
string
>
=
[];
const
wantResult
:
Array
<
string
>
=
[];
Object
.
keys
(
checkedValues
).
map
(
m
=>
{
switch
(
checkedValues
[
m
])
{
case
'
Trial No.
'
:
case
'
ID
'
:
case
'
Start Time
'
:
case
'
End Time
'
:
case
'
Duration
'
:
case
'
Status
'
:
case
'
Operation
'
:
case
'
Default
'
:
case
'
Intermediate result
'
:
break
;
default
:
finalKeys
.
push
(
checkedValues
[
m
]);
}
});
Object
.
keys
(
finalKeys
).
map
(
n
=>
{
want
.
push
({
name
:
finalKeys
[
n
],
index
:
count
++
});
});
Object
.
keys
(
checkedValues
).
map
(
item
=>
{
const
temp
=
checkedValues
[
item
];
Object
.
keys
(
COLUMN_INDEX
).
map
(
key
=>
{
const
index
=
COLUMN_INDEX
[
key
];
if
(
index
.
name
===
temp
)
{
want
.
push
(
index
);
}
});
});
want
.
sort
((
a
:
ColumnIndex
,
b
:
ColumnIndex
)
=>
{
return
a
.
index
-
b
.
index
;
});
Object
.
keys
(
want
).
map
(
i
=>
{
wantResult
.
push
(
want
[
i
].
name
);
});
this
.
props
.
changeColumn
(
wantResult
);
}
openRow
=
(
record
:
TableRecord
):
any
=>
{
return
(
<
OpenRow
trialId
=
{
record
.
id
}
/>
);
this
.
setState
(()
=>
({
isShowColumn
:
true
}));
}
fillSelectedRowsTostate
=
(
selected
:
number
[]
|
string
[],
selectedRows
:
Array
<
TableRecord
>
):
void
=>
{
this
.
setState
({
selectRows
:
selectedRows
,
selectedRowKeys
:
selected
});
}
// open Compare-modal
compareBtn
=
():
void
=>
{
...
...
@@ -324,6 +331,7 @@ class TableList extends React.Component<TableListProps, TableListState> {
this
.
setState
({
isShowCompareModal
:
true
});
}
}
// close Compare-modal
hideCompareModal
=
():
void
=>
{
// close modal. clear select rows data, clear selected track
...
...
@@ -331,178 +339,179 @@ class TableList extends React.Component<TableListProps, TableListState> {
}
// open customized trial modal
setCustomizedTrial
=
(
trialId
:
string
):
void
=>
{
private
setCustomizedTrial
=
(
trialId
:
string
,
event
:
React
.
SyntheticEvent
<
EventTarget
>
):
void
=>
{
event
.
preventDefault
();
event
.
stopPropagation
();
this
.
setState
({
isShowCustomizedModal
:
true
,
copyTrialId
:
trialId
});
}
closeCustomizedTrial
=
():
void
=>
{
private
closeCustomizedTrial
=
():
void
=>
{
this
.
setState
({
isShowCustomizedModal
:
false
,
copyTrialId
:
''
});
}
render
():
React
.
ReactNode
{
const
{
pageSize
,
columnList
}
=
this
.
props
;
const
tableSource
:
Array
<
TableRecord
>
=
JSON
.
parse
(
JSON
.
stringify
(
this
.
props
.
tableSource
));
const
{
intermediateOption
,
modalVisible
,
isShowColumn
,
selectRows
,
isShowCompareModal
,
selectedRowKeys
,
intermediateOtherKeys
,
isShowCustomizedModal
,
copyTrialId
}
=
this
.
state
;
const
rowSelection
=
{
selectedRowKeys
:
selectedRowKeys
,
onChange
:
(
selected
:
string
[]
|
number
[],
selectedRows
:
Array
<
TableRecord
>
):
void
=>
{
this
.
fillSelectedRowsTostate
(
selected
,
selectedRows
);
}
};
// [supportCustomizedTrial: true]
const
supportCustomizedTrial
=
(
EXPERIMENT
.
multiPhase
===
true
)
?
false
:
true
;
const
disabledAddCustomizedTrial
=
[
'
DONE
'
,
'
ERROR
'
,
'
STOPPED
'
].
includes
(
EXPERIMENT
.
status
);
let
showTitle
=
COLUMNPro
;
const
showColumn
:
Array
<
object
>
=
[];
private
onWindowResize
=
():
void
=>
{
this
.
setState
(()
=>
({
modalIntermediateHeight
:
window
.
innerHeight
,
modalIntermediateWidth
:
window
.
innerWidth
}));
}
private
onRenderRow
:
IDetailsListProps
[
'
onRenderRow
'
]
=
props
=>
{
if
(
props
)
{
return
<
Details
detailsProps
=
{
props
}
/>;
}
return
null
;
};
private
getSelectedRows
=
new
Selection
({
onSelectionChanged
:
():
void
=>
{
this
.
setState
(()
=>
({
selectRows
:
this
.
getSelectedRows
.
getSelection
()
}));
}
});
// trial parameters & dict final keys & Trial No. Id ...
private
getAllColumnKeys
=
():
string
[]
=>
{
const
tableSource
:
Array
<
TableRecord
>
=
JSON
.
parse
(
JSON
.
stringify
(
this
.
props
.
tableSource
));
// parameter as table column
const
parameterStr
:
Array
<
string
>
=
[];
const
parameterStr
:
string
[]
=
[];
if
(
tableSource
.
length
>
0
)
{
const
trialMess
=
TRIALS
.
getTrial
(
tableSource
[
0
].
id
);
const
trial
=
trialMess
.
description
.
parameters
;
const
parameterColumn
:
Array
<
string
>
=
Object
.
keys
(
trial
);
const
parameterColumn
:
string
[]
=
Object
.
keys
(
trial
);
parameterColumn
.
forEach
(
value
=>
{
parameterStr
.
push
(
`
${
value
}
(search space)`
);
});
}
showTitle
=
COLUMNPro
.
concat
(
parameterStr
);
let
allColumnList
=
COLUMNPro
.
concat
(
parameterStr
);
// only succeed trials have final keys
if
(
tableSource
.
filter
(
record
=>
record
.
status
===
'
SUCCEEDED
'
).
length
>=
1
)
{
const
temp
=
tableSource
.
filter
(
record
=>
record
.
status
===
'
SUCCEEDED
'
)[
0
].
acc
urac
y
;
const
temp
=
tableSource
.
filter
(
record
=>
record
.
status
===
'
SUCCEEDED
'
)[
0
].
acc
Dictionar
y
;
if
(
temp
!==
undefined
&&
typeof
temp
===
'
object
'
)
{
// concat default column and finalkeys
const
item
=
Object
.
keys
(
temp
);
// item: ['default', 'other-keys', 'maybe loss']
if
(
item
.
length
>
1
)
{
const
want
:
Array
<
string
>
=
[];
const
want
:
string
[]
=
[];
item
.
forEach
(
value
=>
{
if
(
value
!==
'
default
'
)
{
want
.
push
(
value
);
}
});
showTitle
=
COLUMNPro
.
concat
(
want
);
allColumnList
=
allColumnList
.
concat
(
want
);
}
}
}
return
allColumnList
;
}
// get IColumn[]
// when user click [Add Column] need to use the function
private
initTableColumnList
=
(
columnList
:
string
[]):
IColumn
[]
=>
{
// const { columnList } = this.props;
// [supportCustomizedTrial: true]
const
supportCustomizedTrial
=
(
EXPERIMENT
.
multiPhase
===
true
)
?
false
:
true
;
const
disabledAddCustomizedTrial
=
[
'
DONE
'
,
'
ERROR
'
,
'
STOPPED
'
].
includes
(
EXPERIMENT
.
status
);
const
showColumn
:
IColumn
[]
=
[];
for
(
const
item
of
columnList
)
{
const
paraColumn
=
item
.
match
(
/
\(
search space
\)
$/
);
let
cc
;
let
result
;
if
(
paraColumn
!==
null
)
{
cc
=
paraColumn
.
input
;
result
=
paraColumn
.
input
;
}
switch
(
item
)
{
case
'
Trial No.
'
:
showColumn
.
push
(
SequenceIdColumnConfig
);
showColumn
.
push
(
this
.
SequenceIdColumnConfig
);
break
;
case
'
ID
'
:
showColumn
.
push
(
IdColumnConfig
);
showColumn
.
push
(
this
.
IdColumnConfig
);
break
;
case
'
Start Time
'
:
showColumn
.
push
(
StartTimeColumnConfig
);
showColumn
.
push
(
this
.
StartTimeColumnConfig
);
break
;
case
'
End Time
'
:
showColumn
.
push
(
EndTimeColumnConfig
);
showColumn
.
push
(
this
.
EndTimeColumnConfig
);
break
;
case
'
Duration
'
:
showColumn
.
push
(
DurationColumnConfig
);
showColumn
.
push
(
this
.
DurationColumnConfig
);
break
;
case
'
Status
'
:
showColumn
.
push
(
StatusColumnConfig
);
showColumn
.
push
(
this
.
StatusColumnConfig
);
break
;
case
'
Intermediate result
'
:
showColumn
.
push
(
IntermediateCountColumnConfig
);
showColumn
.
push
(
this
.
IntermediateCountColumnConfig
);
break
;
case
'
Default
'
:
showColumn
.
push
(
AccuracyColumnConfig
);
showColumn
.
push
(
this
.
AccuracyColumnConfig
);
break
;
case
'
Operation
'
:
showColumn
.
push
({
title
:
'
Operation
'
,
dataIndex
:
'
operation
'
,
name
:
'
Operation
'
,
key
:
'
operation
'
,
render
:
(
text
:
string
,
record
:
TableRecord
)
=>
{
fieldName
:
'
operation
'
,
minWidth
:
160
,
maxWidth
:
200
,
isResizable
:
true
,
className
:
'
detail-table
'
,
onRender
:
(
record
:
any
)
=>
{
const
trialStatus
=
record
.
status
;
// could kill a job when its status is RUNNING or UNKNOWN
const
flag
:
boolean
=
(
trialStatus
===
'
RUNNING
'
||
trialStatus
===
'
UNKNOWN
'
)
?
false
:
true
;
return
(
<
Row
id
=
"detail-button"
>
<
Stack
className
=
"detail-button"
horizontal
>
{
/* see intermediate result graph */
}
<
Button
type
=
"primary"
className
=
"common-style"
onClick
=
{
this
.
showIntermediateModal
.
bind
(
this
,
record
.
id
)
}
<
PrimaryButton
className
=
"detail-button-operation"
title
=
"Intermediate"
onClick
=
{
this
.
showIntermediateModal
.
bind
(
this
,
record
.
id
)
}
>
<
Icon
type
=
"l
ine
-c
hart
"
/>
</
Button
>
{
L
ine
C
hart
}
</
Primary
Button
>
{
/* kill job */
}
{
flag
?
<
Button
type
=
"default"
disabled
=
{
true
}
className
=
"margin-mediate special"
title
=
"kill"
>
<
Icon
type
=
"stop"
/>
</
Button
>
<
PrimaryButton
className
=
"detail-button-operation"
disabled
=
{
true
}
title
=
"kill"
>
{
blocked
}
</
PrimaryButton
>
:
<
Popconfirm
title
=
"Are you sure to cancel this trial?"
okText
=
"Yes"
cancelText
=
"No"
onConfirm
=
{
killJob
.
bind
(
this
,
record
.
key
,
record
.
id
,
record
.
status
)
}
>
<
Button
type
=
"default"
disabled
=
{
false
}
className
=
"margin-mediate special"
title
=
"kill"
>
<
Icon
type
=
"stop"
/>
</
Button
>
</
Popconfirm
>
<
KillJob
trial
=
{
record
}
/>
}
{
/* Add a new trial-customized trial */
}
{
supportCustomizedTrial
?
<
Button
type
=
"primary"
className
=
"common-style"
disabled
=
{
disabledAddCustomizedTrial
}
onClick
=
{
this
.
setCustomizedTrial
.
bind
(
this
,
record
.
id
)
}
<
PrimaryButton
className
=
"detail-button-operation"
title
=
"Customized trial"
onClick
=
{
this
.
setCustomizedTrial
.
bind
(
this
,
record
.
id
)
}
disabled
=
{
disabledAddCustomizedTrial
}
>
<
Icon
type
=
"copy"
/>
</
Button
>
{
copy
}
</
Primary
Button
>
:
null
}
</
Row
>
</
Stack
>
);
},
});
break
;
case
(
cc
):
case
(
result
):
// remove SEARCH_SPACE title
// const realItem = item.replace(' (search space)', '');
showColumn
.
push
({
title
:
item
.
replace
(
'
(search space)
'
,
''
),
dataIndex
:
item
,
name
:
item
.
replace
(
'
(search space)
'
,
''
),
key
:
item
,
render
:
(
text
:
string
,
record
:
TableRecord
)
=>
{
fieldName
:
item
,
minWidth
:
150
,
onRender
:
(
record
:
TableRecord
)
=>
{
const
eachTrial
=
TRIALS
.
getTrial
(
record
.
id
);
return
(
<
span
>
{
eachTrial
.
description
.
parameters
[
item
.
replace
(
'
(search space)
'
,
''
)]
}
</
span
>
...
...
@@ -511,92 +520,130 @@ class TableList extends React.Component<TableListProps, TableListState> {
});
break
;
default
:
// FIXME
alert
(
'
Unexpected column type
'
);
showColumn
.
push
({
name
:
item
,
key
:
item
,
fieldName
:
item
,
minWidth
:
100
,
onRender
:
(
record
:
TableRecord
)
=>
{
const
accDictionary
=
record
.
accDictionary
;
let
other
=
''
;
if
(
accDictionary
!==
undefined
)
{
other
=
accDictionary
[
item
].
toString
();
}
return
(
<
div
>
{
other
}
</
div
>
);
}
});
}
}
return
showColumn
;
}
componentDidMount
():
void
{
window
.
addEventListener
(
'
resize
'
,
this
.
onWindowResize
);
}
UNSAFE_componentWillReceiveProps
(
nextProps
:
TableListProps
):
void
{
const
{
columnList
,
tableSource
}
=
nextProps
;
this
.
setState
({
tableSourceForSort
:
tableSource
,
tableColumns
:
this
.
initTableColumnList
(
columnList
),
allColumnList
:
this
.
getAllColumnKeys
()
});
}
render
():
React
.
ReactNode
{
const
{
intermediateKey
,
modalIntermediateWidth
,
modalIntermediateHeight
,
tableColumns
,
allColumnList
,
isShowColumn
,
modalVisible
,
selectRows
,
isShowCompareModal
,
intermediateOtherKeys
,
isShowCustomizedModal
,
copyTrialId
,
intermediateOption
}
=
this
.
state
;
const
{
columnList
}
=
this
.
props
;
const
tableSource
:
Array
<
TableRecord
>
=
JSON
.
parse
(
JSON
.
stringify
(
this
.
state
.
tableSourceForSort
));
return
(
<
Row
className
=
"tableList"
>
<
Stack
>
<
div
id
=
"tableList"
>
<
Table
ref
=
{
(
table
:
Table
<
TableRecord
>
|
null
):
any
=>
this
.
tables
=
table
}
columns
=
{
showColumn
}
rowSelection
=
{
rowSelection
}
expandedRowRender
=
{
this
.
openRow
}
dataSource
=
{
tableSource
}
c
la
ssName
=
"commonTableStyle"
s
croll
=
{
{
x
:
'
max-content
'
}
}
pagination
=
{
pageSize
>
0
?
{
pageSize
}
:
false
}
<
DetailsList
columns
=
{
tableColumns
}
items
=
{
tableSource
}
setKey
=
"set"
compact
=
{
true
}
onRenderRow
=
{
this
.
onRenderRow
}
la
youtMode
=
{
DetailsListLayoutMode
.
justified
}
s
electionMode
=
{
SelectionMode
.
multiple
}
selection
=
{
this
.
getSelectedRows
}
/>
{
/* Intermediate Result Modal */
}
<
Modal
title
=
"Intermediate result"
visible
=
{
modalVisible
}
onCancel
=
{
this
.
hideIntermediateModal
}
footer
=
{
null
}
destroyOnClose
=
{
true
}
width
=
"80%"
>
{
intermediateOtherKeys
.
length
>
1
?
<
Row
className
=
"selectKeys"
>
<
Select
className
=
"select"
defaultValue
=
"default"
onSelect
=
{
this
.
selectOtherKeys
}
>
{
Object
.
keys
(
intermediateOtherKeys
).
map
(
item
=>
{
const
keys
=
intermediateOtherKeys
[
item
];
return
<
Option
value
=
{
keys
}
key
=
{
item
}
>
{
keys
}
</
Option
>;
})
}
</
Select
>
</
Row
>
:
<
div
/>
}
<
ReactEcharts
option
=
{
intermediateOption
}
style
=
{
{
width
:
'
100%
'
,
height
:
0.7
*
window
.
innerHeight
}
}
theme
=
"my_theme"
/>
</
Modal
>
</
div
>
{
/*
Add Column
Modal */
}
{
/*
Intermediate Result
Modal */
}
<
Modal
title
=
"Table Title"
visible
=
{
isShowColumn
}
onCancel
=
{
this
.
hideShowColumnModal
}
footer
=
{
null
}
destroyOnClose
=
{
true
}
width
=
"40%"
isOpen
=
{
modalVisible
}
onDismiss
=
{
this
.
hideIntermediateModal
}
containerClassName
=
{
contentStyles
.
container
}
>
<
CheckboxGroup
options
=
{
showTitle
}
defaultValue
=
{
columnList
}
// defaultValue={columnSelected}
onChange
=
{
this
.
selectedColumn
}
className
=
"titleColumn"
<
div
className
=
{
contentStyles
.
header
}
>
<
span
>
Intermediate result
</
span
>
<
IconButton
styles
=
{
iconButtonStyles
}
iconProps
=
{
{
iconName
:
'
Cancel
'
}
}
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
}
<
ReactEcharts
option
=
{
intermediateOption
}
style
=
{
{
width
:
0.5
*
modalIntermediateWidth
,
height
:
0.7
*
modalIntermediateHeight
,
padding
:
20
}
}
theme
=
"my_theme"
/>
</
Modal
>
{
/* Add Column Modal */
}
{
isShowColumn
&&
<
ChangeColumnComponent
hideShowColumnDialog
=
{
this
.
hideShowColumnModal
}
isHideDialog
=
{
!
isShowColumn
}
showColumn
=
{
allColumnList
}
selectedColumn
=
{
columnList
}
changeColumn
=
{
this
.
props
.
changeColumn
}
/>
}
{
/* compare trials based message */
}
<
Compare
compare
Row
s
=
{
selectRows
}
visible
=
{
isShowCompareModal
}
cancelFunc
=
{
this
.
hideCompareModal
}
/>
{
isShowCompareModal
&&
<
Compare
compare
Stack
s
=
{
selectRows
}
cancelFunc
=
{
this
.
hideCompareModal
}
/>
}
{
/* clone trial parameters and could submit a customized trial */
}
<
Customize
visible
=
{
isShowCustomizedModal
}
copyTrialId
=
{
copyTrialId
}
closeCustomizeModal
=
{
this
.
closeCustomizedTrial
}
/>
</
Row
>
</
Stack
>
);
}
}
export
default
TableList
;
export
default
TableList
;
\ No newline at end of file
src/webui/src/index.css
View file @
aa316742
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
;
}
code
{
font-family
:
source-code-pro
,
Menlo
,
Monaco
,
Consolas
,
'Courier New'
,
monospace
;
}
/* http://meyerweb.com/eric/tools/css/reset/
v4.0 | 20180602
License: none (public domain)
...
...
@@ -31,9 +49,6 @@ footer, header, hgroup, main, menu, nav, section {
*[
hidden
]
{
display
:
none
;
}
body
{
line-height
:
1
;
}
ol
,
ul
{
list-style
:
none
;
}
...
...
src/webui/src/index.tsx
View file @
aa316742
import
'
babel-polyfill
'
;
import
*
as
React
from
'
react
'
;
import
*
as
ReactDOM
from
'
react-dom
'
;
import
React
from
'
react
'
;
import
ReactDOM
from
'
react-dom
'
;
import
App
from
'
./App
'
;
import
{
Router
,
Route
,
browserHistory
,
IndexRedirect
}
from
'
react-router
'
;
import
registerServiceWorker
from
'
./registerServiceWorker
'
;
import
Overview
from
'
./components/Overview
'
;
import
TrialsDetail
from
'
./components/TrialsDetail
'
;
import
'
./index.css
'
;
import
*
as
serviceWorker
from
'
./serviceWorker
'
;
ReactDOM
.
render
(
(
<
Router
history
=
{
browserHistory
}
>
<
Route
path
=
"/"
component
=
{
App
}
>
<
IndexRedirect
to
=
"/oview"
/>
<
Route
path
=
"/oview"
component
=
{
Overview
}
/>
<
Route
path
=
"/detail"
component
=
{
TrialsDetail
}
/>
</
Route
>
</
Router
>
),
document
.
getElementById
(
'
root
'
)
as
HTMLElement
(
<
Router
history
=
{
browserHistory
}
>
<
Route
path
=
"/"
component
=
{
App
}
>
<
IndexRedirect
to
=
"/oview"
/>
<
Route
path
=
"/oview"
component
=
{
Overview
}
/>
<
Route
path
=
"/detail"
component
=
{
TrialsDetail
}
/>
{
/* test branch */
}
</
Route
>
</
Router
>
),
document
.
getElementById
(
'
root
'
)
);
registerServiceWorker
();
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker
.
unregister
();
src/webui/src/logo.svg
0 → 100644
View file @
aa316742
<svg
xmlns=
"http://www.w3.org/2000/svg"
viewBox=
"0 0 841.9 595.3"
>
<g
fill=
"#61DAFB"
>
<path
d=
"M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"
/>
<circle
cx=
"420.9"
cy=
"296.5"
r=
"45.7"
/>
<path
d=
"M520.5 78.1z"
/>
</g>
</svg>
src/webui/src/react-app-env.d.ts
0 → 100644
View file @
aa316742
declare
namespace
NodeJS
{
interface
ProcessEnv
{
readonly
NODE_ENV
:
'
development
'
|
'
production
'
|
'
test
'
;
readonly
PUBLIC_URL
:
string
;
}
}
declare
module
'
*.bmp
'
{
const
src
:
string
;
export
default
src
;
}
declare
module
'
*.gif
'
{
const
src
:
string
;
export
default
src
;
}
declare
module
'
*.jpg
'
{
const
src
:
string
;
export
default
src
;
}
declare
module
'
*.jpeg
'
{
const
src
:
string
;
export
default
src
;
}
declare
module
'
*.png
'
{
const
src
:
string
;
export
default
src
;
}
declare
module
'
*.webp
'
{
const
src
:
string
;
export
default
src
;
}
declare
module
'
*.svg
'
{
import
*
as
React
from
'
react
'
;
export
const
ReactComponent
:
React
.
FunctionComponent
<
React
.
SVGProps
<
SVGSVGElement
>>
;
const
src
:
string
;
export
default
src
;
}
declare
module
'
*.module.css
'
{
const
classes
:
{
[
key
:
string
]:
string
};
export
default
classes
;
}
declare
module
'
*.module.scss
'
{
const
classes
:
{
[
key
:
string
]:
string
};
export
default
classes
;
}
declare
module
'
*.module.sass
'
{
const
classes
:
{
[
key
:
string
]:
string
};
export
default
classes
;
}
src/webui/src/
registerS
erviceWorker.ts
→
src/webui/src/
s
erviceWorker.ts
View file @
aa316742
// In production, we register a service worker to serve assets from local cache.
// This optional code is used to register a service worker.
// register() is not called by default.
// This lets the app load faster on subsequent visits in production, and gives
// it offline capabilities. However, it also means that developers (and users)
// will only see deployed updates on the 'N+1' visit to a page, since previously
// cached resources are updated in the background.
// will only see deployed updates on subsequent visits to a page, after all the
// existing tabs open on the page have been closed, since previously cached
// resources are updated in the background.
// To learn more about the benefits of this model
, read https://goo.gl/KwvDNy.
//
This link also includes instructions on opting out of this behavior.
// To learn more about the benefits of this model
and instructions on how to
//
opt-in, read https://bit.ly/CRA-PWA
const
isLocalhost
=
Boolean
(
window
.
location
.
hostname
===
'
localhost
'
||
// [::1] is the IPv6 localhost address.
window
.
location
.
hostname
===
'
[::1]
'
||
// 127.0.0.1/8 is considered localhost for IPv4.
window
.
location
.
hostname
.
match
(
/^127
(?:\.(?:
25
[
0-5
]
|2
[
0-4
][
0-9
]
|
[
01
]?[
0-9
][
0-9
]?)){3}
$/
)
// [::1] is the IPv6 localhost address.
window
.
location
.
hostname
===
'
[::1]
'
||
// 127.0.0.1/8 is considered localhost for IPv4.
window
.
location
.
hostname
.
match
(
/^127
(?:\.(?:
25
[
0-5
]
|2
[
0-4
][
0-9
]
|
[
01
]?[
0-9
][
0-9
]?)){3}
$/
)
);
function
registerValidSW
(
swUrl
:
string
):
void
{
type
Config
=
{
onSuccess
?:
(
registration
:
ServiceWorkerRegistration
)
=>
void
;
onUpdate
?:
(
registration
:
ServiceWorkerRegistration
)
=>
void
;
};
function
registerValidSW
(
swUrl
:
string
,
config
?:
Config
):
void
{
navigator
.
serviceWorker
.
register
(
swUrl
)
.
then
(
registration
=>
{
registration
.
onupdatefound
=
():
void
=>
{
const
installingWorker
=
registration
.
installing
;
if
(
installingWorker
)
{
installingWorker
.
onstatechange
=
():
void
=>
{
if
(
installingWorker
.
state
===
'
installed
'
)
{
if
(
navigator
.
serviceWorker
.
controller
)
{
// At this point, the old content will have been purged and
// the fresh content will have been added to the cache.
// It's the perfect time to display a 'New content is
// available; please refresh.' message in your web app.
console
.
log
(
'
New content is available; please refresh.
'
);
}
else
{
// At this point, everything has been precached.
// It's the perfect time to display a
// 'Content is cached for offline use.' message.
console
.
log
(
'
Content is cached for offline use.
'
);
if
(
installingWorker
==
null
)
{
return
;
}
installingWorker
.
onstatechange
=
():
void
=>
{
if
(
installingWorker
.
state
===
'
installed
'
)
{
if
(
navigator
.
serviceWorker
.
controller
)
{
// At this point, the updated precached content has been fetched,
// but the previous service worker will still serve the older
// content until all client tabs are closed.
console
.
log
(
// eslint-disable-line no-console
'
New content is available and will be used when all
'
+
'
tabs for this page are closed. See https://bit.ly/CRA-PWA.
'
);
// Execute callback
if
(
config
&&
config
.
onUpdate
)
{
config
.
onUpdate
(
registration
);
}
}
else
{
// At this point, everything has been precached.
// It's the perfect time to display a
// "Content is cached for offline use." message.
console
.
log
(
'
Content is cached for offline use.
'
);
// eslint-disable-line no-console
// Execute callback
if
(
config
&&
config
.
onSuccess
)
{
config
.
onSuccess
(
registration
);
}
}
}
;
}
}
}
;
};
})
.
catch
(
error
=>
{
console
.
error
(
'
Error during service worker registration:
'
,
error
);
console
.
error
(
'
Error during service worker registration:
'
,
error
);
// eslint-disable-line no-console
});
}
function
checkValidServiceWorker
(
swUrl
:
string
):
void
{
function
checkValidServiceWorker
(
swUrl
:
string
,
config
?:
Config
):
void
{
// Check if the service worker can be found. If it can't reload the page.
fetch
(
swUrl
)
.
then
(
response
=>
{
// Ensure service worker exists, and that we really are getting a JS file.
const
contentType
=
response
.
headers
.
get
(
'
content-type
'
);
if
(
response
.
status
===
404
||
response
.
headers
.
get
(
'
content
-t
ype
'
)
!
.
indexOf
(
'
javascript
'
)
===
-
1
// eslint-disable-line @typescript-eslint/no-non-null-assertion
(
contentType
!=
null
&&
content
T
ype
.
indexOf
(
'
javascript
'
)
===
-
1
)
)
{
// No service worker found. Probably a different app. Reload the page.
navigator
.
serviceWorker
.
ready
.
then
(
registration
=>
{
...
...
@@ -66,39 +87,48 @@ function checkValidServiceWorker(swUrl: string): void {
});
}
else
{
// Service worker found. Proceed as normal.
registerValidSW
(
swUrl
);
registerValidSW
(
swUrl
,
config
);
}
})
.
catch
(()
=>
{
console
.
log
(
console
.
log
(
// eslint-disable-line no-console
'
No internet connection found. App is running in offline mode.
'
);
});
}
export
default
function
register
():
void
{
export
function
register
(
config
?:
Config
):
void
{
if
(
process
.
env
.
NODE_ENV
===
'
production
'
&&
'
serviceWorker
'
in
navigator
)
{
// The URL constructor is available in all browsers that support SW.
const
publicUrl
=
new
URL
(
process
.
env
.
PUBLIC_URL
!
,
// eslint-disable-line @typescript-eslint/no-non-null-assertion
window
.
location
.
toString
()
(
process
as
{
env
:
{
[
key
:
string
]:
string
}
}).
env
.
PUBLIC_URL
,
window
.
location
.
href
);
if
(
publicUrl
.
origin
!==
window
.
location
.
origin
)
{
// Our service worker won't work if PUBLIC_URL is on a different origin
// from what our page is served on. This might happen if a CDN is used to
// serve assets; see https://github.com/facebook
incubator
/create-react-app/issues/2374
// serve assets; see https://github.com/facebook/create-react-app/issues/2374
return
;
}
window
.
addEventListener
(
'
load
'
,
()
=>
{
const
swUrl
=
`
${
process
.
env
.
PUBLIC_URL
}
/service-worker.js`
;
if
(
!
isLocalhost
)
{
// Is not local host. Just register service worker
registerValidSW
(
swUrl
);
if
(
isLocalhost
)
{
// This is running on localhost. Let's check if a service worker still exists or not.
checkValidServiceWorker
(
swUrl
,
config
);
// Add some additional logging to localhost, pointing developers to the
// service worker/PWA documentation.
navigator
.
serviceWorker
.
ready
.
then
(()
=>
{
console
.
log
(
// eslint-disable-line no-console
'
This web app is being served cache-first by a service
'
+
'
worker. To learn more, visit https://bit.ly/CRA-PWA
'
);
});
}
else
{
//
This is running on localhost. Lets check if a service worker still exists or not.
checkValidServiceWorker
(
swUrl
);
//
Is not localhost. Just register service worker
registerValidSW
(
swUrl
,
config
);
}
});
}
...
...
src/webui/src/static/const.ts
View file @
aa316742
...
...
@@ -4,6 +4,7 @@ const METRIC_GROUP_UPDATE_SIZE = 20;
const
MANAGER_IP
=
`/api/v1/nni`
;
const
DOWNLOAD_IP
=
`/logs`
;
const
WEBUIDOC
=
'
https://nni.readthedocs.io/en/latest/Tutorial/WebUI.html
'
;
const
trialJobStatus
=
[
'
UNKNOWN
'
,
'
WAITING
'
,
...
...
@@ -28,51 +29,14 @@ const DRAWEROPTION = {
readOnly
:
true
,
automaticLayout
:
true
};
const
COLUMN_INDEX
=
[
{
name
:
'
Trial No.
'
,
index
:
1
},
{
name
:
'
ID
'
,
index
:
2
},
{
name
:
'
Start Time
'
,
index
:
3
},
{
name
:
'
End Time
'
,
index
:
4
},
{
name
:
'
Duration
'
,
index
:
5
},
{
name
:
'
Status
'
,
index
:
6
},
{
name
:
'
Intermediate result
'
,
index
:
7
},
{
name
:
'
Default
'
,
index
:
8
},
{
name
:
'
Operation
'
,
index
:
10000
}
];
const
OPERATION
=
'
Operation
'
;
// defatult selected column
const
COLUMN
=
[
'
Trial No.
'
,
'
ID
'
,
'
Duration
'
,
'
Status
'
,
'
Default
'
,
'
Operation
'
];
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
'
];
'
Intermediate result
'
,
'
Default
'
,
OPERATION
];
export
{
MANAGER_IP
,
DOWNLOAD_IP
,
trialJobStatus
,
COLUMNPro
,
CONTROLTYPE
,
MONACO
,
COLUMN
,
COLUMN_INDEX
,
DRAWEROPTION
,
MANAGER_IP
,
DOWNLOAD_IP
,
trialJobStatus
,
COLUMNPro
,
WEBUIDOC
,
CONTROLTYPE
,
MONACO
,
COLUMN
,
DRAWEROPTION
,
OPERATION
,
METRIC_GROUP_UPDATE_THRESHOLD
,
METRIC_GROUP_UPDATE_SIZE
,
};
src/webui/src/static/function.ts
View file @
aa316742
import
*
as
JSON5
from
'
json5
'
;
import
axios
from
'
axios
'
;
import
{
message
}
from
'
antd
'
;
import
{
MANAGER_IP
}
from
'
./const
'
;
import
{
MetricDataRecord
,
FinalType
,
TableObj
}
from
'
./interface
'
;
...
...
@@ -25,7 +24,7 @@ const convertDuration = (num: number): string => {
const
hour
=
Math
.
floor
(
num
/
3600
);
const
minute
=
Math
.
floor
(
num
/
60
%
60
);
const
second
=
Math
.
floor
(
num
%
60
);
const
result
=
[
];
const
result
:
string
[]
=
[];
if
(
hour
>
0
)
{
result
.
push
(
`
${
hour
}
h`
);
}
...
...
@@ -38,13 +37,21 @@ const convertDuration = (num: number): string => {
return
result
.
join
(
'
'
);
};
function
parseMetrics
(
metricData
:
string
):
any
{
if
(
metricData
.
includes
(
'
NaN
'
))
{
return
JSON5
.
parse
(
JSON5
.
parse
(
metricData
));
}
else
{
return
JSON
.
parse
(
JSON
.
parse
(
metricData
));
}
}
// get final result value
// draw Accuracy point graph
const
getFinalResult
=
(
final
?:
MetricDataRecord
[]):
number
=>
{
let
acc
;
let
showDefault
=
0
;
if
(
final
)
{
acc
=
JSON
.
parse
(
final
[
final
.
length
-
1
].
data
);
acc
=
parse
Metrics
(
final
[
final
.
length
-
1
].
data
);
if
(
typeof
(
acc
)
===
'
object
'
)
{
if
(
acc
.
default
)
{
showDefault
=
acc
.
default
;
...
...
@@ -59,10 +66,10 @@ const getFinalResult = (final?: MetricDataRecord[]): number => {
};
// get final result value // acc obj
const
getFinal
=
(
final
?:
MetricDataRecord
[]):
any
=>
{
const
getFinal
=
(
final
?:
MetricDataRecord
[]):
FinalType
|
undefined
=>
{
let
showDefault
:
FinalType
;
if
(
final
)
{
showDefault
=
JSON
.
parse
(
final
[
final
.
length
-
1
].
data
);
showDefault
=
parse
Metrics
(
final
[
final
.
length
-
1
].
data
);
if
(
typeof
showDefault
===
'
number
'
)
{
showDefault
=
{
default
:
showDefault
};
}
...
...
@@ -118,33 +125,35 @@ const killJob = (key: number, id: string, status: string, updateList?: Function)
})
.
then
(
res
=>
{
if
(
res
.
status
===
200
)
{
message
.
destroy
();
message
.
success
(
'
Cancel the job successfully
'
);
// TODO: use Message.txt to tooltip
alert
(
'
Cancel the job successfully
'
);
// render the table
if
(
updateList
)
{
updateList
();
// FIXME
}
}
else
{
message
.
error
(
'
fail to cancel the job
'
);
alert
(
'
fail to cancel the job
'
);
}
})
.
catch
(
error
=>
{
if
(
error
.
response
.
status
===
500
)
{
if
(
error
.
response
.
data
.
error
)
{
message
.
error
(
error
.
response
.
data
.
error
);
alert
(
123
);
// message.error(error.response.data.error);
}
else
{
message
.
error
(
'
500 error, fail to cancel the job
'
);
alert
(
234
);
// message.error('500 error, fail to cancel the job');
}
}
});
};
const
filterByStatus
=
(
item
:
TableObj
):
an
y
=>
{
const
filterByStatus
=
(
item
:
TableObj
):
boole
an
=>
{
return
item
.
status
===
'
SUCCEEDED
'
;
};
// a waittiong trial may havn't start time
const
filterDuration
=
(
item
:
TableObj
):
an
y
=>
{
const
filterDuration
=
(
item
:
TableObj
):
boole
an
=>
{
return
item
.
status
!==
'
WAITING
'
;
};
...
...
@@ -170,21 +179,22 @@ const downFile = (content: string, fileName: string): void => {
}
};
function
formatTimestamp
(
timestamp
?:
number
,
placeholder
?:
string
=
'
N/A
'
):
string
{
return
timestamp
?
new
Date
(
timestamp
).
toLocaleString
(
'
en-US
'
)
:
placeholder
;
}
function
parseMetrics
(
metricData
:
string
):
any
{
if
(
metricData
.
includes
(
'
NaN
'
))
{
return
JSON5
.
parse
(
metricData
)
}
else
{
return
JSON
.
parse
(
metricData
)
// function formatTimestamp(timestamp?: number, placeholder?: string = 'N/A'): string {
function
formatTimestamp
(
timestamp
?:
number
,
placeholder
?:
string
):
string
{
if
(
placeholder
===
undefined
)
{
placeholder
=
'
N/A
'
;
}
return
timestamp
?
new
Date
(
timestamp
).
toLocaleString
(
'
en-US
'
)
:
placeholder
;
}
function
metricAccuracy
(
metric
:
MetricDataRecord
):
number
{
const
data
=
parseMetrics
(
metric
.
data
);
return
typeof
data
===
'
number
'
?
data
:
NaN
;
// return typeof data === 'number' ? data : NaN;
if
(
typeof
data
===
'
number
'
)
{
return
data
;
}
else
{
return
data
.
default
;
}
}
function
formatAccuracy
(
accuracy
:
number
):
string
{
...
...
src/webui/src/static/interface.ts
View file @
aa316742
// draw accuracy graph data interface
// draw accuracy graph data
export
interface
interface
TableObj
{
key
:
number
;
sequenceId
:
number
;
...
...
@@ -23,7 +23,8 @@ interface TableRecord {
intermediateCount
:
number
;
accuracy
?:
number
;
latestAccuracy
:
number
|
undefined
;
formattedLatestAccuracy
:
string
;
// format (LATEST/FINAL)
formattedLatestAccuracy
:
string
;
// format (LATEST/FINAL),
accDictionary
:
FinalType
|
undefined
;
}
interface
SearchSpace
{
...
...
@@ -184,8 +185,8 @@ interface EventMap {
}
export
{
TableObj
,
TableRecord
,
Parameter
s
,
ExperimentProfile
,
AccurPoint
,
DetailAccurPoint
,
TooltipFor
Accuracy
,
ParaObj
,
Dimobj
,
FinalType
,
TooltipForIntermediate
,
SearchSpace
,
Intermedia
,
MetricDataRecord
,
TrialJobInfo
,
NNIManagerStatus
,
EventMap
};
TableObj
,
TableRecord
,
SearchSpace
,
FinalType
,
Error
Parameter
,
Parameters
,
AccurPoint
,
DetailAccurPoint
,
TooltipFor
Intermediate
,
TooltipForAccuracy
,
Dimobj
,
ParaObj
,
Intermedia
,
MetricDataRecord
,
TrialJobInfo
,
ExperimentParams
,
ExperimentProfile
,
NNIManagerStatus
,
EventMap
};
\ No newline at end of file
src/webui/src/static/model/experiment.ts
View file @
aa316742
...
...
@@ -79,6 +79,7 @@ class Experiment {
}
get
error
():
string
{
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
if
(
!
this
.
statusField
)
{
throw
Error
(
'
Experiment status not initialized
'
);
}
...
...
src/webui/src/static/model/trial.ts
View file @
aa316742
...
...
@@ -53,10 +53,13 @@ class Trial implements TableObj {
if
(
this
.
accuracy
!==
undefined
)
{
return
this
.
accuracy
;
}
else
if
(
this
.
intermediates
.
length
>
0
)
{
// TODO: support intermeidate result is dict
const
temp
=
this
.
intermediates
[
this
.
intermediates
.
length
-
1
];
if
(
temp
!==
undefined
)
{
return
parseMetrics
(
temp
.
data
);
if
(
typeof
parseMetrics
(
temp
.
data
)
===
'
object
'
)
{
return
parseMetrics
(
temp
.
data
).
default
;
}
else
{
return
parseMetrics
(
temp
.
data
);
}
}
else
{
return
undefined
;
}
...
...
@@ -82,9 +85,11 @@ class Trial implements TableObj {
duration
,
status
:
this
.
info
.
status
,
intermediateCount
:
this
.
intermediates
.
length
,
accuracy
:
this
.
finalAcc
,
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
accuracy
:
this
.
acc
!==
undefined
?
JSON
.
parse
(
this
.
acc
!
.
default
)
:
undefined
,
latestAccuracy
:
this
.
latestAccuracy
,
formattedLatestAccuracy
:
this
.
formatLatestAccuracy
(),
accDictionary
:
this
.
acc
};
}
...
...
src/webui/src/static/style/accuracy.css
deleted
100644 → 0
View file @
3fe117f0
.showMess
{
position
:
absolute
;
left
:
49%
;
top
:
48%
;
font-size
:
13px
;
color
:
#999
;
}
src/webui/src/static/style/compare.scss
View file @
aa316742
.compare
{
width
:
92%
;
table-layout
:
fixed
;
margin
:
0
auto
;
color
:
#333
;
tr
{
line-height
:
30px
;
.compare-modal
{
/* decide modal size */
.ms-Dialog-main
{
max-width
:
70%
;
}
tr
:nth-of-type
(
even
)
{
background-color
:
gainsboro
;
}
.column
{
max-width
:
124px
;
padding-left
:
18px
;
font-weight
:
600
;
}
.value
{
max-width
:
152px
;
padding-right
:
18px
;
text-align
:
left
;
}
.idList
{
font-weight
:
600
;
}
}
.compare-intermediate
{
position
:
relative
;
.compare-yAxis
{
/* compare-md: table style */
&
-table
{
width
:
92%
;
table-layout
:
fixed
;
margin
:
0
auto
;
color
:
#333
;
position
:
absolute
;
top
:
87%
;
left
:
45%
;
tr
{
line-height
:
30px
;
}
tr
:nth-of-type
(
even
)
{
background-color
:
gainsboro
;
}
.column
{
max-width
:
124px
;
padding-left
:
18px
;
font-weight
:
600
;
}
.value
{
max-width
:
152px
;
padding-right
:
18px
;
text-align
:
left
;
}
.idList
{
font-weight
:
600
;
}
}
/* compare-md: intermediate graph style */
&
-intermediate
{
position
:
relative
;
.compare-yAxis
{
color
:
#333
;
position
:
absolute
;
top
:
87%
;
left
:
45%
;
}
}
}
}
\ No newline at end of file
src/webui/src/static/style/control.scss
deleted
100644 → 0
View file @
3fe117f0
.user
{
background
:
#ECF0F5
;
overflow
:
hidden
;
}
.userCon
{
width
:
100%
;
padding-left
:
30px
;
box-sizing
:
border-box
;
}
/* add job */
.addtrial
{
width
:
100%
;
}
div
.addtitle
{
height
:
64px
;
text-align
:
center
;
line-height
:
64px
;
margin-top
:
16px
;
color
:
#333
;
font-size
:
20px
;
}
/* textarea margin 0 auto */
.userInput
{
width
:
80%
;
margin
:
0
auto
;
color
:
#333
;
}
#userInputJob
,
#InputUpdate
{
color
:
#333
;
}
/* two btn style */
.addBtubox
{
width
:
80%
;
height
:
30px
;
margin
:
0
auto
;
margin-top
:
10px
;
}
.addBtubox
Button
{
float
:
right
;
height
:
30px
;
color
:
#fff
;
background-color
:
#3c8dbc
;
border-color
:
#3c8dbc
;
}
.addBtubox
Button
:hover
,
.addBtubox
Button
:focus
{
background-color
:
#3c8dbc
;
border-color
:
#3c8dbc
;
}
/* add job btn style end */
.updatesear
{
line-height
:
64px
;
text-align
:
center
;
margin-top
:
16px
;
color
:
#333
;
font-size
:
20px
;
}
.line
{
font-weight
:
600
;
color
:
rgb
(
60
,
141
,
188
);
padding-right
:
20px
;
}
/* search box style */
.searchbox
{
width
:
100%
;
}
\ No newline at end of file
src/webui/src/static/style/icon.scss
0 → 100644
View file @
aa316742
.iconButtons
{
margin-top
:
12px
;
i
{
font-size
:
16px
;
color
:
#fff
;
}
}
.docIcon
{
i
{
font-size
:
28px
;
}
}
\ No newline at end of file
src/webui/src/static/style/logDrawer.scss
View file @
aa316742
.card-container
>
.ant-tabs-card
>
.ant-tabs-content
{
margin-top
:
-16px
;
}
.card-container
>
.ant-tabs-card
>
.ant-tabs-content
>
.ant-tabs-tabpane
{
background
:
#fff
;
padding
:
16px
;
}
.card-container
>
.ant-tabs-card
>
.ant-tabs-bar
{
border-color
:
#fff
;
}
.card-container
>
.ant-tabs-card
>
.ant-tabs-bar
.ant-tabs-tab
{
border-color
:
transparent
;
background
:
transparent
;
.download
{
button
,
button
:active
,
button
:hover
{
color
:
#fff
;
border
:
none
;
}
.card-container
>
.ant-tabs-card
>
.ant-tabs-bar
.ant-tabs-tab-active
{
border-color
:
#fff
;
background
:
#fff
;
}
.log-tab-body
{
.refresh
{
margin-left
:
10px
;
display
:
none
;
}
}
.logContainer
{
height
:
100%
;
/* office-fabric-ui */
.ms-Panel-main
{
width
:
55%
;
background
:
#fff
;
}
/* log drawer download & close button's row */
.buttons
{
.buttons
{
margin-top
:
16px
;
.close
{
.close
{
text-align
:
right
;
}
}
/*
.logDrawer{
width: 100%;
.ant-drawer-body{
box-sizing: border-box;
-webkit-box-sizing: border-box;
}
}
*/
.ant-drawer-body
{
background
:
#333
;
}
.card-container
>
.ant-tabs-card
>
.ant-tabs-bar
{
border
:
none
;
}
.card-container
>
.ant-tabs-card
>
.ant-tabs-content
>
.ant-tabs-tabpane
{
background-color
:
#333
;
}
.close
{
Button
,
Button
:active
,
Button
:hover
{
background-color
:
#212121
;
color
:
#fff
;
border
:
none
;
}
}
.download
{
Button
,
Button
:active
,
Button
:hover
{
background-color
:
#2772be
;
color
:
#fff
;
border
:
none
;
}
}
.log-tab-body
>
.ant-tabs-card
>
.ant-tabs-bar
.ant-tabs-tab-active
{
background-color
:
#1e1e1e
;
color
:
#fff
;
border
:
none
;
}
.log-tab-body
.ant-tabs-nav
.ant-tabs-tab
:hover
,
.log-tab-body
.ant-tabs-nav
.ant-tabs-tab
{
color
:
#fff
;
border
:
none
;
}
.ant-tabs.ant-tabs-card
>
.ant-tabs-bar
.ant-tabs-tab
{
border
:
none
;
}
.log-tab-body
{
.refresh
{
margin-left
:
10px
;
display
:
none
;
}
.ant-tabs-tab-active
{
.refresh
{
transition
:
0
.3s
;
display
:
inline-block
;
}
.refresh
:hover
{
transform
:
scale
(
1
.2
);
}
}
}
.just-for-log
{
.monaco-editor
{
.line-numbers
{
color
:
#fff
;
}
.current-line
~
.line-numbers
{
color
:
#FFFAF0
;
}
}
.view-lines
{
background-color
:
#1e1e1e
;
.mtk1
{
color
:
#fff
;
}
}
.margin-view-overlays
{
background-color
:
#1e1e1e
;
}
}
src/webui/src/static/style/nav/nav.scss
0 → 100644
View file @
aa316742
$barHeight
:
56px
;
.navOptions
{
.ms-Button-icon
{
color
:
#fff
;
&
:hover
{
color
:
#fff
;
}
}
.ms-Button--commandBar
{
background-color
:
#0071bc
;
user-select
:
none
;
&
:hover
,
&
:active
{
color
:
#fff
;
.ms-Button-icon
{
color
:
#fff
;
}
}
.ms-Button-textContainer
{
color
:
#fff
;
}
.ms-Button-menuIcon
{
color
:
#fff
;
background-color
:
transparent
;
}
}
}
.nav
{
height
:
$barHeight
;
line-height
:
$barHeight
;
/* desktop mode useful*/
.desktop-logo
{
position
:
relative
;
top
:
6px
;
}
&
-refresh
{
position
:
relative
;
display
:
flex
;
}
&
-refresh-num
{
position
:
absolute
;
top
:
-10px
;
left
:
18px
;
color
:
#fff
;
font-size
:
12px
;
}
}
/* overview and detail tabs common style */
a
.common-tabs
{
font-family
:
'Segoe UI'
,
Tahoma
,
Geneva
,
Verdana
,
sans-serif
;
font-size
:
16px
;
color
:
#b8c7ce
;
text-decoration
:
none
;
}
.common-tabs
:visited
,
.high-light
:hover
{
color
:
#fff
;
text-decoration
:
none
;
}
.common-tabs
:hover
,
.high-light
{
color
:
#fff
;
border-bottom
:
1px
solid
#fff
;
}
.left-right-margin
{
margin-left
:
20px
;
margin-right
:
20px
;
}
\ No newline at end of file
src/webui/src/static/style/openRow.scss
View file @
aa316742
...
...
@@ -2,58 +2,39 @@
$color
:
#0071bc
;
/* bg light grey */
$bgColor
:
#f2f2f2
;
.openRowContent
{
.ant-tabs-vertical.ant-tabs-left
.ant-tabs-bar
.ant-tabs-tab
{
text-align
:
left
!
important
;
}
}
.openRowContent
{
.ant-tabs-nav
.ant-tabs-tab
:hover
{
color
:
$color
;
}
}
.openRowContent
{
margin-top
:
15px
;
.openRow
{
width
:
100%
;
background-color
:
$bgColor
;
.card
{
.ant-tabs-vertical
.ant-tabs-bar
.ant-tabs-tab
{
margin
:
0
;
padding
:
8px
12px
;
}
.ant-tabs-nav
.ant-tabs-tab-active
{
color
:
$color
;
font-weight
:
600
;
background
:
#dedede
;
border-left
:
3px
solid
;
}
.ant-tabs-tabpane
{
background
:
#f2f2f2
;
padding
:
12px
12px
12px
0
;
}
margin-bottom
:
10px
;
&
Content
{
width
:
91%
;
margin
:
0
auto
;
min-height
:
150px
;
padding-bottom
:
10px
;
}
}
.trialLog
{
white-space
:
normal
;
color
:
#212121
;
}
#trialLogContent
{
.ant-input-disabled
{
color
:
#212121
;
background-color
:
#fff
;
cursor
:
pointer
;
border
:
none
;
}
.logcontent
{
height
:
100%
;
}
}
#description
{
.bgHyper
{
background-color
:
#fff
;
margin-bottom
:
12px
;
padding
:
0
10px
;
.bgHyper
ul
{
background-color
:
#fff
!
important
;
padding
:
3px
0
5px
0
!
important
;
font-size
:
14px
;
}
.copy
{
margin-top
:
3px
;
Button
{
background-color
:
#ccc
;
color
:
#191919
;
...
...
src/webui/src/static/style/overview.scss
View file @
aa316742
/* new style */
.overMessage
{
height
:
4
30
px
;
height
:
4
46
px
;
}
.overGraph
{
height
:
362px
;
.accuracy
{
width
:
100%
;
height
:
324px
;
}
}
.blockPadding
{
padding
:
10px
20px
;
}
...
...
@@ -23,7 +16,7 @@
}
.padItem
{
padding
-left
:
20px
;
padding
:
10px
20px
0
20px
;
}
.searchSpace
{
...
...
@@ -31,6 +24,8 @@
font-size
:
14px
;
padding
:
15px
0
;
color
:
#212121
;
width
:
95%
;
margin
:
0
auto
;
}
.nowrap
{
...
...
@@ -52,3 +47,13 @@
.link
{
margin-bottom
:
10px
;
}
.info
{
position
:
relative
;
top
:
15px
;
left
:
10px
;
span
{
color
:
#333
;
font-size
:
14px
;
}
}
\ No newline at end of file
Prev
1
…
9
10
11
12
13
14
15
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