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
8c2f717d
Unverified
Commit
8c2f717d
authored
Apr 25, 2022
by
Lijiaoa
Committed by
GitHub
Apr 25, 2022
Browse files
Refactor kill job (#4772)
parent
f24c8380
Changes
34
Show whitespace changes
Inline
Side-by-side
Showing
20 changed files
with
485 additions
and
445 deletions
+485
-445
ts/webui/src/App.tsx
ts/webui/src/App.tsx
+122
-108
ts/webui/src/components/common/LogPathChild.tsx
ts/webui/src/components/common/LogPathChild.tsx
+2
-2
ts/webui/src/components/common/OpenRow.tsx
ts/webui/src/components/common/OpenRow.tsx
+0
-1
ts/webui/src/components/experiment/overview/Title.tsx
ts/webui/src/components/experiment/overview/Title.tsx
+1
-1
ts/webui/src/components/experiment/overview/params/BasicInfo.tsx
...i/src/components/experiment/overview/params/BasicInfo.tsx
+9
-9
ts/webui/src/components/experiment/trialdetail/TrialsDetail.tsx
...ui/src/components/experiment/trialdetail/TrialsDetail.tsx
+1
-1
ts/webui/src/components/experiment/trialdetail/chart/DefaultMetricPoint.tsx
...nents/experiment/trialdetail/chart/DefaultMetricPoint.tsx
+1
-1
ts/webui/src/components/experiment/trialdetail/chart/Intermediate.tsx
.../components/experiment/trialdetail/chart/Intermediate.tsx
+1
-1
ts/webui/src/components/experiment/trialdetail/table/TableList.tsx
...src/components/experiment/trialdetail/table/TableList.tsx
+148
-191
ts/webui/src/components/experiment/trialdetail/table/tableFunction/Compare.tsx
...ts/experiment/trialdetail/table/tableFunction/Compare.tsx
+2
-2
ts/webui/src/components/experiment/trialdetail/table/tableFunction/CustomizedTrial.tsx
...iment/trialdetail/table/tableFunction/CustomizedTrial.tsx
+0
-7
ts/webui/src/components/experiment/trialdetail/table/tableFunction/killJob/KillJobDialog.tsx
...trialdetail/table/tableFunction/killJob/KillJobDialog.tsx
+38
-0
ts/webui/src/components/experiment/trialdetail/table/tableFunction/killJob/KillJobIndex.tsx
.../trialdetail/table/tableFunction/killJob/KillJobIndex.tsx
+126
-0
ts/webui/src/components/experiment/trialdetail/table/tableFunction/killTrial/Killjob.tsx
...ent/trialdetail/table/tableFunction/killTrial/Killjob.tsx
+0
-85
ts/webui/src/components/experiment/trialdetail/table/tableFunction/search/GeneralSearch.tsx
.../trialdetail/table/tableFunction/search/GeneralSearch.tsx
+6
-5
ts/webui/src/components/experiment/trialdetail/table/tableFunction/search/Search.tsx
...eriment/trialdetail/table/tableFunction/search/Search.tsx
+7
-7
ts/webui/src/components/experiment/trialdetail/table/tableFunction/search/SearchParameterConditions.tsx
.../table/tableFunction/search/SearchParameterConditions.tsx
+5
-3
ts/webui/src/components/experimentManagement/TrialIdColumn.tsx
...bui/src/components/experimentManagement/TrialIdColumn.tsx
+1
-6
ts/webui/src/components/nav/Nav.tsx
ts/webui/src/components/nav/Nav.tsx
+2
-2
ts/webui/src/components/nav/slideNav/ExperimentSummaryPanel.tsx
...ui/src/components/nav/slideNav/ExperimentSummaryPanel.tsx
+13
-13
No files found.
ts/webui/src/App.tsx
View file @
8c2f717d
...
...
@@ -6,7 +6,6 @@ import NavCon from '@components/nav/Nav';
import
MessageInfo
from
'
@components/common/MessageInfo
'
;
import
{
COLUMN
}
from
'
@static/const
'
;
import
{
isManagerExperimentPage
}
from
'
@static/function
'
;
import
'
@style/App.scss
'
;
import
'
@style/common/common.scss
'
;
import
'
@style/experiment/trialdetail/trialsDetail.scss
'
;
...
...
@@ -16,20 +15,6 @@ echarts.registerTheme('nni_theme', {
color
:
'
#3c8dbc
'
});
interface
AppState
{
interval
:
number
;
columnList
:
string
[];
experimentUpdateBroadcast
:
number
;
trialsUpdateBroadcast
:
number
;
maxDurationUnit
:
string
;
metricGraphMode
:
'
max
'
|
'
min
'
;
// tuner's optimize_mode filed
isillegalFinal
:
boolean
;
expWarningMessage
:
string
;
bestTrialEntries
:
string
;
// for overview page: best trial entreis
isUpdate
:
boolean
;
expandRowIDs
:
Set
<
string
>
;
}
export
const
AppContext
=
React
.
createContext
({
interval
:
10
,
// sendons
columnList
:
COLUMN
,
...
...
@@ -52,12 +37,32 @@ export const AppContext = React.createContext({
// eslint-disable-next-line @typescript-eslint/no-empty-function
updateDetailPage
:
()
=>
{},
// eslint-disable-next-line @typescript-eslint/no-empty-function
changeExpandRowIDs
:
(
_val
:
string
,
_type
?:
string
):
void
=>
{}
changeExpandRowIDs
:
(
_val
:
string
,
_type
?:
string
):
void
=>
{},
// eslint-disable-next-line @typescript-eslint/no-empty-function
startTimer
:
()
=>
{},
// eslint-disable-next-line @typescript-eslint/no-empty-function
closeTimer
:
():
void
=>
{},
// eslint-disable-next-line @typescript-eslint/no-empty-function
refreshDetailTable
:
():
void
=>
{}
});
interface
AppState
{
interval
:
number
;
columnList
:
string
[];
experimentUpdateBroadcast
:
number
;
trialsUpdateBroadcast
:
number
;
maxDurationUnit
:
string
;
metricGraphMode
:
'
max
'
|
'
min
'
;
// tuner's optimize_mode filed
isillegalFinal
:
boolean
;
expWarningMessage
:
string
;
bestTrialEntries
:
string
;
// for overview page: best trial entreis
expandRowIDs
:
Set
<
string
>
;
timerIdList
:
number
[];
}
class
App
extends
React
.
Component
<
{},
AppState
>
{
private
timerId
!
:
number
|
undefined
;
private
firstLoad
:
boolean
=
false
;
// when click refresh selector options
private
timerId
=
0
;
constructor
(
props
:
{})
{
super
(
props
);
this
.
state
=
{
...
...
@@ -70,8 +75,8 @@ class App extends React.Component<{}, AppState> {
isillegalFinal
:
false
,
expWarningMessage
:
''
,
bestTrialEntries
:
'
10
'
,
isUpdate
:
true
,
expandRowIDs
:
new
Set
()
expandRowIDs
:
new
Set
()
,
timerIdList
:
[]
};
}
...
...
@@ -82,73 +87,8 @@ class App extends React.Component<{}, AppState> {
trialsUpdateBroadcast
:
state
.
trialsUpdateBroadcast
+
1
,
metricGraphMode
:
EXPERIMENT
.
optimizeMode
===
'
minimize
'
?
'
min
'
:
'
max
'
}));
this
.
timerId
=
window
.
setTimeout
(
this
.
refresh
,
this
.
state
.
interval
*
100
);
}
changeInterval
=
(
interval
:
number
):
void
=>
{
window
.
clearTimeout
(
this
.
timerId
);
if
(
interval
===
0
)
{
return
;
}
// setState will trigger page refresh at once.
// setState is asyc, interval not update to (this.state.interval) at once.
this
.
setState
({
interval
},
()
=>
{
this
.
firstLoad
=
true
;
this
.
refresh
();
});
};
// TODO: use local storage
changeColumn
=
(
columnList
:
string
[]):
void
=>
{
this
.
setState
({
columnList
:
columnList
});
};
changeExpandRowIDs
=
(
id
:
string
,
type
?:
string
):
void
=>
{
const
currentExpandRowIDs
=
this
.
state
.
expandRowIDs
;
if
(
!
currentExpandRowIDs
.
has
(
id
))
{
currentExpandRowIDs
.
add
(
id
);
}
else
{
if
(
!
(
type
!==
undefined
&&
type
===
'
chart
'
))
{
currentExpandRowIDs
.
delete
(
id
);
}
}
this
.
setState
({
expandRowIDs
:
currentExpandRowIDs
});
};
changeMetricGraphMode
=
(
val
:
'
max
'
|
'
min
'
):
void
=>
{
this
.
setState
({
metricGraphMode
:
val
});
};
// overview best trial module
changeEntries
=
(
entries
:
string
):
void
=>
{
this
.
setState
({
bestTrialEntries
:
entries
});
};
// overview max duration unit
changeMaxDurationUnit
=
(
unit
:
string
):
void
=>
{
this
.
setState
({
maxDurationUnit
:
unit
});
};
updateOverviewPage
=
():
void
=>
{
this
.
setState
(
state
=>
({
experimentUpdateBroadcast
:
state
.
experimentUpdateBroadcast
+
1
}));
};
updateDetailPage
=
():
void
=>
{
this
.
setState
(
state
=>
({
trialsUpdateBroadcast
:
state
.
trialsUpdateBroadcast
+
1
}));
};
shouldComponentUpdate
(
nextProps
:
any
,
nextState
:
AppState
):
boolean
{
if
(
!
(
nextState
.
isUpdate
||
nextState
.
isUpdate
===
undefined
))
{
nextState
.
isUpdate
=
true
;
return
false
;
}
return
true
;
this
.
startTimer
();
}
render
():
React
.
ReactNode
{
...
...
@@ -165,7 +105,7 @@ class App extends React.Component<{}, AppState> {
expandRowIDs
}
=
this
.
state
;
if
(
experimentUpdateBroadcast
===
0
||
trialsUpdateBroadcast
===
0
)
{
return
null
;
// TODO: render a loading page
return
null
;
}
const
errorList
=
[
{
errorWhere
:
TRIALS
.
jobListError
(),
errorMessage
:
TRIALS
.
getJobErrorMessage
()
},
...
...
@@ -187,7 +127,7 @@ class App extends React.Component<{}, AppState> {
</
div
>
<
Stack
className
=
'contentBox'
>
<
Stack
className
=
'content'
>
{
/* search space & config */
}
{
/* search space & config
& dispatcher, nnimanagerlog
*/
}
<
SlideNavBtns
/>
{
/* if api has error field, show error message */
}
{
errorList
.
map
(
...
...
@@ -203,6 +143,7 @@ class App extends React.Component<{}, AppState> {
<
MessageInfo
info
=
{
expWarningMessage
}
typeInfo
=
'warning'
/>
</
div
>
)
}
{
/* <AppContext.Provider */
}
<
AppContext
.
Provider
value
=
{
{
interval
,
...
...
@@ -212,14 +153,17 @@ class App extends React.Component<{}, AppState> {
trialsUpdateBroadcast
,
metricGraphMode
,
maxDurationUnit
,
bestTrialEntries
,
changeMaxDurationUnit
:
this
.
changeMaxDurationUnit
,
changeMetricGraphMode
:
this
.
changeMetricGraphMode
,
bestTrialEntries
,
changeEntries
:
this
.
changeEntries
,
updateOverviewPage
:
this
.
updateOverviewPage
,
updateDetailPage
:
this
.
updateDetailPage
,
expandRowIDs
,
changeExpandRowIDs
:
this
.
changeExpandRowIDs
changeExpandRowIDs
:
this
.
changeExpandRowIDs
,
updateOverviewPage
:
this
.
updateOverviewPage
,
updateDetailPage
:
this
.
updateDetailPage
,
// update current record without fetch api
refreshDetailTable
:
this
.
refreshDetailTable
,
// update record with fetch api
startTimer
:
this
.
startTimer
,
closeTimer
:
this
.
closeTimer
}
}
>
{
this
.
props
.
children
}
...
...
@@ -233,9 +177,6 @@ class App extends React.Component<{}, AppState> {
}
private
refresh
=
async
():
Promise
<
void
>
=>
{
// resolve this question: 10s -> 20s, page refresh twice.
// only refresh this page after clicking the refresh options
if
(
this
.
firstLoad
!==
true
)
{
const
[
experimentUpdated
,
trialsUpdated
]
=
await
Promise
.
all
([
EXPERIMENT
.
update
(),
TRIALS
.
update
()]);
if
(
experimentUpdated
)
{
this
.
setState
(
state
=>
({
experimentUpdateBroadcast
:
state
.
experimentUpdateBroadcast
+
1
}));
...
...
@@ -243,28 +184,101 @@ class App extends React.Component<{}, AppState> {
if
(
trialsUpdated
)
{
this
.
setState
(
state
=>
({
trialsUpdateBroadcast
:
state
.
trialsUpdateBroadcast
+
1
}));
}
}
else
{
this
.
firstLoad
=
false
;
}
// experiment status and /trial-jobs api's status could decide website update
if
([
'
DONE
'
,
'
ERROR
'
,
'
STOPPED
'
,
'
VIEWED
'
].
includes
(
EXPERIMENT
.
status
)
||
TRIALS
.
jobListError
())
{
// experiment finished, refresh once more to ensure consistency
this
.
setState
(()
=>
({
interval
:
0
,
isUpdate
:
false
}));
this
.
setState
(()
=>
({
interval
:
0
}));
this
.
closeTimer
();
return
;
}
this
.
timerId
=
window
.
setTimeout
(
this
.
refresh
,
this
.
state
.
interval
*
1000
);
this
.
startTimer
(
);
};
public
async
lastRefresh
():
Promise
<
void
>
{
public
lastRefresh
=
async
():
Promise
<
void
>
=>
{
await
EXPERIMENT
.
update
();
await
TRIALS
.
update
(
true
);
this
.
setState
(
state
=>
({
experimentUpdateBroadcast
:
state
.
experimentUpdateBroadcast
+
1
,
trialsUpdateBroadcast
:
state
.
trialsUpdateBroadcast
+
1
}));
};
public
changeInterval
=
(
interval
:
number
):
void
=>
{
this
.
setState
(()
=>
({
interval
:
interval
}));
// reset interval val
this
.
closeTimer
();
// close page auto refresh
if
(
interval
!==
0
)
{
this
.
refresh
();
}
};
public
changeColumn
=
(
columnList
:
string
[]):
void
=>
{
this
.
setState
({
columnList
:
columnList
});
};
public
changeExpandRowIDs
=
(
id
:
string
,
type
?:
string
):
void
=>
{
const
currentExpandRowIDs
=
this
.
state
.
expandRowIDs
;
if
(
!
currentExpandRowIDs
.
has
(
id
))
{
currentExpandRowIDs
.
add
(
id
);
}
else
{
if
(
!
(
type
!==
undefined
&&
type
===
'
chart
'
))
{
currentExpandRowIDs
.
delete
(
id
);
}
}
this
.
setState
({
expandRowIDs
:
currentExpandRowIDs
});
};
public
changeMetricGraphMode
=
(
val
:
'
max
'
|
'
min
'
):
void
=>
{
this
.
setState
({
metricGraphMode
:
val
});
};
// overview best trial module
public
changeEntries
=
(
entries
:
string
):
void
=>
{
this
.
setState
({
bestTrialEntries
:
entries
});
};
// overview max duration unit
public
changeMaxDurationUnit
=
(
unit
:
string
):
void
=>
{
this
.
setState
({
maxDurationUnit
:
unit
});
};
public
updateOverviewPage
=
():
void
=>
{
this
.
setState
(
state
=>
({
experimentUpdateBroadcast
:
state
.
experimentUpdateBroadcast
+
1
}));
};
public
updateDetailPage
=
async
():
Promise
<
void
>
=>
{
this
.
setState
(
state
=>
({
trialsUpdateBroadcast
:
state
.
trialsUpdateBroadcast
+
1
}));
};
// fetch api to update table record data
public
refreshDetailTable
=
async
():
Promise
<
void
>
=>
{
await
TRIALS
.
update
(
true
);
this
.
setState
(
state
=>
({
trialsUpdateBroadcast
:
state
.
trialsUpdateBroadcast
+
1
}));
};
// start to refresh page automatically
public
startTimer
=
():
void
=>
{
this
.
timerId
=
window
.
setTimeout
(
this
.
refresh
,
this
.
state
.
interval
*
1000
);
const
storeTimerList
=
this
.
state
.
timerIdList
;
storeTimerList
.
push
(
this
.
timerId
);
this
.
setState
(()
=>
({
timerIdList
:
storeTimerList
}));
};
public
closeTimer
=
():
void
=>
{
const
{
timerIdList
}
=
this
.
state
;
timerIdList
.
forEach
(
item
=>
{
window
.
clearTimeout
(
item
);
});
};
}
export
default
App
;
ts/webui/src/components/common/LogPathChild.tsx
View file @
8c2f717d
...
...
@@ -18,11 +18,11 @@ class LogPathChild extends React.Component<LogpathChildProps, {}> {
<
div
className
=
'logpath'
>
<
span
className
=
'logName'
>
{
logName
}
</
span
>
{
isLink
?
(
<
a
className
=
'
logContent
logHref'
rel
=
'noopener noreferrer'
href
=
{
eachLogpath
}
target
=
'_blank'
>
<
a
className
=
'
fontColor333
logHref'
rel
=
'noopener noreferrer'
href
=
{
eachLogpath
}
target
=
'_blank'
>
{
eachLogpath
}
</
a
>
)
:
(
<
span
className
=
'
logContent
'
>
{
eachLogpath
}
</
span
>
<
span
className
=
'
fontColor333
'
>
{
eachLogpath
}
</
span
>
)
}
</
div
>
);
...
...
ts/webui/src/components/common/OpenRow.tsx
View file @
8c2f717d
...
...
@@ -11,7 +11,6 @@ import TrialLog from './TrialLog';
import
MessageInfo
from
'
./MessageInfo
'
;
import
PanelMonacoEditor
from
'
./PanelMonacoEditor
'
;
import
'
@style/experiment/overview/overview.scss
'
;
import
'
@style/copyParameter.scss
'
;
import
'
@style/openRow.scss
'
;
/**
...
...
ts/webui/src/components/experiment/overview/Title.tsx
View file @
8c2f717d
...
...
@@ -8,7 +8,7 @@ export const Title = (): any => (
{
(
value
):
React
.
ReactNode
=>
(
<
Stack
horizontal
className
=
'panelTitle'
>
<
Icon
iconName
=
{
value
.
icon
}
/>
<
span
>
{
value
.
text
}
</
span
>
<
span
className
=
'fontColor333'
>
{
value
.
text
}
</
span
>
</
Stack
>
)
}
</
TitleContext
.
Consumer
>
...
...
ts/webui/src/components/experiment/overview/params/BasicInfo.tsx
View file @
8c2f717d
...
...
@@ -14,12 +14,12 @@ export const BasicInfo = (): any => {
const
descriptionId
:
string
=
useId
(
'
callout-description
'
);
const
ref
=
React
.
createRef
<
HTMLDivElement
>
();
const
[
isCalloutVisible
,
setCalloutVisible
]
=
useState
(
false
);
const
[
isShowLog
Drawer
,
setShowLog
Drawer
]
=
useState
(
false
);
const
[
isShowLog
Panel
,
setShowLog
Panel
]
=
useState
(
false
);
const
onDismiss
=
useCallback
(()
=>
setCalloutVisible
(
false
),
[]);
const
showCallout
=
useCallback
(()
=>
setCalloutVisible
(
true
),
[]);
const
closeLog
Drawer
=
useCallback
(()
=>
setShowLog
Drawer
(
false
),
[]);
const
ShowLog
Drawer
=
useCallback
(()
=>
setShowLog
Drawer
(
true
),
[]);
const
closeLog
Panel
=
useCallback
(()
=>
setShowLog
Panel
(
false
),
[]);
const
ShowLog
Panel
=
useCallback
(()
=>
setShowLog
Panel
(
true
),
[]);
return
(
<
div
>
...
...
@@ -53,17 +53,17 @@ export const BasicInfo = (): any => {
onDismiss
=
{
onDismiss
}
setInitialFocus
=
{
true
}
>
<
div
className
=
{
styles
.
header
}
>
<
p
className
=
{
`
${
styles
.
title
}
color`
}
id
=
{
labelId
}
>
<
div
className
=
{
`
${
styles
.
header
}
font`
}
>
<
p
className
=
{
`
${
styles
.
title
}
color
333
`
}
id
=
{
labelId
}
>
Error
</
p
>
</
div
>
<
div
className
=
{
styles
.
inner
}
>
<
p
className
=
{
`
${
styles
.
subtext
}
color`
}
id
=
{
descriptionId
}
>
<
div
className
=
{
`
${
styles
.
inner
}
font`
}
>
<
p
className
=
{
`
${
styles
.
subtext
}
color
333
`
}
id
=
{
descriptionId
}
>
{
EXPERIMENT
.
error
}
</
p
>
<
div
className
=
{
styles
.
actions
}
>
<
Link
className
=
{
styles
.
link
}
onClick
=
{
ShowLog
Drawer
}
>
<
Link
className
=
{
styles
.
link
}
onClick
=
{
ShowLog
Panel
}
>
Learn about
</
Link
>
</
div
>
...
...
@@ -92,7 +92,7 @@ export const BasicInfo = (): any => {
</
div
>
</
Stack
>
{
/* learn about click -> default active key is dispatcher. */
}
{
isShowLog
Drawer
?
<
LogPanel
closePanel
=
{
closeLog
Drawer
}
activeTab
=
'dispatcher'
/>
:
null
}
{
isShowLog
Panel
?
<
LogPanel
closePanel
=
{
closeLog
Panel
}
activeTab
=
'dispatcher'
/>
:
null
}
</
div
>
);
};
ts/webui/src/components/experiment/trialdetail/TrialsDetail.tsx
View file @
8c2f717d
...
...
@@ -96,7 +96,7 @@ class TrialsDetail extends React.Component<{}, TrialDetailState> {
</
div
>
{
/* trial table list */
}
<
div
className
=
'detailTable'
style
=
{
{
marginTop
:
10
}
}
>
<
TableList
tableSource
=
{
source
}
updateDetailPage
=
{
this
.
context
.
updateDetailPage
}
/>
<
TableList
tableSource
=
{
source
}
/>
</
div
>
</
React
.
Fragment
>
)
}
...
...
ts/webui/src/components/experiment/trialdetail/chart/DefaultMetricPoint.tsx
View file @
8c2f717d
...
...
@@ -172,7 +172,7 @@ class DefaultPoint extends React.Component<DefaultPointProps, DefaultPointState>
notMerge
=
{
true
}
// update now
onEvents
=
{
onEvents
}
/>
<
div
className
=
'default-metric-noData'
>
{
accNodata
}
</
div
>
<
div
className
=
'default-metric-noData
fontColor333
'
>
{
accNodata
}
</
div
>
</
div
>
</
div
>
);
...
...
ts/webui/src/components/experiment/trialdetail/chart/Intermediate.tsx
View file @
8c2f717d
...
...
@@ -277,7 +277,7 @@ class Intermediate extends React.Component<IntermediateProps, IntermediateState>
notMerge
=
{
true
}
// update now
onEvents
=
{
IntermediateEvents
}
/>
<
div
className
=
'
xAxis
'
>
# Intermediate result
</
div
>
<
div
className
=
'
fontColor333
'
>
# Intermediate result
</
div
>
</
div
>
</
div
>
);
...
...
ts/webui/src/components/experiment/trialdetail/table/TableList.tsx
View file @
8c2f717d
...
...
@@ -10,22 +10,29 @@ import {
DirectionalHint
,
Checkbox
}
from
'
@fluentui/react
'
;
import
{
EXPERIMENT
,
TRIALS
}
from
'
@static/datamode
l
'
;
import
{
Trial
}
from
'
@model/tria
l
'
;
import
{
TOOLTIP_BACKGROUND_COLOR
}
from
'
@static/const
'
;
import
{
convertDuration
,
formatTimestamp
,
copyAndSort
,
parametersType
,
parseMetrics
}
from
'
@static/function
'
;
import
{
EXPERIMENT
,
TRIALS
}
from
'
@static/datamodel
'
;
import
{
convertDuration
,
formatTimestamp
,
copyAndSort
,
parametersType
,
_inferColumnTitle
,
getIntermediateAllKeys
}
from
'
@static/function
'
;
import
{
TableObj
,
SortInfo
,
SearchItems
}
from
'
@static/interface
'
;
import
{
getTrialsBySearchFilters
}
from
'
./tableFunction/search/searchFunction
'
;
import
{
blocked
,
copy
,
LineChart
,
tableListIcon
}
from
'
@components/fluent/Icon
'
;
import
ChangeColumnComponent
from
'
../ChangeColumnComponent
'
;
import
Compare
from
'
./tableFunction/Compare
'
;
import
Search
from
'
./tableFunction/search/Search
'
;
import
Customize
from
'
./tableFunction/CustomizedTrial
'
;
import
TensorboardUI
from
'
./tableFunction/tensorboard/TensorboardUI
'
;
import
Search
from
'
./tableFunction/search/Search
'
;
import
KillJob
from
'
./tableFunction/killTrial/Killjob
'
;
import
ChangeColumnComponent
from
'
../ChangeColumnComponent
'
;
import
Compare
from
'
./tableFunction/Compare
'
;
import
KillJobIndex
from
'
./tableFunction/killJob/KillJobIndex
'
;
import
{
getTrialsBySearchFilters
}
from
'
./tableFunction/search/searchFunction
'
;
import
ExpandableDetails
from
'
@components/common/ExpandableDetails
'
;
import
PaginationTable
from
'
@components/common/PaginationTable
'
;
import
CopyButton
from
'
@components/common/CopyButton
'
;
import
{
Trial
}
from
'
@model/trial
'
;
require
(
'
echarts/lib/chart/line
'
);
require
(
'
echarts/lib/component/tooltip
'
);
...
...
@@ -35,33 +42,8 @@ type SearchOptionType = 'id' | 'trialnum' | 'status' | 'parameters';
const
defaultDisplayedColumns
=
[
'
sequenceId
'
,
'
id
'
,
'
duration
'
,
'
status
'
,
'
latestAccuracy
'
];
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
===
'
message
'
)
{
return
'
Message
'
;
}
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
[];
updateDetailPage
:
()
=>
void
;
}
interface
TableListState
{
...
...
@@ -111,6 +93,133 @@ class TableList extends React.Component<TableListProps, TableListState> {
this
.
_expandedTrialIds
=
new
Set
<
string
>
();
}
componentDidUpdate
(
prevProps
:
TableListProps
):
void
{
if
(
this
.
props
.
tableSource
!==
prevProps
.
tableSource
)
{
this
.
_updateTableSource
();
}
}
componentDidMount
():
void
{
this
.
_updateTableSource
();
}
render
():
React
.
ReactNode
{
const
{
displayedItems
,
columns
,
customizeColumnsDialogVisible
,
compareDialogVisible
,
displayedColumns
,
selectedRowIds
,
intermediateDialogTrial
,
copiedTrialId
,
searchItems
,
intermediateKeyList
}
=
this
.
state
;
return
(
<
div
id
=
'tableList'
>
<
Stack
horizontal
className
=
'panelTitle'
style
=
{
{
marginTop
:
10
}
}
>
<
span
style
=
{
{
marginRight
:
12
}
}
>
{
tableListIcon
}
</
span
>
<
span
className
=
'fontColor333'
>
Trial jobs
</
span
>
</
Stack
>
<
Stack
horizontal
className
=
'allList'
>
<
StackItem
>
<
Stack
horizontal
horizontalAlign
=
'end'
className
=
'allList'
>
<
Search
searchFilter
=
{
searchItems
}
// search filter list
changeSearchFilterList
=
{
this
.
changeSearchFilterList
}
/>
</
Stack
>
</
StackItem
>
<
StackItem
styles
=
{
{
root
:
{
position
:
'
absolute
'
,
right
:
'
0
'
}
}
}
>
<
DefaultButton
className
=
'allList-button-gap'
text
=
'Add/Remove columns'
onClick
=
{
():
void
=>
{
this
.
setState
({
customizeColumnsDialogVisible
:
true
});
}
}
/>
<
DefaultButton
text
=
'Compare'
className
=
'allList-compare'
onClick
=
{
():
void
=>
{
this
.
setState
({
compareDialogVisible
:
true
});
}
}
disabled
=
{
selectedRowIds
.
length
===
0
}
/>
<
TensorboardUI
selectedRowIds
=
{
selectedRowIds
}
changeSelectTrialIds
=
{
this
.
changeSelectTrialIds
}
/>
</
StackItem
>
</
Stack
>
{
columns
&&
displayedItems
&&
(
<
PaginationTable
columns
=
{
columns
.
filter
(
column
=>
displayedColumns
.
includes
(
column
.
key
)
||
[
'
_expand
'
,
'
_operation
'
,
'
_selected
'
].
includes
(
column
.
key
)
)
}
items
=
{
displayedItems
}
compact
=
{
true
}
selectionMode
=
{
0
}
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
});
}
}
changeSelectTrialIds
=
{
this
.
changeSelectTrialIds
}
/>
)
}
{
intermediateDialogTrial
!==
undefined
&&
(
<
Compare
title
=
'Intermediate results'
showDetails
=
{
false
}
trials
=
{
[
intermediateDialogTrial
]
}
intermediateKeyList
=
{
intermediateKeyList
}
onHideDialog
=
{
():
void
=>
{
this
.
setState
({
intermediateDialogTrial
:
undefined
});
}
}
/>
)
}
{
customizeColumnsDialogVisible
&&
(
<
ChangeColumnComponent
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
});
}
}
whichComponent
=
'table'
/>
)
}
{
/* 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
=
{
copiedTrialId
!==
undefined
}
copyTrialId
=
{
copiedTrialId
||
''
}
closeCustomizeModal
=
{
():
void
=>
{
this
.
setState
({
copiedTrialId
:
undefined
});
}
}
/>
</
div
>
);
}
/* Table basic function related methods */
private
_onColumnClick
(
ev
:
React
.
MouseEvent
<
HTMLElement
>
,
column
:
IColumn
):
void
{
...
...
@@ -288,6 +397,9 @@ class TableList extends React.Component<TableListProps, TableListState> {
...(
k
===
'
status
'
&&
{
// color status
onRender
:
(
record
):
React
.
ReactNode
=>
(
// kill 成功之后,重新拉取的数据如果有 endtime 字段,会马上render出user_cancel
// 的状态,反之,没有这个字段,table依然是部分刷新,只刷新duration,不会
// 刷新 status
<
span
className
=
{
`
${
record
.
status
}
commonStyle`
}
>
{
record
.
status
}
</
span
>
)
}),
...
...
@@ -434,7 +546,7 @@ class TableList extends React.Component<TableListProps, TableListState> {
onClick
=
{
():
void
=>
{
const
{
tableSource
}
=
this
.
props
;
const
trial
=
tableSource
.
find
(
trial
=>
trial
.
id
===
record
.
id
)
as
TableObj
;
const
intermediateKeyListResult
=
this
.
getIntermediateAllKeys
(
trial
);
const
intermediateKeyListResult
=
getIntermediateAllKeys
(
trial
);
this
.
setState
({
intermediateDialogTrial
:
trial
,
intermediateKeyList
:
intermediateKeyListResult
...
...
@@ -448,7 +560,7 @@ class TableList extends React.Component<TableListProps, TableListState> {
{
blocked
}
</
PrimaryButton
>
)
:
(
<
KillJob
trial
=
{
record
}
/>
<
KillJob
Index
trial
Id
=
{
record
.
id
}
/>
)
}
<
PrimaryButton
className
=
'detail-button-operation'
...
...
@@ -464,166 +576,11 @@ class TableList extends React.Component<TableListProps, TableListState> {
);
}
p
rivate
changeSearchFilterList
=
(
arr
:
Array
<
SearchItems
>
):
void
=>
{
p
ublic
changeSearchFilterList
=
(
arr
:
Array
<
SearchItems
>
):
void
=>
{
this
.
setState
(()
=>
({
searchItems
:
arr
}));
};
private
getIntermediateAllKeys
=
(
intermediateDialogTrial
:
any
):
string
[]
=>
{
let
intermediateAllKeysList
:
string
[]
=
[];
if
(
intermediateDialogTrial
!
.
intermediateMetrics
!==
undefined
&&
intermediateDialogTrial
!
.
intermediateMetrics
[
0
]
)
{
const
parsedMetric
=
parseMetrics
(
intermediateDialogTrial
!
.
intermediateMetrics
[
0
].
data
);
if
(
parsedMetric
!==
undefined
&&
typeof
parsedMetric
===
'
object
'
)
{
const
allIntermediateKeys
:
string
[]
=
[];
// just add type=number keys
for
(
const
key
in
parsedMetric
)
{
if
(
typeof
parsedMetric
[
key
]
===
'
number
'
)
{
allIntermediateKeys
.
push
(
key
);
}
}
intermediateAllKeysList
=
allIntermediateKeys
;
}
}
if
(
intermediateAllKeysList
.
includes
(
'
default
'
)
&&
intermediateAllKeysList
[
0
]
!==
'
default
'
)
{
intermediateAllKeysList
=
intermediateAllKeysList
.
filter
(
item
=>
item
!==
'
default
'
);
intermediateAllKeysList
.
unshift
(
'
default
'
);
}
return
intermediateAllKeysList
;
};
componentDidUpdate
(
prevProps
:
TableListProps
):
void
{
if
(
this
.
props
.
tableSource
!==
prevProps
.
tableSource
)
{
this
.
_updateTableSource
();
}
}
componentDidMount
():
void
{
this
.
_updateTableSource
();
}
render
():
React
.
ReactNode
{
const
{
displayedItems
,
columns
,
customizeColumnsDialogVisible
,
compareDialogVisible
,
displayedColumns
,
selectedRowIds
,
intermediateDialogTrial
,
copiedTrialId
,
searchItems
,
intermediateKeyList
}
=
this
.
state
;
return
(
<
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
>
<
Stack
horizontal
horizontalAlign
=
'end'
className
=
'allList'
>
<
Search
searchFilter
=
{
searchItems
}
// search filter list
changeSearchFilterList
=
{
this
.
changeSearchFilterList
}
updatePage
=
{
this
.
props
.
updateDetailPage
}
/>
</
Stack
>
</
StackItem
>
<
StackItem
styles
=
{
{
root
:
{
position
:
'
absolute
'
,
right
:
'
0
'
}
}
}
>
<
DefaultButton
className
=
'allList-button-gap'
text
=
'Add/Remove columns'
onClick
=
{
():
void
=>
{
this
.
setState
({
customizeColumnsDialogVisible
:
true
});
}
}
/>
<
DefaultButton
text
=
'Compare'
className
=
'allList-compare'
onClick
=
{
():
void
=>
{
this
.
setState
({
compareDialogVisible
:
true
});
}
}
disabled
=
{
selectedRowIds
.
length
===
0
}
/>
<
TensorboardUI
selectedRowIds
=
{
selectedRowIds
}
changeSelectTrialIds
=
{
this
.
changeSelectTrialIds
}
/>
</
StackItem
>
</
Stack
>
{
columns
&&
displayedItems
&&
(
<
PaginationTable
columns
=
{
columns
.
filter
(
column
=>
displayedColumns
.
includes
(
column
.
key
)
||
[
'
_expand
'
,
'
_operation
'
,
'
_selected
'
].
includes
(
column
.
key
)
)
}
items
=
{
displayedItems
}
compact
=
{
true
}
selectionMode
=
{
0
}
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
});
}
}
changeSelectTrialIds
=
{
this
.
changeSelectTrialIds
}
/>
)
}
{
intermediateDialogTrial
!==
undefined
&&
(
<
Compare
title
=
'Intermediate results'
showDetails
=
{
false
}
trials
=
{
[
intermediateDialogTrial
]
}
intermediateKeyList
=
{
intermediateKeyList
}
onHideDialog
=
{
():
void
=>
{
this
.
setState
({
intermediateDialogTrial
:
undefined
});
}
}
/>
)
}
{
customizeColumnsDialogVisible
&&
(
<
ChangeColumnComponent
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
});
}
}
whichComponent
=
'table'
/>
)
}
{
/* 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
=
{
copiedTrialId
!==
undefined
}
copyTrialId
=
{
copiedTrialId
||
''
}
closeCustomizeModal
=
{
():
void
=>
{
this
.
setState
({
copiedTrialId
:
undefined
});
}
}
/>
</
div
>
);
}
}
export
default
TableList
;
ts/webui/src/components/experiment/trialdetail/table/tableFunction/Compare.tsx
View file @
8c2f717d
...
...
@@ -194,7 +194,7 @@ class Compare extends React.Component<CompareProps, CompareState> {
const
metricKeys
=
this
.
_overlapKeys
(
items
.
map
(
item
=>
item
.
metrics
));
return
(
<
table
className
=
{
`compare-modal-table
${
scrollClass
}
`
}
>
<
table
className
=
{
`compare-modal-table
${
scrollClass
}
fontColor333
`
}
>
<
tbody
>
{
this
.
_renderRow
(
'
id
'
,
'
ID
'
,
'
value idList
'
,
items
,
item
=>
item
.
id
)
}
{
this
.
_renderRow
(
'
trialnum
'
,
'
Trial No.
'
,
'
value
'
,
items
,
item
=>
item
.
sequenceId
.
toString
())
}
...
...
@@ -281,7 +281,7 @@ class Compare extends React.Component<CompareProps, CompareState> {
)
:
null
}
<
Stack
className
=
'compare-modal-intermediate'
>
{
this
.
_intermediates
(
items
)
}
<
Stack
className
=
'compare-yAxis'
>
# Intermediate result
</
Stack
>
<
Stack
className
=
'compare-yAxis
fontColor333
'
>
# Intermediate result
</
Stack
>
</
Stack
>
{
showDetails
&&
<
Stack
>
{
this
.
_columns
(
items
)
}
</
Stack
>
}
</
div
>
...
...
ts/webui/src/components/experiment/trialdetail/table/tableFunction/CustomizedTrial.tsx
View file @
8c2f717d
...
...
@@ -205,13 +205,6 @@ class Customize extends React.Component<CustomizeProps, CustomizeState> {
</
StackItem
>
</
Stack
>
))
}
{
/* disable [tag] because we havn't support */
}
{
/* <Stack key="tag" horizontal className="hyper-form tag-input">
<StackItem grow={9} className="title">Tag</StackItem>
<StackItem grow={15} className="inputs">
<input type="text" value='Customized' />
</StackItem>
</Stack> */
}
</
form
>
<
DialogFooter
>
<
PrimaryButton
text
=
'Submit'
onClick
=
{
this
.
addNewTrial
}
/>
...
...
ts/webui/src/components/experiment/trialdetail/table/tableFunction/killJob/KillJobDialog.tsx
0 → 100644
View file @
8c2f717d
import
React
from
'
react
'
;
import
PropTypes
from
'
prop-types
'
;
import
{
PrimaryButton
,
Dialog
,
DialogType
,
DialogFooter
}
from
'
@fluentui/react
'
;
function
KillJobDialog
(
props
):
any
{
const
{
onHideDialog
,
trialId
,
isError
}
=
props
;
const
dialogContentProps
=
{
type
:
DialogType
.
normal
,
title
:
'
Kill job
'
};
return
(
<
Dialog
hidden
=
{
false
}
dialogContentProps
=
{
dialogContentProps
}
modalProps
=
{
{
className
:
'
dialog
'
}
}
>
{
isError
.
isError
?
(
<
div
>
<
div
>
Trial
{
trialId
}
kill failed!
</
div
>
<
span
>
Error message:
{
isError
.
message
}
</
span
>
</
div
>
)
:
(
<
div
>
Trial
<
span
className
=
'bold'
>
{
trialId
}
</
span
>
had been killed successfully.
</
div
>
)
}
<
DialogFooter
>
<
PrimaryButton
onClick
=
{
onHideDialog
}
text
=
'Close'
/>
</
DialogFooter
>
</
Dialog
>
);
}
KillJobDialog
.
propTypes
=
{
trialId
:
PropTypes
.
string
,
isError
:
PropTypes
.
object
,
onHideDialog
:
PropTypes
.
func
};
export
default
KillJobDialog
;
ts/webui/src/components/experiment/trialdetail/table/tableFunction/killJob/KillJobIndex.tsx
0 → 100644
View file @
8c2f717d
import
React
,
{
useState
,
useRef
,
useContext
}
from
'
react
'
;
import
PropTypes
from
'
prop-types
'
;
import
axios
from
'
axios
'
;
import
{
Stack
,
FocusTrapCallout
,
DefaultButton
,
FocusZone
,
PrimaryButton
}
from
'
@fluentui/react
'
;
import
{
MANAGER_IP
}
from
'
@static/const
'
;
import
KillJobDialog
from
'
./KillJobDialog
'
;
import
{
blocked
}
from
'
@components/fluent/Icon
'
;
import
{
gap10
}
from
'
@components/fluent/ChildrenGap
'
;
import
{
styles
}
from
'
@components/experiment/overview/params/basicInfoStyles
'
;
import
{
AppContext
}
from
'
@/App
'
;
function
KillJobIndex
(
props
):
any
{
const
menuButtonElement
=
useRef
(
null
);
const
{
startTimer
,
closeTimer
,
interval
,
refreshDetailTable
}
=
useContext
(
AppContext
);
const
{
trialId
}
=
props
;
const
[
isCalloutVisible
,
setCalloutVisible
]
=
useState
(
false
);
const
[
isVisibleKillDialog
,
setKillDialogVisible
]
=
useState
(
false
);
const
[
error
,
setError
]
=
useState
({
isError
:
false
,
message
:
''
});
const
promptString
=
'
Are you sure to cancel this trial?
'
;
// kill trial
const
killJob
=
(
id
:
string
):
void
=>
{
if
(
interval
!==
0
)
{
closeTimer
();
// close auto refresh to confirm show the kill model
}
axios
(
`
${
MANAGER_IP
}
/trial-jobs/
${
id
}
`
,
{
method
:
'
DELETE
'
,
headers
:
{
'
Content-Type
'
:
'
application/json;charset=utf-8
'
}
})
.
then
(
res
=>
{
if
(
res
.
status
===
200
)
{
setKillDialogVisible
(
true
);
setError
({
isError
:
false
,
message
:
''
});
}
else
{
setKillDialogVisible
(
true
);
setError
({
isError
:
false
,
message
:
'
fail to cancel the job
'
});
}
})
.
catch
(
error
=>
{
if
(
error
.
response
)
{
setKillDialogVisible
(
true
);
setError
({
isError
:
false
,
message
:
error
.
response
.
data
.
error
||
'
Fail to cancel the job
'
});
}
else
{
setKillDialogVisible
(
true
);
setError
({
isError
:
false
,
message
:
error
.
response
.
data
.
error
||
'
500 error, fail to cancel the job
'
});
}
});
};
const
onDismissKillJobMessageDialog
=
async
():
Promise
<
void
>
=>
{
setKillDialogVisible
(
false
);
await
refreshDetailTable
();
if
(
interval
!==
0
)
{
startTimer
();
// start refresh
}
};
const
onDismiss
=
():
void
=>
{
setCalloutVisible
(
false
);
};
const
onKill
=
():
void
=>
{
setCalloutVisible
(
false
);
killJob
(
props
.
trialId
);
};
const
openPrompt
=
(
event
:
React
.
SyntheticEvent
<
EventTarget
>
):
void
=>
{
event
.
preventDefault
();
event
.
stopPropagation
();
setCalloutVisible
(
true
);
};
return
(
<
div
>
<
div
className
=
{
styles
.
buttonArea
}
ref
=
{
menuButtonElement
}
>
<
PrimaryButton
className
=
'detail-button-operation'
onClick
=
{
openPrompt
}
title
=
'kill'
>
{
blocked
}
</
PrimaryButton
>
</
div
>
{
isCalloutVisible
?
(
<
div
>
<
FocusTrapCallout
role
=
'alertdialog'
className
=
{
styles
.
callout
}
gapSpace
=
{
0
}
target
=
{
menuButtonElement
}
onDismiss
=
{
onDismiss
}
setInitialFocus
=
{
true
}
>
<
div
className
=
{
`
${
styles
.
header
}
font`
}
>
<
p
className
=
{
`
${
styles
.
title
}
color333`
}
>
Kill trial
</
p
>
</
div
>
<
div
className
=
{
`
${
styles
.
inner
}
font`
}
>
<
div
>
<
p
className
=
{
`
${
styles
.
subtext
}
color333`
}
>
{
promptString
}
</
p
>
</
div
>
</
div
>
<
FocusZone
>
<
Stack
className
=
{
styles
.
buttons
}
tokens
=
{
gap10
}
horizontal
>
<
DefaultButton
onClick
=
{
onDismiss
}
>
No
</
DefaultButton
>
<
PrimaryButton
onClick
=
{
onKill
}
>
Yes
</
PrimaryButton
>
</
Stack
>
</
FocusZone
>
</
FocusTrapCallout
>
</
div
>
)
:
null
}
{
/* kill job status dialog */
}
{
isVisibleKillDialog
&&
(
<
KillJobDialog
trialId
=
{
trialId
}
isError
=
{
error
}
onHideDialog
=
{
onDismissKillJobMessageDialog
}
/>
)
}
</
div
>
);
}
KillJobIndex
.
propTypes
=
{
trialId
:
PropTypes
.
string
,
updatePage
:
PropTypes
.
func
};
export
default
KillJobIndex
;
ts/webui/src/components/experiment/trialdetail/table/tableFunction/killTrial/Killjob.tsx
deleted
100644 → 0
View file @
f24c8380
import
*
as
React
from
'
react
'
;
import
{
Stack
,
FocusTrapCallout
,
DefaultButton
,
FocusZone
,
PrimaryButton
}
from
'
@fluentui/react
'
;
import
{
killJob
}
from
'
@static/function
'
;
import
{
blocked
}
from
'
@components/fluent/Icon
'
;
import
{
styles
}
from
'
@components/experiment/overview/params/basicInfoStyles
'
;
interface
KillJobState
{
isCalloutVisible
:
boolean
;
}
interface
KillJobProps
{
trial
:
any
;
}
class
KillJob
extends
React
.
Component
<
KillJobProps
,
KillJobState
>
{
private
menuButtonElement
!
:
HTMLElement
|
null
;
constructor
(
props
:
KillJobProps
)
{
super
(
props
);
this
.
state
=
{
isCalloutVisible
:
false
};
}
render
():
React
.
ReactNode
{
const
{
isCalloutVisible
}
=
this
.
state
;
const
prompString
=
'
Are you sure to cancel this trial?
'
;
return
(
<
div
>
<
div
className
=
{
styles
.
buttonArea
}
ref
=
{
(
menuButton
):
any
=>
(
this
.
menuButtonElement
=
menuButton
)
}
>
<
PrimaryButton
className
=
'detail-button-operation'
onClick
=
{
this
.
openPromot
}
title
=
'kill'
>
{
blocked
}
</
PrimaryButton
>
</
div
>
{
isCalloutVisible
?
(
<
div
>
<
FocusTrapCallout
role
=
'alertdialog'
className
=
{
styles
.
callout
}
gapSpace
=
{
0
}
target
=
{
this
.
menuButtonElement
}
onDismiss
=
{
this
.
onDismiss
}
setInitialFocus
=
{
true
}
>
<
div
className
=
{
styles
.
header
}
>
<
p
className
=
{
styles
.
title
}
style
=
{
{
color
:
'
#333
'
}
}
>
Kill trial
</
p
>
</
div
>
<
div
className
=
{
styles
.
inner
}
>
<
div
>
<
p
className
=
{
styles
.
subtext
}
style
=
{
{
color
:
'
#333
'
}
}
>
{
prompString
}
</
p
>
</
div
>
</
div
>
<
FocusZone
>
<
Stack
className
=
{
styles
.
buttons
}
gap
=
{
8
}
horizontal
>
<
DefaultButton
onClick
=
{
this
.
onDismiss
}
>
No
</
DefaultButton
>
<
PrimaryButton
onClick
=
{
this
.
onKill
}
>
Yes
</
PrimaryButton
>
</
Stack
>
</
FocusZone
>
</
FocusTrapCallout
>
</
div
>
)
:
null
}
</
div
>
);
}
private
onDismiss
=
():
void
=>
{
this
.
setState
(()
=>
({
isCalloutVisible
:
false
}));
};
private
onKill
=
():
void
=>
{
this
.
setState
({
isCalloutVisible
:
false
},
()
=>
{
const
{
trial
}
=
this
.
props
;
killJob
(
trial
.
key
,
trial
.
id
,
trial
.
status
);
});
};
private
openPromot
=
(
event
:
React
.
SyntheticEvent
<
EventTarget
>
):
void
=>
{
event
.
preventDefault
();
event
.
stopPropagation
();
this
.
setState
({
isCalloutVisible
:
true
});
};
}
export
default
KillJob
;
ts/webui/src/components/experiment/trialdetail/table/tableFunction/search/GeneralSearch.tsx
View file @
8c2f717d
import
React
,
{
useState
}
from
'
react
'
;
import
React
,
{
useState
,
useContext
}
from
'
react
'
;
import
PropTypes
from
'
prop-types
'
;
import
{
Stack
,
PrimaryButton
}
from
'
@fluentui/react
'
;
import
{
gap10
}
from
'
@components/fluent/ChildrenGap
'
;
import
{
AppContext
}
from
'
@/App
'
;
import
{
getSearchInputValueBySearchList
}
from
'
./searchFunction
'
;
// This file is for search trial ['Trial id', 'Trial No.']
function
GeneralSearch
(
props
):
any
{
const
{
updateDetailPage
}
=
useContext
(
AppContext
);
// searchName val: Trial No. | Trial id
const
{
searchName
,
searchFilter
,
dismiss
,
changeSearchFilterList
,
setSearchInputVal
,
updatePage
}
=
props
;
const
{
searchName
,
searchFilter
,
dismiss
,
changeSearchFilterList
,
setSearchInputVal
}
=
props
;
const
[
firstInputVal
,
setFirstInputVal
]
=
useState
(
getSearchNameInit
());
function
updateFirstInputVal
(
ev
:
React
.
ChangeEvent
<
HTMLInputElement
>
):
void
{
...
...
@@ -56,7 +58,7 @@ function GeneralSearch(props): any {
}
setSearchInputVal
(
getSearchInputValueBySearchList
(
searchFilterConditions
));
changeSearchFilterList
(
searchFilterConditions
);
updatePage
();
update
Detail
Page
();
dismiss
();
// close menu
}
...
...
@@ -75,8 +77,7 @@ GeneralSearch.propTypes = {
searchFilter
:
PropTypes
.
array
,
dismiss
:
PropTypes
.
func
,
setSearchInputVal
:
PropTypes
.
func
,
changeSearchFilterList
:
PropTypes
.
func
,
updatePage
:
PropTypes
.
func
changeSearchFilterList
:
PropTypes
.
func
};
export
default
GeneralSearch
;
ts/webui/src/components/experiment/trialdetail/table/tableFunction/search/Search.tsx
View file @
8c2f717d
import
React
,
{
useState
}
from
'
react
'
;
import
React
,
{
useState
,
useContext
}
from
'
react
'
;
import
PropTypes
from
'
prop-types
'
;
import
{
Stack
,
...
...
@@ -13,11 +13,13 @@ import { SearchItems } from '@static/interface';
import
SearchParameterConditions
from
'
./SearchParameterConditions
'
;
import
GeneralSearch
from
'
./GeneralSearch
'
;
import
{
classNames
,
isChoiceType
}
from
'
./searchFunction
'
;
import
{
AppContext
}
from
'
@/App
'
;
// TableList search layout
function
Search
(
props
):
any
{
const
{
searchFilter
,
changeSearchFilterList
,
updatePage
}
=
props
;
const
{
searchFilter
,
changeSearchFilterList
}
=
props
;
const
{
updateDetailPage
}
=
useContext
(
AppContext
);
const
[
searchInputVal
,
setSearchInputVal
]
=
useState
(
''
);
function
getSearchMenu
(
parameterList
):
IContextualMenuProps
{
...
...
@@ -80,7 +82,7 @@ function Search(props): any {
parameter
=
{
item
.
text
}
searchFilter
=
{
searchFilter
}
// search filter list
changeSearchFilterList
=
{
changeSearchFilterList
}
updatePage
=
{
updatePage
}
updatePage
=
{
update
Detail
Page
}
setSearchInputVal
=
{
setSearchInputVal
}
dismiss
=
{
dismissMenu
}
// close menu
/>
...
...
@@ -94,7 +96,6 @@ function Search(props): any {
searchFilter
=
{
searchFilter
}
// search fliter list
changeSearchFilterList
=
{
changeSearchFilterList
}
setSearchInputVal
=
{
setSearchInputVal
}
updatePage
=
{
updatePage
}
dismiss
=
{
dismissMenu
}
// after click Apply button to close menu
/>
);
...
...
@@ -107,7 +108,7 @@ function Search(props): any {
// update TableList page
function
changeTableListPage
(
searchFilterList
:
Array
<
SearchItems
>
):
void
{
changeSearchFilterList
(
searchFilterList
);
updatePage
();
update
Detail
Page
();
}
// deal with the format 1.[x, (space)xx] 2. (space)[x]
...
...
@@ -266,8 +267,7 @@ function Search(props): any {
Search
.
propTypes
=
{
searchFilter
:
PropTypes
.
array
,
changeSearchFilterList
:
PropTypes
.
func
,
updatePage
:
PropTypes
.
func
changeSearchFilterList
:
PropTypes
.
func
};
export
default
Search
;
ts/webui/src/components/experiment/trialdetail/table/tableFunction/search/SearchParameterConditions.tsx
View file @
8c2f717d
import
React
,
{
useState
}
from
'
react
'
;
import
React
,
{
useState
,
useContext
}
from
'
react
'
;
import
PropTypes
from
'
prop-types
'
;
import
{
Stack
,
PrimaryButton
,
Dropdown
,
IDropdownOption
}
from
'
@fluentui/react
'
;
import
{
EXPERIMENT
}
from
'
@static/datamodel
'
;
import
{
getDropdownOptions
,
getSearchInputValueBySearchList
}
from
'
./searchFunction
'
;
import
{
gap10
}
from
'
@components/fluent/ChildrenGap
'
;
import
{
AppContext
}
from
'
@/App
'
;
// This file is for filtering trial parameters and trial status
function
SearchParameterConditions
(
props
):
any
{
const
{
parameter
,
searchFilter
,
dismiss
,
changeSearchFilterList
,
updatePage
,
setSearchInputVal
}
=
props
;
const
{
parameter
,
searchFilter
,
dismiss
,
changeSearchFilterList
,
setSearchInputVal
}
=
props
;
const
{
updateDetailPage
}
=
useContext
(
AppContext
);
const
isChoiceTypeSearchFilter
=
parameter
===
'
StatusNNI
'
||
EXPERIMENT
.
searchSpace
[
parameter
].
_type
===
'
choice
'
;
const
operatorList
=
isChoiceTypeSearchFilter
?
[
'
=
'
,
'
≠
'
]
:
[
'
between
'
,
'
>
'
,
'
<
'
,
'
=
'
,
'
≠
'
];
...
...
@@ -125,7 +127,7 @@ function SearchParameterConditions(props): any {
setSearchInputVal
(
getSearchInputValueBySearchList
(
newSearchFilters
));
changeSearchFilterList
(
newSearchFilters
);
updatePage
();
update
Detail
Page
();
dismiss
();
// close menu
}
...
...
ts/webui/src/components/experimentManagement/TrialIdColumn.tsx
View file @
8c2f717d
...
...
@@ -25,12 +25,7 @@ class TrialIdColumn extends React.Component<TrialIdColumnProps, {}> {
{
item
.
status
===
'
STOPPED
'
?
(
<
div
className
=
'idColor'
>
{
item
.
id
}
</
div
>
)
:
(
<
a
href
=
{
webuiPortal
}
className
=
'link toAnotherExp idColor'
target
=
'_blank'
rel
=
'noopener noreferrer'
>
<
a
href
=
{
webuiPortal
}
className
=
'link'
target
=
'_blank'
rel
=
'noopener noreferrer'
>
{
item
.
id
}
</
a
>
)
}
...
...
ts/webui/src/components/nav/Nav.tsx
View file @
8c2f717d
...
...
@@ -2,7 +2,7 @@ import * as React from 'react';
import
axios
from
'
axios
'
;
import
{
Stack
,
StackItem
,
CommandBarButton
,
IContextualMenuProps
}
from
'
@fluentui/react
'
;
import
{
Link
}
from
'
react-router-dom
'
;
import
{
WEBUIDOC
,
MANAGER_IP
}
from
'
@static/const
'
;
import
{
MANAGER_IP
,
WEBUIDOC
}
from
'
@static/const
'
;
import
ExperimentSummaryPanel
from
'
./slideNav/ExperimentSummaryPanel
'
;
import
{
OVERVIEWTABS
,
DETAILTABS
,
NNILOGO
}
from
'
./slideNav/NNItabs
'
;
import
{
EXPERIMENT
}
from
'
@static/datamodel
'
;
...
...
@@ -160,7 +160,7 @@ class NavCon extends React.Component<NavProps, NavState> {
</
StackItem
>
{
isvisibleExperimentDrawer
&&
(
<
ExperimentSummaryPanel
closeExp
Drawer
=
{
this
.
closeExpDrawer
}
closeExp
Panel
=
{
this
.
closeExpDrawer
}
experimentProfile
=
{
EXPERIMENT
.
profile
}
/>
)
}
...
...
ts/webui/src/components/nav/slideNav/ExperimentSummaryPanel.tsx
View file @
8c2f717d
...
...
@@ -7,26 +7,26 @@ import { EXPERIMENT, TRIALS } from '@static/datamodel';
import
{
caclMonacoEditorHeight
}
from
'
@static/function
'
;
import
'
@style/logPanel.scss
'
;
interface
Exp
Drawer
Props
{
closeExp
Drawer
:
()
=>
void
;
interface
Exp
Panel
Props
{
closeExp
Panel
:
()
=>
void
;
experimentProfile
:
object
;
}
interface
Exp
Drawer
State
{
interface
Exp
Panel
State
{
experiment
:
string
;
exp
Drawer
Height
:
number
;
exp
Panel
Height
:
number
;
}
class
ExperimentSummaryPanel
extends
React
.
Component
<
Exp
Drawer
Props
,
Exp
Drawer
State
>
{
class
ExperimentSummaryPanel
extends
React
.
Component
<
Exp
Panel
Props
,
Exp
Panel
State
>
{
public
_isExperimentMount
!
:
boolean
;
private
refreshId
!
:
number
|
undefined
;
constructor
(
props
:
Exp
Drawer
Props
)
{
constructor
(
props
:
Exp
Panel
Props
)
{
super
(
props
);
this
.
state
=
{
experiment
:
''
,
exp
Drawer
Height
:
window
.
innerHeight
exp
Panel
Height
:
window
.
innerHeight
};
}
...
...
@@ -67,7 +67,7 @@ class ExperimentSummaryPanel extends React.Component<ExpDrawerProps, ExpDrawerSt
};
onWindowResize
=
():
void
=>
{
this
.
setState
(()
=>
({
exp
Drawer
Height
:
window
.
innerHeight
}));
this
.
setState
(()
=>
({
exp
Panel
Height
:
window
.
innerHeight
}));
};
componentDidMount
():
void
{
...
...
@@ -84,12 +84,12 @@ class ExperimentSummaryPanel extends React.Component<ExpDrawerProps, ExpDrawerSt
}
render
():
React
.
ReactNode
{
const
{
closeExp
Drawer
}
=
this
.
props
;
const
{
experiment
,
exp
Drawer
Height
}
=
this
.
state
;
const
monacoEditorHeight
=
caclMonacoEditorHeight
(
exp
Drawer
Height
);
const
{
closeExp
Panel
}
=
this
.
props
;
const
{
experiment
,
exp
Panel
Height
}
=
this
.
state
;
const
monacoEditorHeight
=
caclMonacoEditorHeight
(
exp
Panel
Height
);
return
(
<
Panel
isOpen
=
{
true
}
hasCloseButton
=
{
false
}
isLightDismiss
=
{
true
}
onLightDismissClick
=
{
closeExp
Drawer
}
>
<
Panel
isOpen
=
{
true
}
hasCloseButton
=
{
false
}
isLightDismiss
=
{
true
}
onLightDismissClick
=
{
closeExp
Panel
}
>
<
div
className
=
'panel'
>
<
div
className
=
'panelName'
>
Summary
</
div
>
<
MonacoEditor
...
...
@@ -104,7 +104,7 @@ class ExperimentSummaryPanel extends React.Component<ExpDrawerProps, ExpDrawerSt
<
PrimaryButton
text
=
'Download'
onClick
=
{
this
.
downExperimentParameters
}
/>
</
StackItem
>
<
StackItem
grow
=
{
50
}
className
=
'close'
>
<
DefaultButton
text
=
'Close'
onClick
=
{
closeExp
Drawer
}
/>
<
DefaultButton
text
=
'Close'
onClick
=
{
closeExp
Panel
}
/>
</
StackItem
>
</
Stack
>
</
div
>
...
...
Prev
1
2
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