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
4ccc9402
Unverified
Commit
4ccc9402
authored
May 26, 2021
by
Lijiaoa
Committed by
GitHub
May 26, 2021
Browse files
Improve search parameters on trial detail page (#3651)
Co-authored-by:
Lijiao
<
Lijiaoa@outlook.com
>
parent
5df75c33
Changes
13
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
866 additions
and
100 deletions
+866
-100
ts/webui/scripts/start.js
ts/webui/scripts/start.js
+1
-1
ts/webui/src/App.tsx
ts/webui/src/App.tsx
+9
-0
ts/webui/src/components/TrialsDetail.tsx
ts/webui/src/components/TrialsDetail.tsx
+1
-4
ts/webui/src/components/modals/ChildrenGap.ts
ts/webui/src/components/modals/ChildrenGap.ts
+7
-0
ts/webui/src/components/trial-detail/TableList.tsx
ts/webui/src/components/trial-detail/TableList.tsx
+45
-86
ts/webui/src/components/trial-detail/search/GeneralSearch.tsx
...ebui/src/components/trial-detail/search/GeneralSearch.tsx
+82
-0
ts/webui/src/components/trial-detail/search/Search.tsx
ts/webui/src/components/trial-detail/search/Search.tsx
+259
-0
ts/webui/src/components/trial-detail/search/SearchParameterConditions.tsx
...ponents/trial-detail/search/SearchParameterConditions.tsx
+197
-0
ts/webui/src/components/trial-detail/search/searchFunction.ts
...ebui/src/components/trial-detail/search/searchFunction.ts
+203
-0
ts/webui/src/static/function.ts
ts/webui/src/static/function.ts
+16
-1
ts/webui/src/static/interface.ts
ts/webui/src/static/interface.ts
+12
-1
ts/webui/src/static/style/common.scss
ts/webui/src/static/style/common.scss
+4
-0
ts/webui/src/static/style/search.scss
ts/webui/src/static/style/search.scss
+30
-7
No files found.
ts/webui/scripts/start.js
View file @
4ccc9402
...
@@ -41,7 +41,7 @@ if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
...
@@ -41,7 +41,7 @@ if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
}
}
// Tools like Cloud9 rely on this.
// Tools like Cloud9 rely on this.
const
DEFAULT_PORT
=
parseInt
(
process
.
env
.
PORT
,
10
)
||
3
000
;
const
DEFAULT_PORT
=
parseInt
(
process
.
env
.
PORT
,
10
)
||
8
000
;
const
HOST
=
process
.
env
.
HOST
||
'
0.0.0.0
'
;
const
HOST
=
process
.
env
.
HOST
||
'
0.0.0.0
'
;
if
(
process
.
env
.
HOST
)
{
if
(
process
.
env
.
HOST
)
{
...
...
ts/webui/src/App.tsx
View file @
4ccc9402
...
@@ -48,6 +48,8 @@ export const AppContext = React.createContext({
...
@@ -48,6 +48,8 @@ export const AppContext = React.createContext({
// eslint-disable-next-line @typescript-eslint/no-empty-function
// eslint-disable-next-line @typescript-eslint/no-empty-function
updateOverviewPage
:
()
=>
{},
updateOverviewPage
:
()
=>
{},
// eslint-disable-next-line @typescript-eslint/no-empty-function
// 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
=>
{}
});
});
...
@@ -133,6 +135,12 @@ class App extends React.Component<{}, AppState> {
...
@@ -133,6 +135,12 @@ class App extends React.Component<{}, AppState> {
}));
}));
};
};
updateDetailPage
=
():
void
=>
{
this
.
setState
(
state
=>
({
trialsUpdateBroadcast
:
state
.
trialsUpdateBroadcast
+
1
}));
};
shouldComponentUpdate
(
nextProps
:
any
,
nextState
:
AppState
):
boolean
{
shouldComponentUpdate
(
nextProps
:
any
,
nextState
:
AppState
):
boolean
{
if
(
!
(
nextState
.
isUpdate
||
nextState
.
isUpdate
===
undefined
))
{
if
(
!
(
nextState
.
isUpdate
||
nextState
.
isUpdate
===
undefined
))
{
nextState
.
isUpdate
=
true
;
nextState
.
isUpdate
=
true
;
...
@@ -207,6 +215,7 @@ class App extends React.Component<{}, AppState> {
...
@@ -207,6 +215,7 @@ class App extends React.Component<{}, AppState> {
bestTrialEntries
,
bestTrialEntries
,
changeEntries
:
this
.
changeEntries
,
changeEntries
:
this
.
changeEntries
,
updateOverviewPage
:
this
.
updateOverviewPage
,
updateOverviewPage
:
this
.
updateOverviewPage
,
updateDetailPage
:
this
.
updateDetailPage
,
expandRowIDs
,
expandRowIDs
,
changeExpandRowIDs
:
this
.
changeExpandRowIDs
changeExpandRowIDs
:
this
.
changeExpandRowIDs
}
}
}
}
...
...
ts/webui/src/components/TrialsDetail.tsx
View file @
4ccc9402
...
@@ -83,10 +83,7 @@ class TrialsDetail extends React.Component<{}, TrialDetailState> {
...
@@ -83,10 +83,7 @@ class TrialsDetail extends React.Component<{}, TrialDetailState> {
</
div
>
</
div
>
{
/* trial table list */
}
{
/* trial table list */
}
<
div
className
=
'detailTable'
style
=
{
{
marginTop
:
10
}
}
>
<
div
className
=
'detailTable'
style
=
{
{
marginTop
:
10
}
}
>
<
TableList
<
TableList
tableSource
=
{
source
}
updateDetailPage
=
{
this
.
context
.
updateDetailPage
}
/>
tableSource
=
{
source
}
trialsUpdateBroadcast
=
{
this
.
context
.
trialsUpdateBroadcast
}
/>
</
div
>
</
div
>
</
React
.
Fragment
>
</
React
.
Fragment
>
)
}
)
}
...
...
ts/webui/src/components/modals/ChildrenGap.ts
0 → 100644
View file @
4ccc9402
import
{
IStackTokens
}
from
'
@fluentui/react
'
;
const
searchConditonsGap
:
IStackTokens
=
{
childrenGap
:
10
};
export
{
searchConditonsGap
};
ts/webui/src/components/trial-detail/TableList.tsx
View file @
4ccc9402
import
React
from
'
react
'
;
import
React
from
'
react
'
;
import
{
import
{
DefaultButton
,
DefaultButton
,
Dropdown
,
IColumn
,
IColumn
,
Icon
,
Icon
,
IDropdownOption
,
PrimaryButton
,
PrimaryButton
,
Stack
,
Stack
,
StackItem
,
StackItem
,
...
@@ -14,13 +12,15 @@ import {
...
@@ -14,13 +12,15 @@ import {
}
from
'
@fluentui/react
'
;
}
from
'
@fluentui/react
'
;
import
{
EXPERIMENT
,
TRIALS
}
from
'
../../static/datamodel
'
;
import
{
EXPERIMENT
,
TRIALS
}
from
'
../../static/datamodel
'
;
import
{
TOOLTIP_BACKGROUND_COLOR
}
from
'
../../static/const
'
;
import
{
TOOLTIP_BACKGROUND_COLOR
}
from
'
../../static/const
'
;
import
{
convertDuration
,
formatTimestamp
,
copyAndSort
}
from
'
../../static/function
'
;
import
{
convertDuration
,
formatTimestamp
,
copyAndSort
,
parametersType
}
from
'
../../static/function
'
;
import
{
TableObj
,
SortInfo
}
from
'
../../static/interface
'
;
import
{
TableObj
,
SortInfo
,
SearchItems
}
from
'
../../static/interface
'
;
import
{
getTrialsBySearchFilters
}
from
'
./search/searchFunction
'
;
import
{
blocked
,
copy
,
LineChart
,
tableListIcon
}
from
'
../buttons/Icon
'
;
import
{
blocked
,
copy
,
LineChart
,
tableListIcon
}
from
'
../buttons/Icon
'
;
import
ChangeColumnComponent
from
'
../modals/ChangeColumnComponent
'
;
import
ChangeColumnComponent
from
'
../modals/ChangeColumnComponent
'
;
import
Compare
from
'
../modals/Compare
'
;
import
Compare
from
'
../modals/Compare
'
;
import
Customize
from
'
../modals/CustomizedTrial
'
;
import
Customize
from
'
../modals/CustomizedTrial
'
;
import
TensorboardUI
from
'
../modals/tensorboard/TensorboardUI
'
;
import
TensorboardUI
from
'
../modals/tensorboard/TensorboardUI
'
;
import
Search
from
'
./search/Search
'
;
import
KillJob
from
'
../modals/Killjob
'
;
import
KillJob
from
'
../modals/Killjob
'
;
import
ExpandableDetails
from
'
../public-child/ExpandableDetails
'
;
import
ExpandableDetails
from
'
../public-child/ExpandableDetails
'
;
import
PaginationTable
from
'
../public-child/PaginationTable
'
;
import
PaginationTable
from
'
../public-child/PaginationTable
'
;
...
@@ -41,12 +41,6 @@ require('echarts/lib/component/tooltip');
...
@@ -41,12 +41,6 @@ require('echarts/lib/component/tooltip');
require
(
'
echarts/lib/component/title
'
);
require
(
'
echarts/lib/component/title
'
);
type
SearchOptionType
=
'
id
'
|
'
trialnum
'
|
'
status
'
|
'
parameters
'
;
type
SearchOptionType
=
'
id
'
|
'
trialnum
'
|
'
status
'
|
'
parameters
'
;
const
searchOptionLiterals
=
{
id
:
'
ID
'
,
trialnum
:
'
Trial No.
'
,
status
:
'
Status
'
,
parameters
:
'
Parameters
'
};
const
defaultDisplayedColumns
=
[
'
sequenceId
'
,
'
id
'
,
'
duration
'
,
'
status
'
,
'
latestAccuracy
'
];
const
defaultDisplayedColumns
=
[
'
sequenceId
'
,
'
id
'
,
'
duration
'
,
'
status
'
,
'
latestAccuracy
'
];
...
@@ -76,7 +70,7 @@ function _inferColumnTitle(columnKey: string): string {
...
@@ -76,7 +70,7 @@ function _inferColumnTitle(columnKey: string): string {
interface
TableListProps
{
interface
TableListProps
{
tableSource
:
TableObj
[];
tableSource
:
TableObj
[];
trialsUpdateBroadcast
:
number
;
updateDetailPage
:
()
=>
void
;
}
}
interface
TableListState
{
interface
TableListState
{
...
@@ -91,6 +85,8 @@ interface TableListState {
...
@@ -91,6 +85,8 @@ interface TableListState {
intermediateDialogTrial
:
TableObj
|
undefined
;
intermediateDialogTrial
:
TableObj
|
undefined
;
copiedTrialId
:
string
|
undefined
;
copiedTrialId
:
string
|
undefined
;
sortInfo
:
SortInfo
;
sortInfo
:
SortInfo
;
searchItems
:
Array
<
SearchItems
>
;
relation
:
Map
<
string
,
string
>
;
}
}
class
TableList
extends
React
.
Component
<
TableListProps
,
TableListState
>
{
class
TableList
extends
React
.
Component
<
TableListProps
,
TableListState
>
{
...
@@ -114,47 +110,14 @@ class TableList extends React.Component<TableListProps, TableListState> {
...
@@ -114,47 +110,14 @@ class TableList extends React.Component<TableListProps, TableListState> {
selectedRowIds
:
[],
selectedRowIds
:
[],
intermediateDialogTrial
:
undefined
,
intermediateDialogTrial
:
undefined
,
copiedTrialId
:
undefined
,
copiedTrialId
:
undefined
,
sortInfo
:
{
field
:
''
,
isDescend
:
true
}
sortInfo
:
{
field
:
''
,
isDescend
:
true
},
searchItems
:
[],
relation
:
parametersType
()
};
};
this
.
_expandedTrialIds
=
new
Set
<
string
>
();
this
.
_expandedTrialIds
=
new
Set
<
string
>
();
}
}
/* 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
);
}
}
return
trials
.
filter
(
searchFilter
);
}
private
_updateSearchFilterType
(
_event
:
React
.
FormEvent
<
HTMLDivElement
>
,
item
:
IDropdownOption
|
undefined
):
void
{
if
(
item
!==
undefined
)
{
const
value
=
item
.
key
.
toString
();
if
(
searchOptionLiterals
.
hasOwnProperty
(
value
))
{
this
.
setState
({
searchType
:
value
as
SearchOptionType
},
this
.
_updateTableSource
);
}
}
}
private
_updateSearchText
(
ev
:
React
.
ChangeEvent
<
HTMLInputElement
>
):
void
{
this
.
setState
({
searchText
:
ev
.
target
.
value
},
this
.
_updateTableSource
);
}
/* Table basic function related methods */
/* Table basic function related methods */
private
_onColumnClick
(
ev
:
React
.
MouseEvent
<
HTMLElement
>
,
column
:
IColumn
):
void
{
private
_onColumnClick
(
ev
:
React
.
MouseEvent
<
HTMLElement
>
,
column
:
IColumn
):
void
{
...
@@ -180,7 +143,7 @@ class TableList extends React.Component<TableListProps, TableListState> {
...
@@ -180,7 +143,7 @@ class TableList extends React.Component<TableListProps, TableListState> {
const
ret
=
{
const
ret
=
{
sequenceId
:
trial
.
sequenceId
,
sequenceId
:
trial
.
sequenceId
,
id
:
trial
.
id
,
id
:
trial
.
id
,
checked
:
selectedRowIds
.
includes
(
trial
.
id
)
?
true
:
false
,
_
checked
:
selectedRowIds
.
includes
(
trial
.
id
)
?
true
:
false
,
startTime
:
(
trial
as
Trial
).
info
.
startTime
,
// FIXME: why do we need info here?
startTime
:
(
trial
as
Trial
).
info
.
startTime
,
// FIXME: why do we need info here?
endTime
:
(
trial
as
Trial
).
info
.
endTime
,
endTime
:
(
trial
as
Trial
).
info
.
endTime
,
duration
:
trial
.
duration
,
duration
:
trial
.
duration
,
...
@@ -221,7 +184,7 @@ class TableList extends React.Component<TableListProps, TableListState> {
...
@@ -221,7 +184,7 @@ class TableList extends React.Component<TableListProps, TableListState> {
}
}
items
.
forEach
(
item
=>
{
items
.
forEach
(
item
=>
{
if
(
item
.
id
===
id
)
{
if
(
item
.
id
===
id
)
{
item
.
checked
=
!!
checked
;
item
.
_
checked
=
!!
checked
;
}
}
});
});
this
.
setState
(()
=>
({
displayedItems
:
items
,
selectedRowIds
:
temp
}));
this
.
setState
(()
=>
({
displayedItems
:
items
,
selectedRowIds
:
temp
}));
...
@@ -231,7 +194,7 @@ class TableList extends React.Component<TableListProps, TableListState> {
...
@@ -231,7 +194,7 @@ class TableList extends React.Component<TableListProps, TableListState> {
const
{
displayedItems
}
=
this
.
state
;
const
{
displayedItems
}
=
this
.
state
;
const
newDisplayedItems
=
displayedItems
;
const
newDisplayedItems
=
displayedItems
;
newDisplayedItems
.
forEach
(
item
=>
{
newDisplayedItems
.
forEach
(
item
=>
{
item
.
checked
=
false
;
item
.
_
checked
=
false
;
});
});
this
.
setState
(()
=>
({
this
.
setState
(()
=>
({
selectedRowIds
:
[],
selectedRowIds
:
[],
...
@@ -253,7 +216,7 @@ class TableList extends React.Component<TableListProps, TableListState> {
...
@@ -253,7 +216,7 @@ class TableList extends React.Component<TableListProps, TableListState> {
onRender
:
(
record
):
React
.
ReactNode
=>
(
onRender
:
(
record
):
React
.
ReactNode
=>
(
<
Checkbox
<
Checkbox
label
=
{
undefined
}
label
=
{
undefined
}
checked
=
{
record
.
checked
}
checked
=
{
record
.
_
checked
}
className
=
'detail-check'
className
=
'detail-check'
onChange
=
{
this
.
selectedTrialOnChangeEvent
.
bind
(
this
,
record
.
id
)
}
onChange
=
{
this
.
selectedTrialOnChangeEvent
.
bind
(
this
,
record
.
id
)
}
/>
/>
...
@@ -438,7 +401,11 @@ class TableList extends React.Component<TableListProps, TableListState> {
...
@@ -438,7 +401,11 @@ class TableList extends React.Component<TableListProps, TableListState> {
private
_updateTableSource
():
void
{
private
_updateTableSource
():
void
{
// call this method when trials or the computation of trial filter has changed
// call this method when trials or the computation of trial filter has changed
const
items
=
this
.
_trialsToTableItems
(
this
.
_filterTrials
(
this
.
props
.
tableSource
));
const
{
searchItems
,
relation
}
=
this
.
state
;
let
items
=
this
.
_trialsToTableItems
(
this
.
props
.
tableSource
);
if
(
searchItems
.
length
>
0
)
{
items
=
getTrialsBySearchFilters
(
items
,
searchItems
,
relation
);
// use search filter to filter data
}
if
(
items
.
length
>
0
)
{
if
(
items
.
length
>
0
)
{
const
columns
=
this
.
_buildColumnsFromTableItems
(
items
);
const
columns
=
this
.
_buildColumnsFromTableItems
(
items
);
this
.
setState
({
this
.
setState
({
...
@@ -496,6 +463,12 @@ class TableList extends React.Component<TableListProps, TableListState> {
...
@@ -496,6 +463,12 @@ class TableList extends React.Component<TableListProps, TableListState> {
);
);
}
}
private
changeSearchFilterList
=
(
arr
:
Array
<
SearchItems
>
):
void
=>
{
this
.
setState
(()
=>
({
searchItems
:
arr
}));
};
componentDidUpdate
(
prevProps
:
TableListProps
):
void
{
componentDidUpdate
(
prevProps
:
TableListProps
):
void
{
if
(
this
.
props
.
tableSource
!==
prevProps
.
tableSource
)
{
if
(
this
.
props
.
tableSource
!==
prevProps
.
tableSource
)
{
this
.
_updateTableSource
();
this
.
_updateTableSource
();
...
@@ -510,13 +483,13 @@ class TableList extends React.Component<TableListProps, TableListState> {
...
@@ -510,13 +483,13 @@ class TableList extends React.Component<TableListProps, TableListState> {
const
{
const
{
displayedItems
,
displayedItems
,
columns
,
columns
,
searchType
,
customizeColumnsDialogVisible
,
customizeColumnsDialogVisible
,
compareDialogVisible
,
compareDialogVisible
,
displayedColumns
,
displayedColumns
,
selectedRowIds
,
selectedRowIds
,
intermediateDialogTrial
,
intermediateDialogTrial
,
copiedTrialId
copiedTrialId
,
searchItems
}
=
this
.
state
;
}
=
this
.
state
;
return
(
return
(
...
@@ -526,7 +499,24 @@ class TableList extends React.Component<TableListProps, TableListState> {
...
@@ -526,7 +499,24 @@ class TableList extends React.Component<TableListProps, TableListState> {
<
span
>
Trial jobs
</
span
>
<
span
>
Trial jobs
</
span
>
</
Stack
>
</
Stack
>
<
Stack
horizontal
className
=
'allList'
>
<
Stack
horizontal
className
=
'allList'
>
<
StackItem
grow
=
{
50
}
>
<
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
<
DefaultButton
text
=
'Compare'
text
=
'Compare'
className
=
'allList-compare'
className
=
'allList-compare'
...
@@ -540,37 +530,6 @@ class TableList extends React.Component<TableListProps, TableListState> {
...
@@ -540,37 +530,6 @@ class TableList extends React.Component<TableListProps, TableListState> {
changeSelectTrialIds
=
{
this
.
changeSelectTrialIds
}
changeSelectTrialIds
=
{
this
.
changeSelectTrialIds
}
/>
/>
</
StackItem
>
</
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
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
>
</
StackItem
>
</
Stack
>
</
Stack
>
{
columns
&&
displayedItems
&&
(
{
columns
&&
displayedItems
&&
(
<
PaginationTable
<
PaginationTable
...
...
ts/webui/src/components/trial-detail/search/GeneralSearch.tsx
0 → 100644
View file @
4ccc9402
import
React
,
{
useState
}
from
'
react
'
;
import
PropTypes
from
'
prop-types
'
;
import
{
Stack
,
PrimaryButton
}
from
'
@fluentui/react
'
;
import
{
searchConditonsGap
}
from
'
../../modals/ChildrenGap
'
;
import
{
getSearchInputValueBySearchList
}
from
'
./searchFunction
'
;
// This file is for search trial ['Trial id', 'Trial No.']
function
GeneralSearch
(
props
):
any
{
// searchName val: Trial No. | Trial id
const
{
searchName
,
searchFilter
,
dismiss
,
changeSearchFilterList
,
setSearchInputVal
,
updatePage
}
=
props
;
const
[
firstInputVal
,
setFirstInputVal
]
=
useState
(
getSearchNameInit
());
function
updateFirstInputVal
(
ev
:
React
.
ChangeEvent
<
HTMLInputElement
>
):
void
{
setFirstInputVal
(
ev
.
target
.
value
);
}
function
getSearchNameInit
():
string
{
let
str
=
''
;
// init ''
const
find
=
searchFilter
.
find
(
item
=>
item
.
name
===
searchName
);
if
(
find
!==
undefined
)
{
str
=
find
.
value1
;
// init by filter value
}
return
str
;
}
function
startFilterTrial
():
void
{
const
{
searchFilter
}
=
props
;
const
searchFilterConditions
=
JSON
.
parse
(
JSON
.
stringify
(
searchFilter
));
const
find
=
searchFilterConditions
.
filter
(
item
=>
item
.
name
===
searchName
);
if
(
firstInputVal
===
''
)
{
alert
(
'
Please input related value!
'
);
return
;
}
if
(
find
.
length
>
0
)
{
// change this record
// Trial id | Trial No. only need {search name, search value} these message
searchFilterConditions
.
forEach
(
item
=>
{
if
(
item
.
name
===
searchName
)
{
item
.
value1
=
firstInputVal
;
// item.operator = '';
item
.
isChoice
=
false
;
}
});
}
else
{
searchFilterConditions
.
push
({
name
:
searchName
,
// operator: '',
value1
:
firstInputVal
,
isChoice
:
false
});
}
setSearchInputVal
(
getSearchInputValueBySearchList
(
searchFilterConditions
));
changeSearchFilterList
(
searchFilterConditions
);
updatePage
();
dismiss
();
// close menu
}
return
(
// Trial id & Trial No.
<
Stack
horizontal
className
=
'filterConditions'
tokens
=
{
searchConditonsGap
}
>
<
span
>
{
searchName
===
'
Trial id
'
?
'
Includes
'
:
'
Equals to
'
}
</
span
>
<
input
type
=
'text'
className
=
'input input-padding'
onChange
=
{
updateFirstInputVal
}
value
=
{
firstInputVal
}
/>
<
PrimaryButton
text
=
'Apply'
className
=
'btn-vertical-middle'
onClick
=
{
startFilterTrial
}
/>
</
Stack
>
);
}
GeneralSearch
.
propTypes
=
{
searchName
:
PropTypes
.
string
,
searchFilter
:
PropTypes
.
array
,
dismiss
:
PropTypes
.
func
,
setSearchInputVal
:
PropTypes
.
func
,
changeSearchFilterList
:
PropTypes
.
func
,
updatePage
:
PropTypes
.
func
};
export
default
GeneralSearch
;
ts/webui/src/components/trial-detail/search/Search.tsx
0 → 100644
View file @
4ccc9402
import
React
,
{
useState
}
from
'
react
'
;
import
PropTypes
from
'
prop-types
'
;
import
{
Stack
,
DefaultButton
,
IContextualMenuProps
,
IContextualMenuItem
,
DirectionalHint
,
SearchBox
}
from
'
@fluentui/react
'
;
import
{
EXPERIMENT
}
from
'
../../../static/datamodel
'
;
import
{
SearchItems
}
from
'
../../../static/interface
'
;
import
SearchParameterConditions
from
'
./SearchParameterConditions
'
;
import
GeneralSearch
from
'
./GeneralSearch
'
;
import
{
classNames
,
isChoiceType
}
from
'
./searchFunction
'
;
// TableList search layout
function
Search
(
props
):
any
{
const
{
searchFilter
,
changeSearchFilterList
,
updatePage
}
=
props
;
const
[
searchInputVal
,
setSearchInputVal
]
=
useState
(
''
);
function
getSearchMenu
(
parameterList
):
IContextualMenuProps
{
const
menu
:
Array
<
object
>
=
[];
parameterList
.
unshift
(
'
StatusNNI
'
);
[
'
Trial id
'
,
'
Trial No.
'
].
forEach
(
item
=>
{
menu
.
push
({
key
:
item
,
text
:
item
,
subMenuProps
:
{
items
:
[
{
key
:
item
,
text
:
item
,
// component: GeneralSearch.tsx
onRender
:
renderIdAndNoComponent
.
bind
(
item
)
}
]
}
});
});
parameterList
.
forEach
(
item
=>
{
menu
.
push
({
key
:
item
,
text
:
item
===
'
StatusNNI
'
?
'
Status
'
:
item
,
subMenuProps
:
{
items
:
[
{
key
:
item
,
text
:
item
,
// component: SearchParameterConditions.tsx
onRender
:
renderParametersSearchComponent
.
bind
(
item
)
}
]
}
});
});
const
filterMenu
:
IContextualMenuProps
=
{
shouldFocusOnMount
:
true
,
directionalHint
:
DirectionalHint
.
bottomLeftEdge
,
className
:
classNames
.
menu
,
items
:
menu
as
any
};
return
filterMenu
;
}
// Avoid nested experiments, nested experiments do not support hyperparameter search
const
searchMenuProps
:
IContextualMenuProps
=
getSearchMenu
(
EXPERIMENT
.
isNestedExp
()
?
[]
:
Object
.
keys
(
EXPERIMENT
.
searchSpace
)
);
function
renderParametersSearchComponent
(
item
:
IContextualMenuItem
,
dismissMenu
:
()
=>
void
):
JSX
.
Element
{
return
(
<
SearchParameterConditions
parameter
=
{
item
.
text
}
searchFilter
=
{
searchFilter
}
// search filter list
changeSearchFilterList
=
{
changeSearchFilterList
}
updatePage
=
{
updatePage
}
setSearchInputVal
=
{
setSearchInputVal
}
dismiss
=
{
dismissMenu
}
// close menu
/>
);
}
function
renderIdAndNoComponent
(
item
:
IContextualMenuItem
,
dismissMenu
:
()
=>
void
):
JSX
.
Element
{
return
(
<
GeneralSearch
searchName
=
{
item
.
text
}
searchFilter
=
{
searchFilter
}
// search fliter list
changeSearchFilterList
=
{
changeSearchFilterList
}
setSearchInputVal
=
{
setSearchInputVal
}
updatePage
=
{
updatePage
}
dismiss
=
{
dismissMenu
}
// after click Apply button to close menu
/>
);
}
function
updateSearchText
(
_
,
newValue
):
void
{
setSearchInputVal
(
newValue
);
}
// update TableList page
function
changeTableListPage
(
searchFilterList
:
Array
<
SearchItems
>
):
void
{
changeSearchFilterList
(
searchFilterList
);
updatePage
();
}
// "[hello, world]", JSON.parse(it) doesn't work so write this function
function
convertStringArrToList
(
str
:
string
):
string
[]
{
const
value
=
str
.
slice
(
1
,
str
.
length
-
1
);
// delete []
// delete ""
const
result
:
string
[]
=
[];
if
(
value
.
includes
(
'
,
'
))
{
const
arr
=
value
.
split
(
'
,
'
);
arr
.
forEach
(
item
=>
{
if
(
item
!==
''
)
{
result
.
push
(
item
);
}
});
return
result
;
}
else
{
if
(
value
===
''
)
{
return
result
;
}
else
{
return
[
value
];
}
}
}
// SearchBox onSearch event: Filter based on the filter criteria entered by the user
function
startFilter
():
void
{
const
regEn
=
/`~!@#$%^&*
()
+
?
"{}.'/im
;
const
regCn
=
/·!#¥(——):;“”‘、,|《。》?、【】
[\]]
/im
;
if
(
regEn
.
test
(
searchInputVal
)
||
regCn
.
test
(
searchInputVal
))
{
alert
(
'
Please delete special characters in the conditions!
'
);
return
;
}
// according [input val] to change searchFilter list
const
allFilterConditions
=
searchInputVal
.
trim
().
split
(
'
;
'
);
const
newSearchFilter
:
any
=
[];
// delete '' in filter list
if
(
allFilterConditions
.
includes
(
''
))
{
allFilterConditions
.
splice
(
allFilterConditions
.
findIndex
(
item
=>
item
===
''
),
1
);
}
allFilterConditions
.
forEach
(
eachFilterConditionStr
=>
{
let
eachFilterConditionArr
:
string
[]
=
[];
// EXPERIMENT.searchSpace[parameter]._type === 'choice'
if
(
eachFilterConditionStr
.
includes
(
'
>
'
||
'
<
'
))
{
const
operator
=
eachFilterConditionStr
.
includes
(
'
>
'
)
===
true
?
'
>
'
:
'
<
'
;
eachFilterConditionArr
=
eachFilterConditionStr
.
trim
().
split
(
operator
);
newSearchFilter
.
push
({
name
:
eachFilterConditionArr
[
0
],
operator
:
operator
,
value1
:
eachFilterConditionArr
[
1
],
value2
:
''
,
choice
:
[],
isChoice
:
false
});
}
else
if
(
eachFilterConditionStr
.
includes
(
'
≠
'
))
{
// drop_rate≠6; status≠[x,xx,xxx]; conv_size≠[3,7]
eachFilterConditionArr
=
eachFilterConditionStr
.
trim
().
split
(
'
≠
'
);
const
filterName
=
eachFilterConditionArr
[
0
]
===
'
Status
'
?
'
StatusNNI
'
:
eachFilterConditionArr
[
0
];
const
isChoicesType
=
isChoiceType
(
filterName
);
newSearchFilter
.
push
({
name
:
filterName
,
operator
:
'
≠
'
,
value1
:
isChoicesType
?
''
:
JSON
.
parse
(
eachFilterConditionArr
[
1
]),
value2
:
''
,
choice
:
isChoicesType
?
convertStringArrToList
(
eachFilterConditionArr
[
1
])
:
[],
isChoice
:
isChoicesType
?
true
:
false
});
}
else
{
// = : conv_size:[1,2,3,4]; Trial id:3; hidden_size:[1,2], status:[val1,val2,val3]
eachFilterConditionArr
=
eachFilterConditionStr
.
trim
().
split
(
'
:
'
);
const
filterName
=
eachFilterConditionArr
[
0
]
===
'
Status
'
?
'
StatusNNI
'
:
eachFilterConditionArr
[
0
];
const
isChoicesType
=
isChoiceType
(
filterName
);
const
isArray
=
eachFilterConditionArr
.
length
>
1
&&
eachFilterConditionArr
[
1
].
includes
(
'
[
'
||
'
]
'
)
?
true
:
false
;
if
(
isArray
===
true
)
{
if
(
isChoicesType
===
true
)
{
// status:[SUCCEEDED]
newSearchFilter
.
push
({
name
:
filterName
,
operator
:
'
=
'
,
value1
:
''
,
value2
:
''
,
choice
:
convertStringArrToList
(
eachFilterConditionArr
[
1
]),
isChoice
:
true
});
}
else
{
// drop_rate:[1,10]
newSearchFilter
.
push
({
name
:
eachFilterConditionArr
[
0
],
operator
:
'
between
'
,
value1
:
JSON
.
parse
(
eachFilterConditionArr
[
1
])[
0
],
value2
:
JSON
.
parse
(
eachFilterConditionArr
[
1
])[
1
],
choice
:
[],
isChoice
:
false
});
}
}
else
{
newSearchFilter
.
push
({
name
:
eachFilterConditionArr
[
0
],
operator
:
'
=
'
,
value1
:
eachFilterConditionArr
[
1
],
value2
:
''
,
choice
:
[],
isChoice
:
false
});
}
}
});
changeTableListPage
(
newSearchFilter
);
}
// clear search input all value, clear all search filter
function
clearFliter
():
void
{
changeTableListPage
([]);
}
return
(
<
div
>
<
Stack
horizontal
>
<
DefaultButton
text
=
'Filter'
menuProps
=
{
searchMenuProps
}
/>
{
/* search input: store filter conditons, also, user could input filter conditions, could search */
}
<
SearchBox
styles
=
{
{
root
:
{
width
:
530
}
}
}
placeholder
=
'Search'
onChange
=
{
updateSearchText
}
value
=
{
searchInputVal
}
onSearch
=
{
startFilter
}
onEscape
=
{
clearFliter
}
onClear
=
{
clearFliter
}
/>
</
Stack
>
</
div
>
);
}
Search
.
propTypes
=
{
searchFilter
:
PropTypes
.
array
,
changeSearchFilterList
:
PropTypes
.
func
,
updatePage
:
PropTypes
.
func
};
export
default
Search
;
ts/webui/src/components/trial-detail/search/SearchParameterConditions.tsx
0 → 100644
View file @
4ccc9402
import
React
,
{
useState
}
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
{
searchConditonsGap
}
from
'
../../modals/ChildrenGap
'
;
// This file is for filtering trial parameters and trial status
function
SearchParameterConditions
(
props
):
any
{
const
{
parameter
,
searchFilter
,
dismiss
,
changeSearchFilterList
,
updatePage
,
setSearchInputVal
}
=
props
;
const
isChoiceTypeSearchFilter
=
parameter
===
'
StatusNNI
'
||
EXPERIMENT
.
searchSpace
[
parameter
].
_type
===
'
choice
'
;
const
operatorList
=
isChoiceTypeSearchFilter
?
[
'
=
'
,
'
≠
'
]
:
[
'
between
'
,
'
>
'
,
'
<
'
,
'
=
'
,
'
≠
'
];
const
initValueList
=
getInitVal
();
const
[
operatorVal
,
setOperatorVal
]
=
useState
(
initValueList
[
0
]);
const
[
firstInputVal
,
setFirstInputVal
]
=
useState
(
initValueList
[
1
]
as
string
);
const
[
secondInputVal
,
setSecondInputVal
]
=
useState
(
initValueList
[
2
]
as
string
);
// status or choice parameter dropdown selected value list
const
[
choiceList
,
setChoiceList
]
=
useState
(
initValueList
[
3
]
as
string
[]);
function
getInitVal
():
Array
<
string
|
string
[]
>
{
// push value: operator, firstInputVal(value1), secondInputVal(value2), choiceValue
const
str
:
Array
<
string
|
string
[]
>
=
[];
if
(
searchFilter
.
length
>
0
)
{
const
filterElement
=
searchFilter
.
find
(
ele
=>
ele
.
name
===
parameter
);
if
(
filterElement
!==
undefined
)
{
str
.
push
(
filterElement
.
operator
,
filterElement
.
value1
.
toString
(),
filterElement
.
value2
.
toString
(),
filterElement
.
choice
.
toString
().
split
(
'
,
'
)
);
}
else
{
// set init value
str
.
push
(
`
${
isChoiceTypeSearchFilter
?
'
=
'
:
'
between
'
}
`
,
''
,
''
,
[]
as
string
[]);
}
}
else
{
str
.
push
(
`
${
isChoiceTypeSearchFilter
?
'
=
'
:
'
between
'
}
`
,
''
,
''
,
[]
as
string
[]);
}
return
str
;
}
function
updateOperatorDropdown
(
_event
:
React
.
FormEvent
<
HTMLDivElement
>
,
item
:
IDropdownOption
|
undefined
):
void
{
if
(
item
!==
undefined
)
{
setOperatorVal
(
item
.
key
.
toString
());
}
}
// get [status | parameters that type is choice] list
function
updateChoiceDropdown
(
_event
:
React
.
FormEvent
<
HTMLDivElement
>
,
item
:
IDropdownOption
|
undefined
):
void
{
if
(
item
!==
undefined
)
{
const
result
=
item
.
selected
?
[...
choiceList
,
item
.
key
as
string
]
:
choiceList
.
filter
(
key
=>
key
!==
item
.
key
);
setChoiceList
(
result
);
}
}
function
updateFirstInputVal
(
ev
:
React
.
ChangeEvent
<
HTMLInputElement
>
):
void
{
setFirstInputVal
(
ev
.
target
.
value
);
}
function
updateSecondInputVal
(
ev
:
React
.
ChangeEvent
<
HTMLInputElement
>
):
void
{
setSecondInputVal
(
ev
.
target
.
value
);
}
function
getSecondInputVal
():
string
{
if
(
secondInputVal
===
''
&&
operatorVal
===
'
between
'
)
{
// if user uses 'between' operator and doesn't write the second input value,
// help to set second value as this parameter max value
return
EXPERIMENT
.
searchSpace
[
parameter
].
_value
[
1
].
toString
();
}
return
secondInputVal
as
string
;
}
// click Apply button
function
startFilterTrials
():
void
{
if
(
isChoiceTypeSearchFilter
===
false
)
{
if
(
firstInputVal
===
''
)
{
alert
(
'
Please input related value!
'
);
return
;
}
}
if
(
firstInputVal
.
match
(
/
[
a-zA-Z
]
/
)
||
secondInputVal
.
match
(
/
[
a-zA-Z
]
/
))
{
alert
(
'
Please input a number!
'
);
return
;
}
let
newSearchFilters
=
JSON
.
parse
(
JSON
.
stringify
(
searchFilter
));
const
find
=
newSearchFilters
.
filter
(
ele
=>
ele
.
name
===
parameter
);
if
(
find
.
length
>
0
)
{
// if user clear all selected options, will clear this filter condition on the searchFilter list
// eg: conv_size -> choiceList = [], searchFilter will remove (name === 'conv_size')
if
((
isChoiceTypeSearchFilter
&&
choiceList
.
length
!==
0
)
||
isChoiceTypeSearchFilter
===
false
)
{
newSearchFilters
.
forEach
(
item
=>
{
if
(
item
.
name
===
parameter
)
{
item
.
operator
=
operatorVal
;
item
.
value1
=
firstInputVal
;
item
.
value2
=
getSecondInputVal
();
item
.
choice
=
choiceList
;
item
.
isChoice
=
isChoiceTypeSearchFilter
?
true
:
false
;
}
});
}
else
{
newSearchFilters
=
newSearchFilters
.
filter
(
item
=>
item
.
name
!==
parameter
);
}
}
else
{
if
((
isChoiceTypeSearchFilter
&&
choiceList
.
length
!==
0
)
||
isChoiceTypeSearchFilter
===
false
)
{
newSearchFilters
.
push
({
name
:
parameter
,
operator
:
operatorVal
,
value1
:
firstInputVal
,
value2
:
getSecondInputVal
(),
choice
:
choiceList
,
isChoice
:
isChoiceTypeSearchFilter
?
true
:
false
});
}
}
setSearchInputVal
(
getSearchInputValueBySearchList
(
newSearchFilters
));
changeSearchFilterList
(
newSearchFilters
);
updatePage
();
dismiss
();
// close menu
}
return
(
// for trial parameters & Status
<
Stack
horizontal
className
=
'filterConditions'
tokens
=
{
searchConditonsGap
}
>
<
Dropdown
selectedKey
=
{
operatorVal
}
options
=
{
operatorList
.
map
(
item
=>
({
key
:
item
,
text
:
item
}))
}
onChange
=
{
updateOperatorDropdown
}
className
=
'btn-vertical-middle'
styles
=
{
{
root
:
{
width
:
100
}
}
}
/>
{
isChoiceTypeSearchFilter
?
(
<
Dropdown
// selectedKeys:[] multiy, selectedKey: string
selectedKeys
=
{
choiceList
}
multiSelect
options
=
{
getDropdownOptions
(
parameter
)
}
onChange
=
{
updateChoiceDropdown
}
className
=
'btn-vertical-middle'
styles
=
{
{
root
:
{
width
:
190
}
}
}
/>
)
:
(
<
React
.
Fragment
>
{
operatorVal
===
'
between
'
?
(
<
div
>
<
input
type
=
'text'
className
=
'input input-padding'
onChange
=
{
updateFirstInputVal
}
value
=
{
firstInputVal
}
/>
<
span
className
=
'and'
>
and
</
span
>
<
input
type
=
'text'
className
=
'input input-padding'
onChange
=
{
updateSecondInputVal
}
value
=
{
secondInputVal
}
/>
</
div
>
)
:
(
<
input
type
=
'text'
className
=
'input input-padding'
onChange
=
{
updateFirstInputVal
}
value
=
{
firstInputVal
}
/>
)
}
</
React
.
Fragment
>
)
}
<
PrimaryButton
text
=
'Apply'
className
=
'btn-vertical-middle'
onClick
=
{
startFilterTrials
}
/>
</
Stack
>
);
}
SearchParameterConditions
.
propTypes
=
{
parameter
:
PropTypes
.
string
,
searchFilter
:
PropTypes
.
array
,
dismiss
:
PropTypes
.
func
,
setSearchInputVal
:
PropTypes
.
func
,
changeSearchFilterList
:
PropTypes
.
func
,
updatePage
:
PropTypes
.
func
};
export
default
SearchParameterConditions
;
ts/webui/src/components/trial-detail/search/searchFunction.ts
0 → 100644
View file @
4ccc9402
import
{
mergeStyleSets
}
from
'
@fluentui/react
'
;
import
{
trialJobStatus
}
from
'
../../../static/const
'
;
import
{
EXPERIMENT
}
from
'
../../../static/datamodel
'
;
import
{
TableObj
,
SearchItems
}
from
'
../../../static/interface
'
;
const
classNames
=
mergeStyleSets
({
menu
:
{
textAlign
:
'
center
'
,
maxWidth
:
600
,
selectors
:
{
'
.ms-ContextualMenu-item
'
:
{
height
:
'
auto
'
}
}
},
item
:
{
display
:
'
inline-block
'
,
width
:
40
,
height
:
40
,
lineHeight
:
40
,
textAlign
:
'
center
'
,
verticalAlign
:
'
middle
'
,
marginBottom
:
8
,
cursor
:
'
pointer
'
,
selectors
:
{
'
&:hover
'
:
{
backgroundColor
:
'
#eaeaea
'
}
}
},
categoriesList
:
{
margin
:
0
,
padding
:
0
,
listStyleType
:
'
none
'
},
button
:
{
width
:
'
40%
'
,
margin
:
'
2%
'
}
});
function
getDropdownOptions
(
parameter
):
any
{
if
(
parameter
===
'
StatusNNI
'
)
{
return
trialJobStatus
.
map
(
item
=>
({
key
:
item
,
text
:
item
}));
}
else
{
return
EXPERIMENT
.
searchSpace
[
parameter
].
_value
.
map
(
item
=>
({
key
:
item
.
toString
(),
text
:
item
.
toString
()
}));
}
}
// change origin data according to parameter type, string -> number
const
convertParametersValue
=
(
searchItems
:
SearchItems
[],
relation
:
Map
<
string
,
string
>
):
SearchItems
[]
=>
{
const
choice
:
any
[]
=
[];
searchItems
.
forEach
(
item
=>
{
if
(
relation
.
get
(
item
.
name
)
===
'
number
'
)
{
if
(
item
.
isChoice
===
true
)
{
item
.
choice
.
forEach
(
ele
=>
{
choice
.
push
(
JSON
.
parse
(
ele
));
});
item
.
choice
=
choice
;
}
else
{
item
.
value1
=
JSON
.
parse
(
item
.
value1
);
if
(
item
.
value2
!==
''
)
{
item
.
value2
=
JSON
.
parse
(
item
.
value2
);
}
}
}
});
return
searchItems
;
};
// relation: trial parameter -> type {conv_size -> number}
const
getTrialsBySearchFilters
=
(
arr
:
TableObj
[],
searchItems
:
SearchItems
[],
relation
:
Map
<
string
,
string
>
):
TableObj
[]
=>
{
const
que
=
convertParametersValue
(
searchItems
,
relation
);
// start to filter data by ['Trial id', 'Trial No.', 'Status'] [...parameters]...
que
.
forEach
(
element
=>
{
if
(
element
.
name
===
'
Trial id
'
)
{
arr
=
arr
.
filter
(
trial
=>
trial
.
id
.
toUpperCase
().
includes
(
element
.
value1
.
toUpperCase
()));
}
else
if
(
element
.
name
===
'
Trial No.
'
)
{
arr
=
arr
.
filter
(
trial
=>
trial
.
sequenceId
.
toString
()
===
element
.
value1
);
}
else
if
(
element
.
name
===
'
StatusNNI
'
)
{
arr
=
searchChoiceFilter
(
arr
,
element
,
'
status
'
);
}
else
{
const
parameter
=
`space/
${
element
.
name
}
`
;
if
(
element
.
isChoice
===
true
)
{
arr
=
searchChoiceFilter
(
arr
,
element
,
element
.
name
);
}
else
{
if
(
element
.
operator
===
'
=
'
)
{
arr
=
arr
.
filter
(
trial
=>
trial
[
parameter
]
===
element
.
value1
);
}
else
if
(
element
.
operator
===
'
>
'
)
{
arr
=
arr
.
filter
(
trial
=>
trial
[
parameter
]
>
element
.
value1
);
}
else
if
(
element
.
operator
===
'
<
'
)
{
arr
=
arr
.
filter
(
trial
=>
trial
[
parameter
]
<
element
.
value1
);
}
else
if
(
element
.
operator
===
'
between
'
)
{
arr
=
arr
.
filter
(
trial
=>
trial
[
parameter
]
>
element
.
value1
&&
trial
[
parameter
]
<
element
.
value2
);
}
else
{
// operator is '≠'
arr
=
arr
.
filter
(
trial
=>
trial
[
parameter
]
!==
element
.
value1
);
}
}
}
});
return
arr
;
};
// isChoice = true: status and trial parameters
function
findTrials
(
arr
:
TableObj
[],
choice
:
string
[],
filed
:
string
):
TableObj
[]
{
const
newResult
:
TableObj
[]
=
[];
const
parameter
=
filed
===
'
status
'
?
'
status
'
:
`space/
${
filed
}
`
;
arr
.
forEach
(
trial
=>
{
choice
.
forEach
(
item
=>
{
if
(
trial
[
parameter
]
===
item
)
{
newResult
.
push
(
trial
);
}
});
});
return
newResult
;
}
function
searchChoiceFilter
(
arr
:
TableObj
[],
element
:
SearchItems
,
field
:
string
):
TableObj
[]
{
if
(
element
.
operator
===
'
=
'
)
{
return
findTrials
(
arr
,
element
.
choice
,
field
);
}
else
{
let
choice
;
if
(
field
===
'
status
'
)
{
choice
=
trialJobStatus
.
filter
(
index
=>
!
new
Set
(
element
.
choice
).
has
(
index
));
}
else
{
choice
=
EXPERIMENT
.
searchSpace
[
field
].
_value
.
filter
(
index
=>
!
new
Set
(
element
.
choice
).
has
(
index
));
}
return
findTrials
(
arr
,
choice
,
field
);
}
}
// click Apply btn: set searchBox value now
function
getSearchInputValueBySearchList
(
searchFilter
):
string
{
let
str
=
''
;
// store search input value
searchFilter
.
forEach
(
item
=>
{
const
filterName
=
item
.
name
===
'
StatusNNI
'
?
'
Status
'
:
item
.
name
;
if
(
item
.
isChoice
===
false
)
{
// id, No, !choice parameter
if
(
item
.
name
===
'
Trial id
'
||
item
.
name
===
'
Trial No.
'
)
{
str
=
str
+
`
${
item
.
name
}
:
${
item
.
value1
}
; `
;
}
else
{
// !choice parameter
if
([
'
=
'
,
'
≠
'
,
'
>
'
,
'
<
'
].
includes
(
item
.
operator
))
{
str
=
str
+
`
${
filterName
}${
item
.
operator
===
'
=
'
?
'
:
'
:
item
.
operator
}${
item
.
value1
}
; `
;
}
else
{
// between
str
=
str
+
`
${
filterName
}
:[
${
item
.
value1
}
,
${
item
.
value2
}
]; `
;
}
}
}
else
{
// status, choice parameter
str
=
str
+
`
${
filterName
}${
item
.
operator
===
'
=
'
?
'
:
'
:
'
≠
'
}
[
${[...
item
.
choice
]}
]; `
;
}
});
return
str
;
}
/***
* from experiment search space
* "conv_size": {
"_type": "choice", // is choice type
"_value": [
2,
3,
5,
7
]
},
*/
function
isChoiceType
(
parameterName
):
boolean
{
// 判断是 [choice, status] 还是普通的类型
let
flag
=
false
;
// normal type
if
(
parameterName
===
'
StatusNNI
'
)
{
flag
=
true
;
}
if
(
parameterName
in
EXPERIMENT
.
searchSpace
)
{
flag
=
EXPERIMENT
.
searchSpace
[
parameterName
].
_type
===
'
choice
'
?
true
:
false
;
}
return
flag
;
}
export
{
classNames
,
getDropdownOptions
,
getTrialsBySearchFilters
,
getSearchInputValueBySearchList
,
isChoiceType
};
ts/webui/src/static/function.ts
View file @
4ccc9402
...
@@ -2,6 +2,7 @@ import * as JSON5 from 'json5';
...
@@ -2,6 +2,7 @@ import * as JSON5 from 'json5';
import
axios
from
'
axios
'
;
import
axios
from
'
axios
'
;
import
{
IContextualMenuProps
}
from
'
@fluentui/react
'
;
import
{
IContextualMenuProps
}
from
'
@fluentui/react
'
;
import
{
MANAGER_IP
}
from
'
./const
'
;
import
{
MANAGER_IP
}
from
'
./const
'
;
import
{
EXPERIMENT
}
from
'
./datamodel
'
;
import
{
MetricDataRecord
,
FinalType
,
TableObj
,
Tensorboard
}
from
'
./interface
'
;
import
{
MetricDataRecord
,
FinalType
,
TableObj
,
Tensorboard
}
from
'
./interface
'
;
function
getPrefix
():
string
|
undefined
{
function
getPrefix
():
string
|
undefined
{
...
@@ -356,6 +357,19 @@ function getTensorboardMenu(queryTensorboardList: Tensorboard[], stopFunc, seeDe
...
@@ -356,6 +357,19 @@ function getTensorboardMenu(queryTensorboardList: Tensorboard[], stopFunc, seeDe
return
tensorboardMenu
;
return
tensorboardMenu
;
}
}
// search space type map list: now get type from search space
const
parametersType
=
():
Map
<
string
,
string
>
=>
{
const
parametersTypeMap
=
new
Map
();
const
trialParameterlist
=
Object
.
keys
(
EXPERIMENT
.
searchSpace
);
trialParameterlist
.
forEach
(
item
=>
{
parametersTypeMap
.
set
(
item
,
typeof
EXPERIMENT
.
searchSpace
[
item
].
_value
[
0
]);
});
return
parametersTypeMap
;
};
export
{
export
{
getPrefix
,
getPrefix
,
convertTime
,
convertTime
,
...
@@ -381,5 +395,6 @@ export {
...
@@ -381,5 +395,6 @@ export {
caclMonacoEditorHeight
,
caclMonacoEditorHeight
,
copyAndSort
,
copyAndSort
,
disableTensorboard
,
disableTensorboard
,
getTensorboardMenu
getTensorboardMenu
,
parametersType
};
};
ts/webui/src/static/interface.ts
View file @
4ccc9402
...
@@ -203,6 +203,16 @@ interface Tensorboard {
...
@@ -203,6 +203,16 @@ interface Tensorboard {
port
:
string
;
port
:
string
;
}
}
// for TableList search
interface
SearchItems
{
name
:
string
;
operator
:
string
;
value1
:
string
;
// first input value
value2
:
string
;
// second input value
choice
:
string
[];
// use select multiy value list
isChoice
:
boolean
;
// for parameters: type = choice and status also as choice type
}
export
{
export
{
TableObj
,
TableObj
,
TableRecord
,
TableRecord
,
...
@@ -226,5 +236,6 @@ export {
...
@@ -226,5 +236,6 @@ export {
MultipleAxes
,
MultipleAxes
,
SortInfo
,
SortInfo
,
AllExperimentList
,
AllExperimentList
,
Tensorboard
Tensorboard
,
SearchItems
};
};
ts/webui/src/static/style/common.scss
View file @
4ccc9402
...
@@ -73,3 +73,7 @@ $themeBlue: #0071bc;
...
@@ -73,3 +73,7 @@ $themeBlue: #0071bc;
.bold
{
.bold
{
font-weight
:
bold
;
font-weight
:
bold
;
}
}
.input-padding
{
padding-left
:
10px
;
}
ts/webui/src/static/style/search.scss
View file @
4ccc9402
...
@@ -3,23 +3,17 @@
...
@@ -3,23 +3,17 @@
width
:
96%
;
width
:
96%
;
margin
:
0
auto
;
margin
:
0
auto
;
margin-top
:
15px
;
margin-top
:
15px
;
position
:
relative
;
&
-compare
{
&
-compare
{
margin-top
:
15px
;
margin-top
:
15px
;
}
}
&
-entry
{
line-height
:
32px
;
}
/* compare button style */
/* compare button style */
&
-button-gap
{
&
-button-gap
{
margin-right
:
10px
;
margin-right
:
10px
;
}
}
&
-search-input
{
padding-left
:
10px
;
}
}
}
/* each row's Intermediate btn -> Modal */
/* each row's Intermediate btn -> Modal */
...
@@ -33,3 +27,32 @@
...
@@ -33,3 +27,32 @@
width
:
120px
;
width
:
120px
;
}
}
}
}
$filterConditionsHeight
:
54px
;
.filterConditions
{
height
:
$filterConditionsHeight
;
line-height
:
$filterConditionsHeight
;
padding
:
0
15px
;
.btn-vertical-middle
{
margin-top
:
11px
;
}
.input
{
width
:
100px
;
height
:
24px
;
margin-top
:
13px
;
border
:
1px
solid
#333
;
border-radius
:
20px
;
outline
:
none
;
}
.and
{
margin
:
0
4px
;
}
}
.ms-ContextualMenu-Callout
{
display
:
block
;
}
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