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
f1105409
Unverified
Commit
f1105409
authored
Oct 15, 2020
by
liuzhe-lz
Committed by
GitHub
Oct 15, 2020
Browse files
Merge pull request #2959 from microsoft/v1.9
Merge v1.9 back to master
parents
0a6c234a
88a225f8
Changes
80
Hide whitespace changes
Inline
Side-by-side
Showing
20 changed files
with
1254 additions
and
727 deletions
+1254
-727
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
src/webui/src/components/overview/experiment/Command.tsx
src/webui/src/components/overview/experiment/Command.tsx
+63
-0
src/webui/src/components/overview/experiment/basicInfoStyles.ts
...bui/src/components/overview/experiment/basicInfoStyles.ts
+52
-0
src/webui/src/components/overview/overviewConst.ts
src/webui/src/components/overview/overviewConst.ts
+21
-0
src/webui/src/components/overview/table/Details.tsx
src/webui/src/components/overview/table/Details.tsx
+1
-1
src/webui/src/components/overview/table/SuccessTable.tsx
src/webui/src/components/overview/table/SuccessTable.tsx
+34
-18
src/webui/src/components/public-child/ExpandableDetails.tsx
src/webui/src/components/public-child/ExpandableDetails.tsx
+22
-0
src/webui/src/components/public-child/OpenRow.tsx
src/webui/src/components/public-child/OpenRow.tsx
+1
-1
src/webui/src/components/public-child/PaginationTable.tsx
src/webui/src/components/public-child/PaginationTable.tsx
+120
-0
src/webui/src/components/public-child/config/TrialConfigButton.tsx
.../src/components/public-child/config/TrialConfigButton.tsx
+27
-0
src/webui/src/components/public-child/config/TrialConfigPanel.tsx
...i/src/components/public-child/config/TrialConfigPanel.tsx
+104
-0
src/webui/src/components/stateless-component/NNItabs.tsx
src/webui/src/components/stateless-component/NNItabs.tsx
+1
-1
src/webui/src/components/trial-detail/Para.tsx
src/webui/src/components/trial-detail/Para.tsx
+51
-6
src/webui/src/components/trial-detail/TableList.tsx
src/webui/src/components/trial-detail/TableList.tsx
+465
-698
src/webui/src/index.tsx
src/webui/src/index.tsx
+8
-1
src/webui/src/static/const.ts
src/webui/src/static/const.ts
+1
-1
No files found.
src/webui/src/components/overview/count/ExpDuration.tsx
0 → 100644
View file @
f1105409
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 @
f1105409
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 @
f1105409
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 @
f1105409
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 @
f1105409
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
>
);
};
src/webui/src/components/overview/experiment/Command.tsx
0 → 100644
View file @
f1105409
import
React
from
'
react
'
;
import
{
TooltipHost
,
Stack
}
from
'
@fluentui/react
'
;
import
{
EXPERIMENT
}
from
'
../../../static/datamodel
'
;
import
'
../../../static/style/overview/command.scss
'
;
export
const
Command
=
():
any
=>
{
const
clusterMetaData
=
EXPERIMENT
.
profile
.
params
.
clusterMetaData
;
const
tuner
=
EXPERIMENT
.
profile
.
params
.
tuner
;
const
advisor
=
EXPERIMENT
.
profile
.
params
.
advisor
;
const
assessor
=
EXPERIMENT
.
profile
.
params
.
assessor
;
let
title
=
''
;
let
builtinName
=
''
;
let
trialCommand
=
'
unknown
'
;
if
(
tuner
!==
undefined
)
{
title
=
title
.
concat
(
'
Tuner
'
);
if
(
tuner
.
builtinTunerName
!==
undefined
)
{
builtinName
=
builtinName
.
concat
(
tuner
.
builtinTunerName
);
}
}
if
(
advisor
!==
undefined
)
{
title
=
title
.
concat
(
'
/ Assessor
'
);
if
(
advisor
.
builtinAdvisorName
!==
undefined
)
{
builtinName
=
builtinName
.
concat
(
advisor
.
builtinAdvisorName
);
}
}
if
(
assessor
!==
undefined
)
{
title
=
title
.
concat
(
'
/ Addvisor
'
);
if
(
assessor
.
builtinAssessorName
!==
undefined
)
{
builtinName
=
builtinName
.
concat
(
assessor
.
builtinAssessorName
);
}
}
if
(
clusterMetaData
!==
undefined
)
{
for
(
const
item
of
clusterMetaData
)
{
if
(
item
.
key
===
'
command
'
)
{
trialCommand
=
item
.
value
;
}
}
}
return
(
<
div
className
=
'command basic'
>
<
div
className
=
'command1'
>
<
p
>
Training platform
</
p
>
<
div
className
=
'nowrap'
>
{
EXPERIMENT
.
profile
.
params
.
trainingServicePlatform
}
</
div
>
<
p
className
=
'lineMargin'
>
{
title
}
</
p
>
<
div
className
=
'nowrap'
>
{
builtinName
}
</
div
>
</
div
>
<
Stack
className
=
'command2'
>
<
p
>
Log directory
</
p
>
<
div
className
=
'nowrap'
>
<
TooltipHost
content
=
{
EXPERIMENT
.
profile
.
logDir
||
'
unknown
'
}
className
=
'nowrap'
>
{
EXPERIMENT
.
profile
.
logDir
||
'
unknown
'
}
</
TooltipHost
>
</
div
>
<
p
className
=
'lineMargin'
>
Trial command
</
p
>
<
div
className
=
'nowrap'
>
<
TooltipHost
content
=
{
trialCommand
||
'
unknown
'
}
className
=
'nowrap'
>
{
trialCommand
||
'
unknown
'
}
</
TooltipHost
>
</
div
>
</
Stack
>
</
div
>
);
};
src/webui/src/components/overview/experiment/basicInfoStyles.ts
0 → 100644
View file @
f1105409
import
{
FontWeights
,
mergeStyleSets
,
getTheme
}
from
'
@fluentui/react
'
;
const
theme
=
getTheme
();
export
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
}
]
});
src/webui/src/components/overview/overviewConst.ts
0 → 100644
View file @
f1105409
const
itemStyle1
:
React
.
CSSProperties
=
{
width
:
'
75%
'
};
const
itemStyleSucceed
:
React
.
CSSProperties
=
{
width
:
'
28%
'
};
const
itemStyle2
:
React
.
CSSProperties
=
{
height
:
38
};
// top trials entries
const
entriesOption
=
[
{
key
:
'
10
'
,
text
:
'
10
'
},
{
key
:
'
20
'
,
text
:
'
20
'
},
{
key
:
'
30
'
,
text
:
'
30
'
},
{
key
:
'
50
'
,
text
:
'
40
'
},
{
key
:
'
100
'
,
text
:
'
100
'
}
];
export
{
itemStyle1
,
itemStyleSucceed
,
itemStyle2
,
entriesOption
};
src/webui/src/components/overview/Details.tsx
→
src/webui/src/components/overview/
table/
Details.tsx
View file @
f1105409
import
*
as
React
from
'
react
'
;
import
{
DetailsRow
,
IDetailsRowBaseProps
}
from
'
@fluentui/react
'
;
import
OpenRow
from
'
../public-child/OpenRow
'
;
import
OpenRow
from
'
../
../
public-child/OpenRow
'
;
interface
DetailsProps
{
detailsProps
:
IDetailsRowBaseProps
;
...
...
src/webui/src/components/overview/SuccessTable.tsx
→
src/webui/src/components/overview/
table/
SuccessTable.tsx
View file @
f1105409
import
*
as
React
from
'
react
'
;
import
{
DetailsList
,
IDetailsListProps
,
IColumn
}
from
'
@fluentui/react
'
;
import
DefaultMetric
from
'
../public-child/DefaultMetric
'
;
import
DefaultMetric
from
'
../
../
public-child/DefaultMetric
'
;
import
Details
from
'
./Details
'
;
import
{
convertDuration
}
from
'
../../static/function
'
;
import
{
TRIALS
}
from
'
../../static/datamodel
'
;
import
{
DETAILTABS
}
from
'
../stateless-component/NNItabs
'
;
import
'
../../static/style/succTable.scss
'
;
import
'
../../static/style/openRow.scss
'
;
import
{
convertDuration
}
from
'
../../../static/function
'
;
import
{
TRIALS
}
from
'
../../../static/datamodel
'
;
import
{
DETAILTABS
}
from
'
../../stateless-component/NNItabs
'
;
import
'
../../../static/style/succTable.scss
'
;
import
'
../../../static/style/tableStatus.css
'
;
import
'
../../../static/style/openRow.scss
'
;
interface
SuccessTableProps
{
trialIds
:
string
[];
...
...
@@ -15,12 +16,17 @@ interface SuccessTableProps {
interface
SuccessTableState
{
columns
:
IColumn
[];
source
:
Array
<
any
>
;
innerWidth
:
number
;
}
class
SuccessTable
extends
React
.
Component
<
SuccessTableProps
,
SuccessTableState
>
{
constructor
(
props
:
SuccessTableProps
)
{
super
(
props
);
this
.
state
=
{
columns
:
this
.
columns
,
source
:
TRIALS
.
table
(
this
.
props
.
trialIds
)
};
this
.
state
=
{
columns
:
this
.
columns
,
source
:
TRIALS
.
table
(
this
.
props
.
trialIds
),
innerWidth
:
window
.
innerWidth
};
}
private
onRenderRow
:
IDetailsListProps
[
'
onRenderRow
'
]
=
props
=>
{
...
...
@@ -70,8 +76,8 @@ class SuccessTable extends React.Component<SuccessTableProps, SuccessTableState>
name
:
'
Trial No.
'
,
key
:
'
sequenceId
'
,
fieldName
:
'
sequenceId
'
,
// required!
minWidth
:
60
,
maxWidth
:
120
,
minWidth
:
(
window
.
innerWidth
*
0.333
-
150
)
/
5
,
maxWidth
:
(
window
.
innerWidth
*
0.333
-
150
)
/
5
,
isResizable
:
true
,
data
:
'
number
'
,
onColumnClick
:
this
.
onColumnClick
...
...
@@ -80,8 +86,8 @@ class SuccessTable extends React.Component<SuccessTableProps, SuccessTableState>
name
:
'
ID
'
,
key
:
'
id
'
,
fieldName
:
'
id
'
,
minWidth
:
80
,
maxWidth
:
100
,
minWidth
:
(
window
.
innerWidth
*
0.333
-
150
)
/
5
,
maxWidth
:
(
window
.
innerWidth
*
0.333
-
150
)
/
5
,
isResizable
:
true
,
className
:
'
tableHead leftTitle
'
,
data
:
'
string
'
,
...
...
@@ -90,8 +96,8 @@ class SuccessTable extends React.Component<SuccessTableProps, SuccessTableState>
{
name
:
'
Duration
'
,
key
:
'
duration
'
,
minWidth
:
100
,
maxWidth
:
210
,
minWidth
:
(
window
.
innerWidth
*
0.333
-
150
)
/
5
,
maxWidth
:
(
window
.
innerWidth
*
0.333
-
150
)
/
5
,
isResizable
:
true
,
fieldName
:
'
duration
'
,
data
:
'
number
'
,
...
...
@@ -105,8 +111,8 @@ class SuccessTable extends React.Component<SuccessTableProps, SuccessTableState>
{
name
:
'
Status
'
,
key
:
'
status
'
,
minWidth
:
140
,
maxWidth
:
210
,
minWidth
:
(
window
.
innerWidth
*
0.333
-
150
)
/
5
,
maxWidth
:
(
window
.
innerWidth
*
0.333
-
150
)
/
5
,
isResizable
:
true
,
fieldName
:
'
status
'
,
onRender
:
(
item
:
any
):
React
.
ReactNode
=>
{
...
...
@@ -117,8 +123,8 @@ class SuccessTable extends React.Component<SuccessTableProps, SuccessTableState>
name
:
'
Default metric
'
,
key
:
'
accuracy
'
,
fieldName
:
'
accuracy
'
,
minWidth
:
120
,
maxWidth
:
360
,
minWidth
:
(
window
.
innerWidth
*
0.333
-
200
)
/
5
,
//
maxWidth:
(window.innerWidth * 0.333 - 150) / 5
,
isResizable
:
true
,
data
:
'
number
'
,
onColumnClick
:
this
.
onColumnClick
,
...
...
@@ -128,6 +134,17 @@ class SuccessTable extends React.Component<SuccessTableProps, SuccessTableState>
}
];
setInnerWidth
=
():
void
=>
{
this
.
setState
(()
=>
({
innerWidth
:
window
.
innerWidth
}));
};
componentDidMount
():
void
{
window
.
addEventListener
(
'
resize
'
,
this
.
setInnerWidth
);
}
componentWillUnmount
():
void
{
window
.
removeEventListener
(
'
resize
'
,
this
.
setInnerWidth
);
}
componentDidUpdate
(
prevProps
:
SuccessTableProps
):
void
{
if
(
this
.
props
.
trialIds
!==
prevProps
.
trialIds
)
{
const
{
trialIds
}
=
this
.
props
;
...
...
@@ -138,7 +155,6 @@ class SuccessTable extends React.Component<SuccessTableProps, SuccessTableState>
render
():
React
.
ReactNode
{
const
{
columns
,
source
}
=
this
.
state
;
const
isNoneData
=
source
.
length
===
0
?
true
:
false
;
return
(
<
div
id
=
'succTable'
>
<
DetailsList
...
...
src/webui/src/components/public-child/ExpandableDetails.tsx
0 → 100644
View file @
f1105409
import
*
as
React
from
'
react
'
;
import
{
DetailsRow
,
IDetailsRowBaseProps
}
from
'
@fluentui/react
'
;
import
OpenRow
from
'
../public-child/OpenRow
'
;
interface
ExpandableDetailsProps
{
detailsProps
:
IDetailsRowBaseProps
;
isExpand
:
boolean
;
}
class
ExpandableDetails
extends
React
.
Component
<
ExpandableDetailsProps
,
{}
>
{
render
():
React
.
ReactNode
{
const
{
detailsProps
,
isExpand
}
=
this
.
props
;
return
(
<
div
>
<
DetailsRow
{
...
detailsProps
}
/>
{
isExpand
&&
<
OpenRow
trialId
=
{
detailsProps
.
item
.
id
}
/>
}
</
div
>
);
}
}
export
default
ExpandableDetails
;
src/webui/src/components/public-child/OpenRow.tsx
View file @
f1105409
...
...
@@ -8,7 +8,7 @@ import JSONTree from 'react-json-tree';
import
PaiTrialLog
from
'
../public-child/PaiTrialLog
'
;
import
TrialLog
from
'
../public-child/TrialLog
'
;
import
MessageInfo
from
'
../modals/MessageInfo
'
;
import
'
../../static/style/overview.scss
'
;
import
'
../../static/style/overview
/overview
.scss
'
;
import
'
../../static/style/copyParameter.scss
'
;
import
'
../../static/style/openRow.scss
'
;
...
...
src/webui/src/components/public-child/PaginationTable.tsx
0 → 100644
View file @
f1105409
import
*
as
React
from
'
react
'
;
import
{
DetailsList
,
Dropdown
,
Icon
,
IDetailsListProps
,
IDropdownOption
,
IStackTokens
,
Stack
}
from
'
@fluentui/react
'
;
import
ReactPaginate
from
'
react-paginate
'
;
interface
PaginationTableState
{
itemsPerPage
:
number
;
currentPage
:
number
;
itemsOnPage
:
any
[];
// this needs to be stored in state to prevent re-rendering
}
const
horizontalGapStackTokens
:
IStackTokens
=
{
childrenGap
:
20
,
padding
:
10
};
function
_currentTableOffset
(
perPage
:
number
,
currentPage
:
number
,
source
:
any
[]):
number
{
return
perPage
===
-
1
?
0
:
Math
.
min
(
currentPage
,
Math
.
floor
((
source
.
length
-
1
)
/
perPage
))
*
perPage
;
}
function
_obtainPaginationSlice
(
perPage
:
number
,
currentPage
:
number
,
source
:
any
[]):
any
[]
{
if
(
perPage
===
-
1
)
{
return
source
;
}
else
{
const
offset
=
_currentTableOffset
(
perPage
,
currentPage
,
source
);
return
source
.
slice
(
offset
,
offset
+
perPage
);
}
}
class
PaginationTable
extends
React
.
PureComponent
<
IDetailsListProps
,
PaginationTableState
>
{
constructor
(
props
:
IDetailsListProps
)
{
super
(
props
);
this
.
state
=
{
itemsPerPage
:
20
,
currentPage
:
0
,
itemsOnPage
:
[]
};
}
private
_onItemsPerPageSelect
(
event
:
React
.
FormEvent
<
HTMLDivElement
>
,
item
:
IDropdownOption
|
undefined
):
void
{
if
(
item
!==
undefined
)
{
const
{
items
}
=
this
.
props
;
// use current offset to calculate the next `current_page`
const
currentOffset
=
_currentTableOffset
(
this
.
state
.
itemsPerPage
,
this
.
state
.
currentPage
,
items
);
const
itemsPerPage
=
item
.
key
as
number
;
const
currentPage
=
Math
.
floor
(
currentOffset
/
itemsPerPage
);
this
.
setState
({
itemsPerPage
:
itemsPerPage
,
currentPage
:
currentPage
,
itemsOnPage
:
_obtainPaginationSlice
(
itemsPerPage
,
currentPage
,
this
.
props
.
items
)
});
}
}
private
_onPageSelect
(
event
:
any
):
void
{
const
currentPage
=
event
.
selected
;
this
.
setState
({
currentPage
:
currentPage
,
itemsOnPage
:
_obtainPaginationSlice
(
this
.
state
.
itemsPerPage
,
currentPage
,
this
.
props
.
items
)
});
}
componentDidUpdate
(
prevProps
:
IDetailsListProps
):
void
{
if
(
prevProps
.
items
!==
this
.
props
.
items
)
{
this
.
setState
({
itemsOnPage
:
_obtainPaginationSlice
(
this
.
state
.
itemsPerPage
,
this
.
state
.
currentPage
,
this
.
props
.
items
)
});
}
}
render
():
React
.
ReactNode
{
const
{
itemsPerPage
,
itemsOnPage
}
=
this
.
state
;
const
detailListProps
=
{
...
this
.
props
,
items
:
itemsOnPage
};
const
itemsCount
=
this
.
props
.
items
.
length
;
const
pageCount
=
itemsPerPage
===
-
1
?
1
:
Math
.
ceil
(
itemsCount
/
itemsPerPage
);
const
perPageOptions
=
[
{
key
:
10
,
text
:
'
10 items per page
'
},
{
key
:
20
,
text
:
'
20 items per page
'
},
{
key
:
50
,
text
:
'
50 items per page
'
},
{
key
:
-
1
,
text
:
'
All items
'
}
];
return
(
<
div
>
<
DetailsList
{
...
detailListProps
}
/>
<
Stack
horizontal
horizontalAlign
=
'end'
verticalAlign
=
'baseline'
styles
=
{
{
root
:
{
padding
:
10
}
}
}
tokens
=
{
horizontalGapStackTokens
}
>
<
Dropdown
selectedKey
=
{
itemsPerPage
}
options
=
{
perPageOptions
}
onChange
=
{
this
.
_onItemsPerPageSelect
.
bind
(
this
)
}
styles
=
{
{
dropdown
:
{
width
:
150
}
}
}
/>
<
ReactPaginate
previousLabel
=
{
<
Icon
aria
-
hidden
=
{
true
}
iconName
=
'ChevronLeft'
/>
}
nextLabel
=
{
<
Icon
aria
-
hidden
=
{
true
}
iconName
=
'ChevronRight'
/>
}
breakLabel
=
{
'
...
'
}
breakClassName
=
{
'
break
'
}
pageCount
=
{
pageCount
}
marginPagesDisplayed
=
{
2
}
pageRangeDisplayed
=
{
2
}
onPageChange
=
{
this
.
_onPageSelect
.
bind
(
this
)
}
containerClassName
=
{
itemsCount
===
0
?
'
pagination hidden
'
:
'
pagination
'
}
subContainerClassName
=
{
'
pages pagination
'
}
disableInitialCallback
=
{
false
}
activeClassName
=
{
'
active
'
}
/>
</
Stack
>
</
div
>
);
}
}
export
default
PaginationTable
;
src/webui/src/components/public-child/config/TrialConfigButton.tsx
0 → 100644
View file @
f1105409
import
React
,
{
useState
,
useCallback
}
from
'
react
'
;
import
{
DefaultButton
,
Stack
}
from
'
@fluentui/react
'
;
import
TrialConfigPanel
from
'
./TrialConfigPanel
'
;
import
'
../../../static/style/overview/config.scss
'
;
export
const
TrialConfigButton
=
():
any
=>
{
const
[
isShowConfigPanel
,
setShowConfigPanle
]
=
useState
(
false
);
const
[
activeTab
,
setActiveTab
]
=
useState
(
'
1
'
);
const
hideConfigPanel
=
useCallback
(()
=>
setShowConfigPanle
(
false
),
[]);
const
showTrialConfigpPanel
=
useCallback
(()
=>
{
setShowConfigPanle
(
true
);
setActiveTab
(
'
config
'
);
},
[]);
const
showSearchSpacePanel
=
useCallback
(()
=>
{
setShowConfigPanle
(
true
);
setActiveTab
(
'
search space
'
);
},
[]);
return
(
<
React
.
Fragment
>
<
Stack
className
=
'config'
>
<
DefaultButton
text
=
'Config'
onClick
=
{
showTrialConfigpPanel
}
/>
<
DefaultButton
text
=
'Search space'
onClick
=
{
showSearchSpacePanel
}
/>
</
Stack
>
{
isShowConfigPanel
&&
<
TrialConfigPanel
hideConfigPanel
=
{
hideConfigPanel
}
activeTab
=
{
activeTab
}
/>
}
</
React
.
Fragment
>
);
};
src/webui/src/components/public-child/config/TrialConfigPanel.tsx
0 → 100644
View file @
f1105409
import
*
as
React
from
'
react
'
;
import
{
Stack
,
Panel
,
Pivot
,
PivotItem
,
PrimaryButton
}
from
'
@fluentui/react
'
;
import
{
EXPERIMENT
}
from
'
../../../static/datamodel
'
;
import
MonacoEditor
from
'
react-monaco-editor
'
;
import
{
MONACO
}
from
'
../../../static/const
'
;
import
{
convertDuration
}
from
'
../../../static/function
'
;
import
{
prettyStringify
}
from
'
../../../static/json_util
'
;
import
lodash
from
'
lodash
'
;
import
'
../../../static/style/logDrawer.scss
'
;
interface
LogDrawerProps
{
hideConfigPanel
:
()
=>
void
;
activeTab
?:
string
;
}
interface
LogDrawerState
{
panelInnerHeight
:
number
;
}
class
TrialConfigPanel
extends
React
.
Component
<
LogDrawerProps
,
LogDrawerState
>
{
constructor
(
props
:
LogDrawerProps
)
{
super
(
props
);
this
.
state
=
{
panelInnerHeight
:
window
.
innerHeight
};
}
setLogDrawerHeight
():
void
{
this
.
setState
(()
=>
({
panelInnerHeight
:
window
.
innerHeight
}));
}
async
componentDidMount
():
Promise
<
void
>
{
window
.
addEventListener
(
'
resize
'
,
this
.
setLogDrawerHeight
);
}
componentWillUnmount
():
void
{
window
.
removeEventListener
(
'
resize
'
,
this
.
setLogDrawerHeight
);
}
render
():
React
.
ReactNode
{
const
{
hideConfigPanel
,
activeTab
}
=
this
.
props
;
const
{
panelInnerHeight
}
=
this
.
state
;
// [marginTop 16px] + [Search space 46px] +
// button[height: 32px, marginTop: 45px, marginBottom: 25px] + [padding-bottom: 20px]
const
monacoEditorHeight
=
panelInnerHeight
-
184
;
const
blacklist
=
[
'
id
'
,
'
logDir
'
,
'
startTime
'
,
'
endTime
'
,
'
experimentName
'
,
'
searchSpace
'
,
'
trainingServicePlatform
'
];
const
filter
=
(
key
:
string
,
val
:
any
):
any
=>
{
return
blacklist
.
includes
(
key
)
?
undefined
:
val
;
};
const
profile
=
lodash
.
cloneDeep
(
EXPERIMENT
.
profile
);
profile
.
execDuration
=
convertDuration
(
profile
.
execDuration
);
profile
.
params
.
maxExecDuration
=
convertDuration
(
profile
.
params
.
maxExecDuration
);
const
showProfile
=
JSON
.
stringify
(
profile
,
filter
,
2
);
return
(
<
Stack
>
<
Panel
isOpen
=
{
true
}
hasCloseButton
=
{
false
}
isFooterAtBottom
=
{
true
}
isLightDismiss
=
{
true
}
onLightDismissClick
=
{
hideConfigPanel
}
>
<
div
className
=
'log-tab-body'
>
<
Pivot
initialSelectedKey
=
{
activeTab
}
style
=
{
{
minHeight
:
190
,
paddingTop
:
'
16px
'
}
}
>
<
PivotItem
headerText
=
'Search space'
itemKey
=
'search space'
>
<
MonacoEditor
height
=
{
monacoEditorHeight
}
language
=
'json'
theme
=
'vs-light'
value
=
{
prettyStringify
(
EXPERIMENT
.
searchSpace
,
300
,
2
)
}
options
=
{
MONACO
}
/>
</
PivotItem
>
<
PivotItem
headerText
=
'Config'
itemKey
=
'config'
>
<
div
className
=
'profile'
>
<
MonacoEditor
width
=
'100%'
height
=
{
monacoEditorHeight
}
language
=
'json'
theme
=
'vs-light'
value
=
{
showProfile
}
options
=
{
MONACO
}
/>
</
div
>
</
PivotItem
>
</
Pivot
>
</
div
>
<
PrimaryButton
text
=
'Close'
className
=
'configClose'
onClick
=
{
hideConfigPanel
}
/>
</
Panel
>
</
Stack
>
);
}
}
export
default
TrialConfigPanel
;
src/webui/src/components/stateless-component/NNItabs.tsx
View file @
f1105409
...
...
@@ -15,7 +15,7 @@ const DETAILTABS = (
const
NNILOGO
=
(
<
NavLink
to
=
{
'
/oview
'
}
>
<
img
src
=
{
require
(
'
../../static/img/logo
2
.png
'
)
}
alt
=
'NNI logo'
style
=
{
{
height
:
40
}
}
/>
<
img
src
=
{
require
(
'
../../static/img/logo.png
'
)
}
alt
=
'NNI logo'
style
=
{
{
height
:
40
}
}
/>
</
NavLink
>
);
...
...
src/webui/src/components/trial-detail/Para.tsx
View file @
f1105409
import
*
as
d3
from
'
d3
'
;
import
{
Dropdown
,
IDropdownOption
,
Stack
}
from
'
@fluentui/react
'
;
import
{
Dropdown
,
IDropdownOption
,
Stack
,
DefaultButton
}
from
'
@fluentui/react
'
;
import
ParCoords
from
'
parcoord-es
'
;
import
'
parcoord-es/dist/parcoords.css
'
;
import
*
as
React
from
'
react
'
;
...
...
@@ -9,12 +9,16 @@ import { filterByStatus } from '../../static/function';
import
{
TableObj
,
SingleAxis
,
MultipleAxes
}
from
'
../../static/interface
'
;
import
'
../../static/style/button.scss
'
;
import
'
../../static/style/para.scss
'
;
import
ChangeColumnComponent
from
'
../modals/ChangeColumnComponent
'
;
interface
ParaState
{
dimName
:
string
[];
selectedPercent
:
string
;
primaryMetricKey
:
string
;
noChart
:
boolean
;
customizeColumnsDialogVisible
:
boolean
;
availableDimensions
:
string
[];
chosenDimensions
:
string
[];
}
interface
ParaProps
{
...
...
@@ -45,7 +49,10 @@ class Para extends React.Component<ParaProps, ParaState> {
dimName
:
[],
primaryMetricKey
:
'
default
'
,
selectedPercent
:
'
1
'
,
noChart
:
true
noChart
:
true
,
customizeColumnsDialogVisible
:
false
,
availableDimensions
:
[],
chosenDimensions
:
[]
};
}
...
...
@@ -82,11 +89,24 @@ class Para extends React.Component<ParaProps, ParaState> {
}
render
():
React
.
ReactNode
{
const
{
selectedPercent
,
noChart
}
=
this
.
state
;
const
{
selectedPercent
,
noChart
,
customizeColumnsDialogVisible
,
availableDimensions
,
chosenDimensions
}
=
this
.
state
;
return
(
<
div
className
=
'parameter'
>
<
Stack
horizontal
className
=
'para-filter'
horizontalAlign
=
'end'
>
<
DefaultButton
text
=
'Add/Remove axes'
onClick
=
{
():
void
=>
{
this
.
setState
({
customizeColumnsDialogVisible
:
true
});
}
}
styles
=
{
{
root
:
{
marginRight
:
10
}
}
}
/>
<
Dropdown
selectedKey
=
{
selectedPercent
}
onChange
=
{
this
.
percentNum
}
...
...
@@ -101,6 +121,21 @@ class Para extends React.Component<ParaProps, ParaState> {
/>
{
this
.
finalKeysDropdown
()
}
</
Stack
>
{
customizeColumnsDialogVisible
&&
availableDimensions
.
length
>
0
&&
(
<
ChangeColumnComponent
selectedColumns
=
{
chosenDimensions
}
allColumns
=
{
availableDimensions
.
map
(
dim
=>
({
key
:
dim
,
name
:
dim
}))
}
onSelectedChange
=
{
(
selected
:
string
[]):
void
=>
{
this
.
setState
({
chosenDimensions
:
selected
},
()
=>
{
this
.
renderParallelCoordinates
();
});
}
}
onHideDialog
=
{
():
void
=>
{
this
.
setState
({
customizeColumnsDialogVisible
:
false
});
}
}
minSelected
=
{
2
}
/>
)
}
<
div
className
=
'parcoords'
style
=
{
this
.
chartMulineStyle
}
ref
=
{
this
.
paraRef
}
/>
{
noChart
&&
<
div
className
=
'nodata'
>
No data
</
div
>
}
</
div
>
...
...
@@ -143,13 +178,13 @@ class Para extends React.Component<ParaProps, ParaState> {
private
renderParallelCoordinates
():
void
{
const
{
searchSpace
}
=
this
.
props
;
const
percent
=
parseFloat
(
this
.
state
.
selectedPercent
);
const
{
primaryMetricKey
}
=
this
.
state
;
const
{
primaryMetricKey
,
chosenDimensions
}
=
this
.
state
;
const
inferredSearchSpace
=
TRIALS
.
inferredSearchSpace
(
searchSpace
);
const
inferredMetricSpace
=
TRIALS
.
inferredMetricSpace
();
let
convertedTrials
=
this
.
getTrialsAsObjectList
(
inferredSearchSpace
,
inferredMetricSpace
);
const
dimensions
:
[
any
,
any
][]
=
[];
const
dimensions
:
[
string
,
any
][]
=
[];
let
colorDim
:
string
|
undefined
=
undefined
,
colorScale
:
any
=
undefined
;
// treat every axis as numeric to fit for brush
...
...
@@ -213,7 +248,11 @@ class Para extends React.Component<ParaProps, ParaState> {
}
this
.
pcs
.
data
(
convertedTrials
)
.
dimensions
(
dimensions
.
reduce
((
obj
,
entry
)
=>
({
...
obj
,
[
entry
[
0
]]:
entry
[
1
]
}),
{}));
.
dimensions
(
dimensions
.
filter
(([
d
,
_
])
=>
chosenDimensions
.
length
===
0
||
chosenDimensions
.
includes
(
d
))
.
reduce
((
obj
,
entry
)
=>
({
...
obj
,
[
entry
[
0
]]:
entry
[
1
]
}),
{})
);
if
(
firstRun
)
{
this
.
pcs
.
margin
(
this
.
innerChartMargins
)
...
...
@@ -230,6 +269,12 @@ class Para extends React.Component<ParaProps, ParaState> {
if
(
firstRun
)
{
this
.
setState
({
noChart
:
false
});
}
// set new available dims
this
.
setState
({
availableDimensions
:
dimensions
.
map
(
e
=>
e
[
0
]),
chosenDimensions
:
chosenDimensions
.
length
===
0
?
dimensions
.
map
(
e
=>
e
[
0
])
:
chosenDimensions
});
}
private
getTrialsAsObjectList
(
inferredSearchSpace
:
MultipleAxes
,
inferredMetricSpace
:
MultipleAxes
):
{}[]
{
...
...
src/webui/src/components/trial-detail/TableList.tsx
View file @
f1105409
import
React
,
{
lazy
}
from
'
react
'
;
import
axios
from
'
axios
'
;
import
ReactEcharts
from
'
echarts-for-react
'
;
import
{
Stack
,
DefaultButton
,
Dropdown
,
DetailsList
,
IDetailsListProps
,
DetailsListLayoutMode
,
PrimaryButton
,
Modal
,
IDropdownOption
,
IColumn
,
Icon
,
IDropdownOption
,
PrimaryButton
,
Selection
,
SelectionMode
,
IconButton
,
TooltipHost
,
IStackTokens
Stack
,
StackItem
,
TooltipHost
}
from
'
@fluentui/react
'
;
import
ReactPaginate
from
'
react-paginate
'
;
import
{
LineChart
,
blocked
,
copy
}
from
'
../buttons/Icon
'
;
import
{
MANAGER_IP
,
COLUMNPro
}
from
'
../../static/const
'
;
import
{
convertDuration
,
formatTimestamp
,
intermediateGraphOption
,
parseMetrics
}
from
'
../../static/function
'
;
import
React
from
'
react
'
;
import
{
EXPERIMENT
,
TRIALS
}
from
'
../../static/datamodel
'
;
import
{
TableRecord
,
TrialJobInfo
}
from
'
../../static/interface
'
;
const
Details
=
lazy
(()
=>
import
(
'
../overview/Details
'
));
const
ChangeColumnComponent
=
lazy
(()
=>
import
(
'
../modals/ChangeColumnComponent
'
));
const
Compare
=
lazy
(()
=>
import
(
'
../modals/Compare
'
));
const
KillJob
=
lazy
(()
=>
import
(
'
../modals/Killjob
'
));
const
Customize
=
lazy
(()
=>
import
(
'
../modals/CustomizedTrial
'
));
import
{
contentStyles
,
iconButtonStyles
}
from
'
../buttons/ModalTheme
'
;
import
{
convertDuration
,
formatTimestamp
}
from
'
../../static/function
'
;
import
{
TableObj
}
from
'
../../static/interface
'
;
import
'
../../static/style/search.scss
'
;
import
'
../../static/style/tableStatus.css
'
;
import
'
../../static/style/logPath.scss
'
;
import
'
../../static/style/table.scss
'
;
import
'
../../static/style/button.scss
'
;
import
'
../../static/style/logPath.scss
'
;
import
'
../../static/style/openRow.scss
'
;
import
'
../../static/style/pagination.scss
'
;
import
'
../../static/style/search.scss
'
;
import
'
../../static/style/table.scss
'
;
import
'
../../static/style/tableStatus.css
'
;
import
{
blocked
,
copy
,
LineChart
,
tableListIcon
}
from
'
../buttons/Icon
'
;
import
ChangeColumnComponent
from
'
../modals/ChangeColumnComponent
'
;
import
Compare
from
'
../modals/Compare
'
;
import
Customize
from
'
../modals/CustomizedTrial
'
;
import
KillJob
from
'
../modals/Killjob
'
;
import
ExpandableDetails
from
'
../public-child/ExpandableDetails
'
;
import
PaginationTable
from
'
../public-child/PaginationTable
'
;
import
{
Trial
}
from
'
../../static/model/trial
'
;
const
echarts
=
require
(
'
echarts/lib/echarts
'
);
require
(
'
echarts/lib/chart/line
'
);
...
...
@@ -45,749 +43,518 @@ echarts.registerTheme('my_theme', {
color
:
'
#3c8dbc
'
});
const
horizontalGapStackTokens
:
IStackTokens
=
{
childrenGap
:
20
,
padding
:
10
type
SearchOptionType
=
'
id
'
|
'
trialnum
'
|
'
status
'
|
'
parameters
'
;
const
searchOptionLiterals
=
{
id
:
'
ID
'
,
trialnum
:
'
Trial No.
'
,
status
:
'
Status
'
,
parameters
:
'
Parameters
'
};
interface
TableListProps
{
pageSize
:
number
;
tableSource
:
Array
<
TableRecord
>
;
columnList
:
string
[];
// user select columnKeys
changeColumn
:
(
val
:
string
[])
=>
void
;
trialsUpdateBroadcast
:
number
;
}
const
defaultDisplayedColumns
=
[
'
sequenceId
'
,
'
id
'
,
'
duration
'
,
'
status
'
,
'
latestAccuracy
'
];
interface
SortInfo
{
field
:
string
;
isDescend
?:
boolean
;
}
function
_copyAndSort
<
T
>
(
items
:
T
[],
columnKey
:
string
,
isSortedDescending
?:
boolean
):
any
{
const
key
=
columnKey
as
keyof
T
;
return
items
.
slice
(
0
).
sort
(
function
(
a
:
T
,
b
:
T
):
any
{
if
(
a
[
key
]
===
undefined
||
Object
.
is
(
a
[
key
],
NaN
)
||
Object
.
is
(
a
[
key
],
Infinity
)
||
Object
.
is
(
a
[
key
],
-
Infinity
)
||
typeof
a
[
key
]
===
'
object
'
)
{
return
1
;
}
if
(
b
[
key
]
===
undefined
||
Object
.
is
(
b
[
key
],
NaN
)
||
Object
.
is
(
b
[
key
],
Infinity
)
||
Object
.
is
(
b
[
key
],
-
Infinity
)
||
typeof
b
[
key
]
===
'
object
'
)
{
return
-
1
;
}
return
(
isSortedDescending
?
a
[
key
]
<
b
[
key
]
:
a
[
key
]
>
b
[
key
])
?
1
:
-
1
;
});
}
function
_inferColumnTitle
(
columnKey
:
string
):
string
{
if
(
columnKey
===
'
sequenceId
'
)
{
return
'
Trial No.
'
;
}
else
if
(
columnKey
===
'
id
'
)
{
return
'
ID
'
;
}
else
if
(
columnKey
===
'
intermediateCount
'
)
{
return
'
Intermediate results (#)
'
;
}
else
if
(
columnKey
.
startsWith
(
'
space/
'
))
{
return
columnKey
.
split
(
'
/
'
,
2
)[
1
]
+
'
(space)
'
;
}
else
if
(
columnKey
===
'
latestAccuracy
'
)
{
return
'
Default metric
'
;
// to align with the original design
}
else
if
(
columnKey
.
startsWith
(
'
metric/
'
))
{
return
columnKey
.
split
(
'
/
'
,
2
)[
1
]
+
'
(metric)
'
;
}
else
if
(
columnKey
.
startsWith
(
'
_
'
))
{
return
columnKey
;
}
else
{
// camel case to verbose form
const
withSpace
=
columnKey
.
replace
(
/
[
A-Z
]
/g
,
letter
=>
`
${
letter
.
toLowerCase
()}
`
);
return
withSpace
.
charAt
(
0
).
toUpperCase
()
+
withSpace
.
slice
(
1
);
}
}
interface
TableListProps
{
tableSource
:
TableObj
[];
trialsUpdateBroadcast
:
number
;
}
interface
TableListState
{
intermediateOption
:
object
;
modalVisible
:
boolean
;
isObjFinal
:
boolean
;
isShowColumn
:
boolean
;
selectRows
:
Array
<
any
>
;
isShowCompareModal
:
boolean
;
selectedRowKeys
:
string
[]
|
number
[];
intermediateData
:
Array
<
object
>
;
// a trial's intermediate results (include dict)
intermediateId
:
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
>
;
sortMessage
:
SortInfo
;
offset
:
number
;
tablePerPage
:
Array
<
TableRecord
>
;
perPage
:
number
;
currentPage
:
number
;
pageCount
:
number
;
displayedItems
:
any
[];
displayedColumns
:
string
[];
columns
:
IColumn
[];
searchType
:
SearchOptionType
;
searchText
:
string
;
selectedRowIds
:
string
[];
customizeColumnsDialogVisible
:
boolean
;
compareDialogVisible
:
boolean
;
intermediateDialogTrial
:
TableObj
|
undefined
;
copiedTrialId
:
string
|
undefined
;
sortInfo
:
SortInfo
;
}
class
TableList
extends
React
.
Component
<
TableListProps
,
TableListState
>
{
p
ublic
intervalTrialLog
=
10
;
p
ublic
t
rialId
!
:
string
;
p
rivate
_selection
:
Selection
;
p
rivate
_expandedT
rialId
s
:
Set
<
string
>
;
constructor
(
props
:
TableListProps
)
{
super
(
props
);
this
.
state
=
{
intermediateOption
:
{},
modalVisible
:
false
,
isObjFinal
:
false
,
isShowColumn
:
false
,
isShowCompareModal
:
false
,
selectRows
:
[],
selectedRowKeys
:
[],
// close selected trial message after modal closed
intermediateData
:
[],
intermediateId
:
''
,
intermediateOtherKeys
:
[],
isShowCustomizedModal
:
false
,
isCalloutVisible
:
false
,
copyTrialId
:
''
,
intermediateKey
:
'
default
'
,
isExpand
:
false
,
modalIntermediateWidth
:
window
.
innerWidth
,
modalIntermediateHeight
:
window
.
innerHeight
,
tableColumns
:
this
.
initTableColumnList
(
this
.
props
.
columnList
),
allColumnList
:
this
.
getAllColumnKeys
(),
sortMessage
:
{
field
:
''
,
isDescend
:
false
},
offset
:
0
,
tablePerPage
:
[],
perPage
:
20
,
currentPage
:
0
,
pageCount
:
0
,
tableSourceForSort
:
this
.
props
.
tableSource
displayedItems
:
[],
displayedColumns
:
defaultDisplayedColumns
,
columns
:
[],
searchType
:
'
id
'
,
searchText
:
''
,
customizeColumnsDialogVisible
:
false
,
compareDialogVisible
:
false
,
selectedRowIds
:
[],
intermediateDialogTrial
:
undefined
,
copiedTrialId
:
undefined
,
sortInfo
:
{
field
:
''
,
isDescend
:
true
}
};
}
// sort for table column
onColumnClick
=
(
ev
:
React
.
MouseEvent
<
HTMLElement
>
,
getColumn
:
IColumn
):
void
=>
{
const
{
tableColumns
}
=
this
.
state
;
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
;
this
.
_selection
=
new
Selection
({
onSelectionChanged
:
():
void
=>
{
this
.
setState
({
selectedRowIds
:
this
.
_selection
.
getSelection
().
map
(
s
=>
(
s
as
any
).
id
)
});
}
});
this
.
setState
(
{
tableColumns
:
newColumns
,
sortMessage
:
{
field
:
getColumn
.
key
,
isDescend
:
currColumn
.
isSortedDescending
}
},
()
=>
{
this
.
updateData
();
}
);
};
AccuracyColumnConfig
:
any
=
{
name
:
'
Default metric
'
,
className
:
'
leftTitle
'
,
key
:
'
latestAccuracy
'
,
fieldName
:
'
latestAccuracy
'
,
minWidth
:
200
,
maxWidth
:
300
,
isResizable
:
true
,
data
:
'
number
'
,
onColumnClick
:
this
.
onColumnClick
,
onRender
:
(
item
):
React
.
ReactNode
=>
(
<
TooltipHost
content
=
{
item
.
formattedLatestAccuracy
}
>
<
div
className
=
'ellipsis'
>
{
item
.
formattedLatestAccuracy
}
</
div
>
</
TooltipHost
>
)
};
SequenceIdColumnConfig
:
any
=
{
name
:
'
Trial No.
'
,
key
:
'
sequenceId
'
,
fieldName
:
'
sequenceId
'
,
minWidth
:
80
,
maxWidth
:
240
,
className
:
'
tableHead
'
,
data
:
'
number
'
,
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
:
400
,
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
:
200
,
maxWidth
:
400
,
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
:
300
,
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
:
250
,
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
>
};
this
.
_expandedTrialIds
=
new
Set
<
string
>
();
}
showIntermediateModal
=
async
(
record
:
TrialJobInfo
,
event
:
React
.
SyntheticEvent
<
EventTarget
>
):
Promise
<
void
>
=>
{
event
.
preventDefault
();
event
.
stopPropagation
();
const
res
=
await
axios
.
get
(
`
${
MANAGER_IP
}
/metric-data/
${
record
.
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
const
{
intermediateKey
}
=
this
.
state
;
const
otherkeys
:
string
[]
=
[];
const
metricDatas
=
res
.
data
;
if
(
metricDatas
.
length
!==
0
)
{
// just add type=number keys
const
intermediateMetrics
=
parseMetrics
(
metricDatas
[
0
].
data
);
for
(
const
key
in
intermediateMetrics
)
{
if
(
typeof
intermediateMetrics
[
key
]
===
'
number
'
)
{
otherkeys
.
push
(
key
);
}
}
/* Search related methods */
// This functions as the filter for the final trials displayed in the current table
private
_filterTrials
(
trials
:
TableObj
[]):
TableObj
[]
{
const
{
searchText
,
searchType
}
=
this
.
state
;
// search a trial by Trial No. | Trial ID | Parameters | Status
let
searchFilter
=
(
_
:
TableObj
):
boolean
=>
true
;
// eslint-disable-line no-unused-vars
if
(
searchText
.
trim
())
{
if
(
searchType
===
'
id
'
)
{
searchFilter
=
(
trial
):
boolean
=>
trial
.
id
.
toUpperCase
().
includes
(
searchText
.
toUpperCase
());
}
else
if
(
searchType
===
'
trialnum
'
)
{
searchFilter
=
(
trial
):
boolean
=>
trial
.
sequenceId
.
toString
()
===
searchText
;
}
else
if
(
searchType
===
'
status
'
)
{
searchFilter
=
(
trial
):
boolean
=>
trial
.
status
.
toUpperCase
().
includes
(
searchText
.
toUpperCase
());
}
else
if
(
searchType
===
'
parameters
'
)
{
// TODO: support filters like `x: 2` (instead of `'x': 2`)
searchFilter
=
(
trial
):
boolean
=>
JSON
.
stringify
(
trial
.
description
.
parameters
).
includes
(
searchText
);
}
// intermediateArr just store default val
metricDatas
.
map
(
item
=>
{
if
(
item
.
type
===
'
PERIODICAL
'
)
{
const
temp
=
parseMetrics
(
item
.
data
);
if
(
typeof
temp
===
'
object
'
)
{
intermediateArr
.
push
(
temp
[
intermediateKey
]);
}
else
{
intermediateArr
.
push
(
temp
);
}
}
});
const
intermediate
=
intermediateGraphOption
(
intermediateArr
,
record
.
id
);
this
.
setState
({
intermediateData
:
res
.
data
,
// store origin intermediate data for a trial
intermediateOption
:
intermediate
,
intermediateOtherKeys
:
otherkeys
,
intermediateId
:
record
.
id
});
}
this
.
setState
({
modalVisible
:
true
}
);
}
;
return
trials
.
filter
(
searchFilter
);
}
// intermediate button click -> intermediate graph for each trial
// support intermediate is dict
selectOtherKeys
=
(
event
:
React
.
FormEvent
<
HTMLDivElement
>
,
item
?:
IDropdownOption
):
void
=>
{
private
_updateSearchFilterType
(
_event
:
React
.
FormEvent
<
HTMLDivElement
>
,
item
:
IDropdownOption
|
undefined
):
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
=>
{
if
(
intermediateData
[
item
].
type
===
'
PERIODICAL
'
)
{
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
value
=
item
.
key
.
toString
();
if
(
searchOptionLiterals
.
hasOwnProperty
(
value
))
{
this
.
setState
({
searchType
:
value
as
SearchOptionType
},
this
.
_updateTableSource
);
}
const
intermediate
=
intermediateGraphOption
(
intermediateArr
,
intermediateId
);
// re-render
this
.
setState
({
intermediateKey
:
value
,
intermediateOption
:
intermediate
});
}
};
hideIntermediateModal
=
():
void
=>
{
this
.
setState
({
modalVisible
:
false
});
};
hideShowColumnModal
=
():
void
=>
{
this
.
setState
(()
=>
({
isShowColumn
:
false
}));
};
// click add column btn, just show the modal of addcolumn
addColumn
=
():
void
=>
{
// show user select check button
this
.
setState
(()
=>
({
isShowColumn
:
true
}));
};
fillSelectedRowsTostate
=
(
selected
:
number
[]
|
string
[],
selectedRows
:
Array
<
TableRecord
>
):
void
=>
{
this
.
setState
({
selectRows
:
selectedRows
,
selectedRowKeys
:
selected
});
};
}
// open Compare-modal
compareBtn
=
():
void
=>
{
const
{
selectRows
}
=
this
.
state
;
if
(
selectRows
.
length
===
0
)
{
alert
(
'
Please select datas you want to compare!
'
);
}
else
{
this
.
setState
({
isShowCompareModal
:
true
});
}
};
private
_updateSearchText
(
ev
:
React
.
ChangeEvent
<
HTMLInputElement
>
):
void
{
this
.
setState
({
searchText
:
ev
.
target
.
value
},
this
.
_updateTableSource
);
}
// close Compare-modal
hideCompareModal
=
():
void
=>
{
// close modal. clear select rows data, clear selected track
this
.
setState
({
isShowCompareModal
:
false
,
selectedRowKeys
:
[],
selectRows
:
[]
});
};
/* Table basic function related methods */
// open customized trial modal
private
setCustomizedTrial
=
(
trialId
:
string
,
event
:
React
.
SyntheticEvent
<
EventTarget
>
):
void
=>
{
event
.
preventDefault
();
event
.
stopPropagation
();
this
.
setState
({
isShowCustomizedModal
:
true
,
copyTrialId
:
trialId
});
};
private
_onColumnClick
(
ev
:
React
.
MouseEvent
<
HTMLElement
>
,
column
:
IColumn
):
void
{
// handle the click events on table header (do sorting)
const
{
columns
}
=
this
.
state
;
const
newColumns
:
IColumn
[]
=
columns
.
slice
();
const
currColumn
:
IColumn
=
newColumns
.
filter
(
currCol
=>
column
.
key
===
currCol
.
key
)[
0
];
const
isSortedDescending
=
!
currColumn
.
isSortedDescending
;
this
.
setState
(
{
sortInfo
:
{
field
:
column
.
key
,
isDescend
:
isSortedDescending
}
},
this
.
_updateTableSource
);
}
private
closeCustomizedTrial
=
():
void
=>
{
this
.
setState
({
isShowCustomizedModal
:
false
,
copyTrialId
:
''
private
_trialsToTableItems
(
trials
:
TableObj
[]):
any
[]
{
// TODO: use search space and metrics space from TRIALS will cause update issues.
const
searchSpace
=
TRIALS
.
inferredSearchSpace
(
EXPERIMENT
.
searchSpaceNew
);
const
metricSpace
=
TRIALS
.
inferredMetricSpace
();
const
items
=
trials
.
map
(
trial
=>
{
const
ret
=
{
sequenceId
:
trial
.
sequenceId
,
id
:
trial
.
id
,
startTime
:
(
trial
as
Trial
).
info
.
startTime
,
// FIXME: why do we need info here?
endTime
:
(
trial
as
Trial
).
info
.
endTime
,
duration
:
trial
.
duration
,
status
:
trial
.
status
,
intermediateCount
:
trial
.
intermediates
.
length
,
_expandDetails
:
this
.
_expandedTrialIds
.
has
(
trial
.
id
)
// hidden field names should start with `_`
};
for
(
const
[
k
,
v
]
of
trial
.
parameters
(
searchSpace
))
{
ret
[
`space/
${
k
.
baseName
}
`
]
=
v
;
}
for
(
const
[
k
,
v
]
of
trial
.
metrics
(
metricSpace
))
{
ret
[
`metric/
${
k
.
baseName
}
`
]
=
v
;
}
ret
[
'
latestAccuracy
'
]
=
(
trial
as
Trial
).
latestAccuracy
;
ret
[
'
_formattedLatestAccuracy
'
]
=
(
trial
as
Trial
).
formatLatestAccuracy
();
return
ret
;
});
};
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
:
string
[]
=
[];
if
(
!
EXPERIMENT
.
isNestedExp
())
{
if
(
tableSource
.
length
>
0
)
{
const
trialMess
=
TRIALS
.
getTrial
(
tableSource
[
0
].
id
);
const
trial
=
trialMess
.
description
.
parameters
;
const
parameterColumn
:
string
[]
=
Object
.
keys
(
trial
);
parameterColumn
.
forEach
(
value
=>
{
parameterStr
.
push
(
`
${
value
}
(search space)`
);
});
}
const
{
sortInfo
}
=
this
.
state
;
if
(
sortInfo
.
field
!==
''
)
{
return
_copyAndSort
(
items
,
sortInfo
.
field
,
sortInfo
.
isDescend
);
}
else
{
return
items
;
}
// concat trial all final keys and remove dup "default" val, return list
const
finalKeysList
=
TRIALS
.
finalKeys
().
filter
(
item
=>
item
!==
'
default
'
);
return
COLUMNPro
.
concat
(
parameterStr
).
concat
(
finalKeysList
);
};
}
// get IColumn[]
// when user click [Add Column] need to use the function
private
initTableColumnList
=
(
columnList
:
string
[]):
IColumn
[]
=>
{
// const { columnList } = this.props;
const
disabledAddCustomizedTrial
=
[
'
DONE
'
,
'
ERROR
'
,
'
STOPPED
'
].
includes
(
EXPERIMENT
.
status
);
const
showColumn
:
IColumn
[]
=
[];
for
(
const
item
of
columnList
)
{
const
paraColumn
=
item
.
match
(
/
\(
search space
\)
$/
);
let
result
;
if
(
paraColumn
!==
null
)
{
result
=
paraColumn
.
input
;
private
_buildColumnsFromTableItems
(
tableItems
:
any
[]):
IColumn
[]
{
// extra column, for a icon to expand the trial details panel
const
columns
:
IColumn
[]
=
[
{
key
:
'
_expand
'
,
name
:
''
,
onRender
:
(
item
,
index
):
any
=>
{
return
(
<
Icon
aria
-
hidden
=
{
true
}
iconName
=
'ChevronRight'
styles
=
{
{
root
:
{
transition
:
'
all 0.2s
'
,
transform
:
`rotate(
${
item
.
_expandDetails
?
90
:
0
}
deg)`
}
}
}
onClick
=
{
(
event
):
void
=>
{
event
.
stopPropagation
();
const
newItem
:
any
=
{
...
item
,
_expandDetails
:
!
item
.
_expandDetails
};
if
(
newItem
.
_expandDetails
)
{
// preserve to be restored when refreshed
this
.
_expandedTrialIds
.
add
(
newItem
.
id
);
}
else
{
this
.
_expandedTrialIds
.
delete
(
newItem
.
id
);
}
const
newItems
=
[...
this
.
state
.
displayedItems
];
newItems
[
index
as
number
]
=
newItem
;
this
.
setState
({
displayedItems
:
newItems
});
}
}
onMouseDown
=
{
(
e
):
void
=>
{
e
.
stopPropagation
();
}
}
onMouseUp
=
{
(
e
):
void
=>
{
e
.
stopPropagation
();
}
}
/>
);
},
fieldName
:
'
expand
'
,
isResizable
:
false
,
minWidth
:
20
,
maxWidth
:
20
}
switch
(
item
)
{
case
'
Trial No.
'
:
showColumn
.
push
(
this
.
SequenceIdColumnConfig
);
break
;
case
'
ID
'
:
showColumn
.
push
(
this
.
IdColumnConfig
);
break
;
case
'
Start time
'
:
showColumn
.
push
(
this
.
StartTimeColumnConfig
);
break
;
case
'
End time
'
:
showColumn
.
push
(
this
.
EndTimeColumnConfig
);
break
;
case
'
Duration
'
:
showColumn
.
push
(
this
.
DurationColumnConfig
);
break
;
case
'
Status
'
:
showColumn
.
push
(
this
.
StatusColumnConfig
);
break
;
case
'
Intermediate result
'
:
showColumn
.
push
(
this
.
IntermediateCountColumnConfig
);
break
;
case
'
Default
'
:
showColumn
.
push
(
this
.
AccuracyColumnConfig
);
break
;
case
'
Operation
'
:
showColumn
.
push
({
name
:
'
Operation
'
,
key
:
'
operation
'
,
fieldName
:
'
operation
'
,
minWidth
:
160
,
maxWidth
:
200
,
isResizable
:
true
,
className
:
'
detail-table
'
,
onRender
:
(
record
:
any
)
=>
{
const
trialStatus
=
record
.
status
;
const
flag
:
boolean
=
trialStatus
===
'
RUNNING
'
||
trialStatus
===
'
UNKNOWN
'
?
false
:
true
;
return
(
<
Stack
className
=
'detail-button'
horizontal
>
{
/* see intermediate result graph */
}
<
PrimaryButton
className
=
'detail-button-operation'
title
=
'Intermediate'
onClick
=
{
this
.
showIntermediateModal
.
bind
(
this
,
record
)
}
>
{
LineChart
}
</
PrimaryButton
>
{
/* kill job */
}
{
flag
?
(
<
PrimaryButton
className
=
'detail-button-operation'
disabled
=
{
true
}
title
=
'kill'
>
{
blocked
}
</
PrimaryButton
>
)
:
(
<
KillJob
trial
=
{
record
}
/>
)
}
{
/* Add a new trial-customized trial */
}
<
PrimaryButton
className
=
'detail-button-operation'
title
=
'Customized trial'
onClick
=
{
this
.
setCustomizedTrial
.
bind
(
this
,
record
.
id
)
}
disabled
=
{
disabledAddCustomizedTrial
}
>
{
copy
}
</
PrimaryButton
>
</
Stack
>
);
}
});
break
;
case
result
:
// remove SEARCH_SPACE title
// const realItem = item.replace(' (search space)', '');
showColumn
.
push
({
name
:
item
.
replace
(
'
(search space)
'
,
''
),
key
:
item
,
fieldName
:
item
,
minWidth
:
150
,
onRender
:
(
record
:
TableRecord
)
=>
{
const
eachTrial
=
TRIALS
.
getTrial
(
record
.
id
);
return
<
span
>
{
eachTrial
.
description
.
parameters
[
item
.
replace
(
'
(search space)
'
,
''
)]
}
</
span
>;
}
});
break
;
default
:
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
(
<
TooltipHost
content
=
{
other
}
>
<
div
className
=
'ellipsis'
>
{
other
}
</
div
>
</
TooltipHost
>
);
}
});
];
// looking at the first row only for now
for
(
const
k
of
Object
.
keys
(
tableItems
[
0
]))
{
if
(
k
===
'
metric/default
'
)
{
// FIXME: default metric is hacked as latestAccuracy currently
continue
;
}
const
lengths
=
tableItems
.
map
(
item
=>
`
${
item
[
k
]}
`
.
length
);
const
avgLengths
=
lengths
.
reduce
((
a
,
b
)
=>
a
+
b
)
/
lengths
.
length
;
const
columnTitle
=
_inferColumnTitle
(
k
);
const
columnWidth
=
Math
.
max
(
columnTitle
.
length
,
avgLengths
);
// TODO: add blacklist
columns
.
push
({
name
:
columnTitle
,
key
:
k
,
fieldName
:
k
,
minWidth
:
columnWidth
*
13
,
maxWidth
:
columnWidth
*
18
,
isResizable
:
true
,
onColumnClick
:
this
.
_onColumnClick
.
bind
(
this
),
...(
k
===
'
status
'
&&
{
// color status
onRender
:
(
record
):
React
.
ReactNode
=>
(
<
span
className
=
{
`
${
record
.
status
}
commonStyle`
}
>
{
record
.
status
}
</
span
>
)
}),
...((
k
.
startsWith
(
'
metric/
'
)
||
k
.
startsWith
(
'
space/
'
))
&&
{
// show tooltip
onRender
:
(
record
):
React
.
ReactNode
=>
(
<
TooltipHost
content
=
{
record
[
k
]
}
>
<
div
className
=
'ellipsis'
>
{
record
[
k
]
}
</
div
>
</
TooltipHost
>
)
}),
...(
k
===
'
latestAccuracy
'
&&
{
// FIXME: this is ad-hoc
onRender
:
(
record
):
React
.
ReactNode
=>
(
<
TooltipHost
content
=
{
record
.
_formattedLatestAccuracy
}
>
<
div
className
=
'ellipsis'
>
{
record
.
_formattedLatestAccuracy
}
</
div
>
</
TooltipHost
>
)
}),
...([
'
startTime
'
,
'
endTime
'
].
includes
(
k
)
&&
{
onRender
:
(
record
):
React
.
ReactNode
=>
<
span
>
{
formatTimestamp
(
record
[
k
],
'
--
'
)
}
</
span
>
}),
...(
k
===
'
duration
'
&&
{
onRender
:
(
record
):
React
.
ReactNode
=>
(
<
span
className
=
'durationsty'
>
{
convertDuration
(
record
[
k
])
}
</
span
>
)
})
});
}
return
showColumn
;
};
componentDidMount
():
void
{
window
.
addEventListener
(
'
resize
'
,
this
.
onWindowResize
);
this
.
updateData
();
}
// operations column
columns
.
push
({
name
:
'
Operation
'
,
key
:
'
_operation
'
,
fieldName
:
'
operation
'
,
minWidth
:
160
,
maxWidth
:
200
,
isResizable
:
true
,
className
:
'
detail-table
'
,
onRender
:
this
.
_renderOperationColumn
.
bind
(
this
)
});
componentDidUpdate
(
prevProps
:
TableListProps
):
void
{
if
(
this
.
props
.
columnList
!==
prevProps
.
columnList
||
this
.
props
.
tableSource
!==
prevProps
.
tableSource
||
prevProps
.
trialsUpdateBroadcast
!==
this
.
props
.
trialsUpdateBroadcast
)
{
const
{
columnList
}
=
this
.
props
;
this
.
setState
(
{
tableColumns
:
this
.
initTableColumnList
(
columnList
),
allColumnList
:
this
.
getAllColumnKeys
()
},
()
=>
{
this
.
updateData
();
}
);
const
{
sortInfo
}
=
this
.
state
;
for
(
const
column
of
columns
)
{
if
(
column
.
key
===
sortInfo
.
field
)
{
column
.
isSorted
=
true
;
column
.
isSortedDescending
=
sortInfo
.
isDescend
;
}
else
{
column
.
isSorted
=
false
;
column
.
isSortedDescending
=
true
;
}
}
return
columns
;
}
// slice all table data into current page data
updateData
():
void
{
const
tableSource
:
Array
<
TableRecord
>
=
this
.
props
.
tableSource
;
const
{
offset
,
perPage
,
sortMessage
}
=
this
.
state
;
if
(
sortMessage
.
field
!==
''
)
{
tableSource
.
sort
(
function
(
a
,
b
):
any
{
if
(
a
[
sortMessage
.
field
]
===
undefined
||
Object
.
is
(
a
[
sortMessage
.
field
],
NaN
)
||
Object
.
is
(
a
[
sortMessage
.
field
],
Infinity
)
||
Object
.
is
(
a
[
sortMessage
.
field
],
-
Infinity
)
||
typeof
a
[
sortMessage
.
field
]
===
'
object
'
)
{
return
1
;
}
if
(
b
[
sortMessage
.
field
]
===
undefined
||
Object
.
is
(
b
[
sortMessage
.
field
],
NaN
)
||
Object
.
is
(
b
[
sortMessage
.
field
],
Infinity
)
||
Object
.
is
(
b
[
sortMessage
.
field
],
-
Infinity
)
||
typeof
b
[
sortMessage
.
field
]
===
'
object
'
)
{
return
-
1
;
}
return
(
sortMessage
.
isDescend
?
a
[
sortMessage
.
field
]
<
b
[
sortMessage
.
field
]
:
a
[
sortMessage
.
field
]
>
b
[
sortMessage
.
field
])
?
1
:
-
1
;
private
_updateTableSource
():
void
{
// call this method when trials or the computation of trial filter has changed
const
items
=
this
.
_trialsToTableItems
(
this
.
_filterTrials
(
this
.
props
.
tableSource
));
if
(
items
.
length
>
0
)
{
const
columns
=
this
.
_buildColumnsFromTableItems
(
items
);
this
.
setState
({
displayedItems
:
items
,
columns
:
columns
});
}
else
{
this
.
setState
({
displayedItems
:
[],
columns
:
[]
});
}
}
const
tableSlice
=
tableSource
.
slice
(
offset
,
offset
+
perPage
);
const
curPageCount
=
Math
.
ceil
(
tableSource
.
length
/
perPage
);
private
_updateDisplayedColumns
(
displayedColumns
:
string
[]):
void
{
this
.
setState
({
tablePerPage
:
tableSlice
,
pageCount
:
curPageCount
displayedColumns
:
displayedColumns
});
}
// update data when click the page index of pagination
handlePageClick
=
(
evt
:
any
):
void
=>
{
const
selectedPage
=
evt
.
selected
;
const
offset
=
selectedPage
*
this
.
state
.
perPage
;
this
.
setState
(
{
currentPage
:
selectedPage
,
offset
:
offset
},
()
=>
{
this
.
updateData
();
}
private
_renderOperationColumn
(
record
:
any
):
React
.
ReactNode
{
const
runningTrial
:
boolean
=
[
'
RUNNING
'
,
'
UNKNOWN
'
].
includes
(
record
.
status
)
?
false
:
true
;
const
disabledAddCustomizedTrial
=
[
'
DONE
'
,
'
ERROR
'
,
'
STOPPED
'
].
includes
(
EXPERIMENT
.
status
);
return
(
<
Stack
className
=
'detail-button'
horizontal
>
<
PrimaryButton
className
=
'detail-button-operation'
title
=
'Intermediate'
onClick
=
{
():
void
=>
{
const
{
tableSource
}
=
this
.
props
;
const
trial
=
tableSource
.
find
(
trial
=>
trial
.
id
===
record
.
id
)
as
TableObj
;
this
.
setState
({
intermediateDialogTrial
:
trial
});
}
}
>
{
LineChart
}
</
PrimaryButton
>
{
runningTrial
?
(
<
PrimaryButton
className
=
'detail-button-operation'
disabled
=
{
true
}
title
=
'kill'
>
{
blocked
}
</
PrimaryButton
>
)
:
(
<
KillJob
trial
=
{
record
}
/>
)
}
<
PrimaryButton
className
=
'detail-button-operation'
title
=
'Customized trial'
onClick
=
{
():
void
=>
{
this
.
setState
({
copiedTrialId
:
record
.
id
});
}
}
disabled
=
{
disabledAddCustomizedTrial
}
>
{
copy
}
</
PrimaryButton
>
</
Stack
>
);
};
// update per page items when click the dropdown of pagination
updatePerPage
=
(
event
:
React
.
FormEvent
<
HTMLDivElement
>
,
item
:
IDropdownOption
|
undefined
):
void
=>
{
const
{
pageCount
}
=
this
.
state
;
if
(
item
!==
undefined
)
{
const
currentPerPage
=
item
.
key
===
'
all
'
?
this
.
props
.
tableSource
.
length
:
Number
(
item
.
key
);
const
currentPageCount
=
this
.
props
.
tableSource
.
length
<=
currentPerPage
?
1
:
pageCount
;
}
this
.
setState
(
{
perPage
:
currentPerPage
,
offset
:
0
,
currentPage
:
0
,
pageCount
:
currentPageCount
},
()
=>
{
this
.
updateData
();
}
);
componentDidUpdate
(
prevProps
:
TableListProps
):
void
{
if
(
this
.
props
.
tableSource
!==
prevProps
.
tableSource
)
{
this
.
_updateTableSource
();
}
};
}
componentDidMount
():
void
{
this
.
_updateTableSource
();
}
render
():
React
.
ReactNode
{
const
{
intermediateKey
,
modalIntermediateWidth
,
modalIntermediateHeight
,
tableColumns
,
allColumnList
,
isShowColumn
,
modalVisible
,
selectRows
,
isShowCompareModal
,
intermediateOtherKeys
,
isShowCustomizedModal
,
copyTrialId
,
intermediateOption
,
tablePerPage
displayedItems
,
columns
,
searchType
,
customizeColumnsDialogVisible
,
compareDialogVisible
,
displayedColumns
,
selectedRowIds
,
intermediateDialogTrial
,
copiedTrialId
}
=
this
.
state
;
const
{
columnList
}
=
this
.
props
;
const
perPageOptions
=
[
{
key
:
'
10
'
,
text
:
'
10 items per page
'
},
{
key
:
'
20
'
,
text
:
'
20 items per page
'
},
{
key
:
'
50
'
,
text
:
'
50 items per page
'
},
{
key
:
'
all
'
,
text
:
'
All items
'
}
];
return
(
<
Stack
>
<
div
id
=
'tableList'
>
<
DetailsList
columns
=
{
tableColumns
}
items
=
{
tablePerPage
}
setKey
=
'set'
compact
=
{
true
}
onRenderRow
=
{
this
.
onRenderRow
}
layoutMode
=
{
DetailsListLayoutMode
.
justified
}
selectionMode
=
{
SelectionMode
.
multiple
}
selection
=
{
this
.
getSelectedRows
}
/>
<
Stack
horizontal
horizontalAlign
=
'end'
verticalAlign
=
'baseline'
styles
=
{
{
root
:
{
padding
:
10
}
}
}
tokens
=
{
horizontalGapStackTokens
}
>
<
Dropdown
selectedKey
=
{
this
.
state
.
perPage
===
this
.
props
.
tableSource
.
length
?
'
all
'
:
String
(
this
.
state
.
perPage
)
}
options
=
{
perPageOptions
}
onChange
=
{
this
.
updatePerPage
}
styles
=
{
{
dropdown
:
{
width
:
150
}
}
}
/>
<
ReactPaginate
previousLabel
=
{
'
<
'
}
nextLabel
=
{
'
>
'
}
breakLabel
=
{
'
...
'
}
breakClassName
=
{
'
break
'
}
pageCount
=
{
this
.
state
.
pageCount
}
marginPagesDisplayed
=
{
2
}
pageRangeDisplayed
=
{
2
}
onPageChange
=
{
this
.
handlePageClick
}
containerClassName
=
{
this
.
props
.
tableSource
.
length
==
0
?
'
pagination hidden
'
:
'
pagination
'
}
subContainerClassName
=
{
'
pages pagination
'
}
disableInitialCallback
=
{
false
}
activeClassName
=
{
'
active
'
}
forcePage
=
{
this
.
state
.
currentPage
}
/>
</
Stack
>
</
div
>
{
/* Intermediate Result Modal */
}
<
Modal
isOpen
=
{
modalVisible
}
onDismiss
=
{
this
.
hideIntermediateModal
}
containerClassName
=
{
contentStyles
.
container
}
>
<
div
className
=
{
contentStyles
.
header
}
>
<
span
>
Intermediate result
</
span
>
<
IconButton
styles
=
{
iconButtonStyles
}
iconProps
=
{
{
iconName
:
'
Cancel
'
}
}
ariaLabel
=
'Close popup modal'
onClick
=
{
this
.
hideIntermediateModal
as
any
}
<
div
id
=
'tableList'
>
<
Stack
horizontal
className
=
'panelTitle'
style
=
{
{
marginTop
:
10
}
}
>
<
span
style
=
{
{
marginRight
:
12
}
}
>
{
tableListIcon
}
</
span
>
<
span
>
Trial jobs
</
span
>
</
Stack
>
<
Stack
horizontal
className
=
'allList'
>
<
StackItem
grow
=
{
50
}
>
<
DefaultButton
text
=
'Compare'
className
=
'allList-compare'
onClick
=
{
():
void
=>
{
this
.
setState
({
compareDialogVisible
:
true
});
}
}
disabled
=
{
selectedRowIds
.
length
===
0
}
/>
</
div
>
{
intermediateOtherKeys
.
length
>
1
?
(
<
Stack
horizontalAlign
=
'end'
className
=
'selectKeys'
>
</
StackItem
>
<
StackItem
grow
=
{
50
}
>
<
Stack
horizontal
horizontalAlign
=
'end'
className
=
'allList'
>
<
DefaultButton
className
=
'allList-button-gap'
text
=
'Add/Remove columns'
onClick
=
{
():
void
=>
{
this
.
setState
({
customizeColumnsDialogVisible
:
true
});
}
}
/>
<
Dropdown
className
=
'select'
selectedKey
=
{
intermediateKey
}
options
=
{
intermediateOtherKeys
.
map
((
key
,
item
)
=>
{
return
{
key
:
key
,
text
:
intermediateOtherKeys
[
item
]
};
})
}
onChange
=
{
this
.
selectOtherKeys
}
selectedKey
=
{
searchType
}
options
=
{
Object
.
entries
(
searchOptionLiterals
).
map
(([
k
,
v
])
=>
({
key
:
k
,
text
:
v
}))
}
onChange
=
{
this
.
_updateSearchFilterType
.
bind
(
this
)
}
styles
=
{
{
root
:
{
width
:
150
}
}
}
/>
<
input
type
=
'text'
className
=
'allList-search-input'
placeholder
=
{
`Search by
${
[
'
id
'
,
'
trialnum
'
].
includes
(
searchType
)
?
searchOptionLiterals
[
searchType
]
:
searchType
}
`
}
onChange
=
{
this
.
_updateSearchText
.
bind
(
this
)
}
style
=
{
{
width
:
230
}
}
/>
</
Stack
>
)
:
null
}
<
div
className
=
'intermediate-graph'
>
<
ReactEcharts
option
=
{
intermediateOption
}
style
=
{
{
width
:
0.5
*
modalIntermediateWidth
,
height
:
0.7
*
modalIntermediateHeight
,
maxHeight
:
534
,
padding
:
20
}
}
theme
=
'my_theme'
/>
<
div
className
=
'xAxis'
>
#Intermediate result
</
div
>
</
div
>
</
Modal
>
{
/* Add Column Modal */
}
{
isShowColumn
&&
(
</
StackItem
>
</
Stack
>
{
columns
&&
displayedItems
&&
(
<
PaginationTable
columns
=
{
columns
.
filter
(
column
=>
displayedColumns
.
includes
(
column
.
key
)
||
[
'
_expand
'
,
'
_operation
'
].
includes
(
column
.
key
)
)
}
items
=
{
displayedItems
}
compact
=
{
true
}
selection
=
{
this
.
_selection
}
selectionMode
=
{
SelectionMode
.
multiple
}
selectionPreservedOnEmptyClick
=
{
true
}
onRenderRow
=
{
(
props
):
any
=>
{
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
return
<
ExpandableDetails
detailsProps
=
{
props
!
}
isExpand
=
{
props
!
.
item
.
_expandDetails
}
/>;
}
}
/>
)
}
{
compareDialogVisible
&&
(
<
Compare
title
=
'Compare trials'
showDetails
=
{
true
}
trials
=
{
this
.
props
.
tableSource
.
filter
(
trial
=>
selectedRowIds
.
includes
(
trial
.
id
))
}
onHideDialog
=
{
():
void
=>
{
this
.
setState
({
compareDialogVisible
:
false
});
}
}
/>
)
}
{
intermediateDialogTrial
!==
undefined
&&
(
<
Compare
title
=
'Intermediate results'
showDetails
=
{
false
}
trials
=
{
[
intermediateDialogTrial
]
}
onHideDialog
=
{
():
void
=>
{
this
.
setState
({
intermediateDialogTrial
:
undefined
});
}
}
/>
)
}
{
customizeColumnsDialogVisible
&&
(
<
ChangeColumnComponent
hideShowColumnDialog
=
{
this
.
hideShowColumnModal
}
isHideDialog
=
{
!
isShowColumn
}
showColumn
=
{
allColumnList
}
selectedColumn
=
{
columnList
}
changeColumn
=
{
this
.
props
.
changeColumn
}
selectedColumns
=
{
displayedColumns
}
allColumns
=
{
columns
.
filter
(
column
=>
!
column
.
key
.
startsWith
(
'
_
'
))
.
map
(
column
=>
({
key
:
column
.
key
,
name
:
column
.
name
}))
}
onSelectedChange
=
{
this
.
_updateDisplayedColumns
.
bind
(
this
)
}
onHideDialog
=
{
():
void
=>
{
this
.
setState
({
customizeColumnsDialogVisible
:
false
});
}
}
/>
)
}
{
/* compare trials based message */
}
{
isShowCompareModal
&&
<
Compare
compareStacks
=
{
selectRows
}
cancelFunc
=
{
this
.
hideCompareModal
}
/>
}
{
/* clone trial parameters and could submit a customized trial */
}
{
/* Clone a trial and customize a set of new parameters */
}
{
/* visible is done inside because prompt is needed even when the dialog is closed */
}
<
Customize
visible
=
{
isShowCustomizedModal
}
copyTrialId
=
{
copyTrialId
}
closeCustomizeModal
=
{
this
.
closeCustomizedTrial
}
visible
=
{
copiedTrialId
!==
undefined
}
copyTrialId
=
{
copiedTrialId
||
''
}
closeCustomizeModal
=
{
():
void
=>
{
this
.
setState
({
copiedTrialId
:
undefined
});
}
}
/>
</
Stack
>
</
div
>
);
}
}
...
...
src/webui/src/index.tsx
View file @
f1105409
...
...
@@ -5,13 +5,20 @@ import { BrowserRouter as Router, Route, Switch, Redirect } from 'react-router-d
const
Overview
=
lazy
(()
=>
import
(
'
./components/Overview
'
));
const
TrialsDetail
=
lazy
(()
=>
import
(
'
./components/TrialsDetail
'
));
import
'
./index.css
'
;
import
'
./static/style/loading.scss
'
;
import
*
as
serviceWorker
from
'
./serviceWorker
'
;
ReactDOM
.
render
(
<
Router
>
<
App
>
<
Switch
>
<
Suspense
fallback
=
{
null
}
>
<
Suspense
fallback
=
{
<
div
className
=
'loading'
>
<
img
src
=
{
require
(
'
./static/img/loading.gif
'
)
}
/>
</
div
>
}
>
<
Route
path
=
'/oview'
component
=
{
Overview
}
/>
<
Route
path
=
'/detail'
component
=
{
TrialsDetail
}
/>
<
Route
path
=
'/'
render
=
{
():
React
.
ReactNode
=>
<
Redirect
to
=
{
{
pathname
:
'
/oview
'
}
}
/>
}
/>
...
...
src/webui/src/static/const.ts
View file @
f1105409
...
...
@@ -15,7 +15,7 @@ const trialJobStatus = [
'
SYS_CANCELED
'
,
'
EARLY_STOPPED
'
];
const
CONTROLTYPE
=
[
'
SEARCH_SPACE
'
,
'
TRIAL_CONCURRENCY
'
,
'
MAX_EXEC_DURATION
'
];
const
CONTROLTYPE
=
[
'
MAX_EXEC_DURATION
'
,
'
MAX_TRIAL_NUM
'
,
'
TRIAL_CONCURRENCY
'
,
'
SEARCH_SPACE
'
];
const
MONACO
=
{
readOnly
:
true
,
automaticLayout
:
true
,
...
...
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