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
593a275c
Commit
593a275c
authored
Dec 14, 2020
by
Yuge Zhang
Browse files
Merge branch 'master' of
https://github.com/microsoft/nni
into dev-retiarii
parents
b3cdee85
683c458a
Changes
85
Show whitespace changes
Inline
Side-by-side
Showing
20 changed files
with
785 additions
and
161 deletions
+785
-161
setup.py
setup.py
+26
-2
ts/nni_manager/rest_server/restValidationSchemas.ts
ts/nni_manager/rest_server/restValidationSchemas.ts
+1
-0
ts/nni_manager/training_service/kubernetes/adl/adlApiClient.ts
...i_manager/training_service/kubernetes/adl/adlApiClient.ts
+8
-5
ts/nni_manager/training_service/kubernetes/adl/adlConfig.ts
ts/nni_manager/training_service/kubernetes/adl/adlConfig.ts
+5
-1
ts/nni_manager/training_service/kubernetes/adl/adlJobInfoCollector.ts
...er/training_service/kubernetes/adl/adlJobInfoCollector.ts
+5
-5
ts/nni_manager/training_service/kubernetes/adl/adlTrainingService.ts
...ger/training_service/kubernetes/adl/adlTrainingService.ts
+20
-2
ts/nni_manager/training_service/kubernetes/kubernetesApiClient.ts
...anager/training_service/kubernetes/kubernetesApiClient.ts
+13
-7
ts/nni_manager/training_service/kubernetes/kubernetesTrainingService.ts
.../training_service/kubernetes/kubernetesTrainingService.ts
+0
-7
ts/nni_manager/training_service/local/gpuScheduler.ts
ts/nni_manager/training_service/local/gpuScheduler.ts
+2
-2
ts/nni_manager/training_service/local/localTrainingService.ts
...ni_manager/training_service/local/localTrainingService.ts
+2
-2
ts/webui/src/App.scss
ts/webui/src/App.scss
+2
-0
ts/webui/src/App.tsx
ts/webui/src/App.tsx
+58
-69
ts/webui/src/components/NavCon.tsx
ts/webui/src/components/NavCon.tsx
+45
-56
ts/webui/src/components/NavConst.ts
ts/webui/src/components/NavConst.ts
+15
-0
ts/webui/src/components/Overview.tsx
ts/webui/src/components/Overview.tsx
+2
-2
ts/webui/src/components/buttons/Icon.tsx
ts/webui/src/components/buttons/Icon.tsx
+7
-1
ts/webui/src/components/managementExp/ExperimentManager.tsx
ts/webui/src/components/managementExp/ExperimentManager.tsx
+434
-0
ts/webui/src/components/managementExp/FilterBtns.tsx
ts/webui/src/components/managementExp/FilterBtns.tsx
+81
-0
ts/webui/src/components/managementExp/Header.tsx
ts/webui/src/components/managementExp/Header.tsx
+26
-0
ts/webui/src/components/managementExp/TrialIdColumn.tsx
ts/webui/src/components/managementExp/TrialIdColumn.tsx
+33
-0
No files found.
setup.py
View file @
593a275c
...
...
@@ -65,7 +65,6 @@ dependencies = [
'ruamel.yaml'
,
'requests'
,
'responses'
,
'scipy'
,
'schema'
,
'PythonWebHDFS'
,
'colorama'
,
...
...
@@ -77,7 +76,9 @@ dependencies = [
'dataclasses ; python_version < "3.7"'
,
'numpy < 1.19.4 ; sys_platform == "win32"'
,
'numpy < 1.20 ; sys_platform != "win32" and python_version < "3.7"'
,
'numpy ; sys.platform != "win32" and python_version >= "3.7"'
'numpy ; sys.platform != "win32" and python_version >= "3.7"'
,
'scipy < 1.6 ; python_version < "3.7"'
,
'scipy ; python_version >= "3.7"'
,
]
release
=
os
.
environ
.
get
(
'NNI_RELEASE'
)
...
...
@@ -110,6 +111,14 @@ def _setup():
python_requires
=
'>=3.6'
,
install_requires
=
dependencies
,
extras_require
=
{
'SMAC'
:
[
'ConfigSpaceNNI @ git+https://github.com/QuanluZhang/ConfigSpace.git'
,
'smac @ git+https://github.com/QuanluZhang/SMAC3.git'
],
'BOHB'
:
[
'ConfigSpace==0.4.7'
,
'statsmodels==0.10.0'
],
'PPOTuner'
:
[
'enum34'
,
'gym'
]
},
setup_requires
=
[
'requests'
],
entry_points
=
{
...
...
@@ -157,6 +166,19 @@ def _find_node_files():
def
_using_conda_or_virtual_environment
():
return
sys
.
prefix
!=
sys
.
base_prefix
or
os
.
path
.
isdir
(
os
.
path
.
join
(
sys
.
prefix
,
'conda-meta'
))
def
_copy_data_files
():
# after installation, nni needs to find this location in nni.tools.package_utils.get_registered_algo_config_path
# since we can not import nni here, we need to ensure get_registered_algo_config_path use the same
# logic here to retrieve registered_algorithms.yml
if
_using_conda_or_virtual_environment
():
nni_config_dir
=
os
.
path
.
join
(
sys
.
prefix
,
'nni'
)
elif
sys
.
platform
==
'win32'
:
nni_config_dir
=
os
.
path
.
join
(
os
.
getenv
(
'APPDATA'
),
'nni'
)
else
:
nni_config_dir
=
os
.
path
.
expanduser
(
'~/.config/nni'
)
if
not
os
.
path
.
exists
(
nni_config_dir
):
os
.
makedirs
(
nni_config_dir
)
shutil
.
copyfile
(
'./deployment/registered_algorithms.yml'
,
os
.
path
.
join
(
nni_config_dir
,
'registered_algorithms.yml'
))
class
BuildTs
(
Command
):
description
=
'build TypeScript modules'
...
...
@@ -178,6 +200,7 @@ class Build(build):
sys
.
exit
(
'Please set environment variable "NNI_RELEASE=<release_version>"'
)
if
os
.
path
.
islink
(
'nni_node/main.js'
):
sys
.
exit
(
'A development build already exists. Please uninstall NNI and run "python3 setup.py clean --all".'
)
_copy_data_files
()
super
().
run
()
class
Develop
(
develop
):
...
...
@@ -203,6 +226,7 @@ class Develop(develop):
def
run
(
self
):
if
not
self
.
skip_ts
:
setup_ts
.
build
(
release
=
None
)
_copy_data_files
()
super
().
run
()
class
Clean
(
clean
):
...
...
ts/nni_manager/rest_server/restValidationSchemas.ts
View file @
593a275c
...
...
@@ -101,6 +101,7 @@ export namespace ValidationSchemas {
name
:
joi
.
string
().
min
(
1
).
required
()
}),
// ############## adl ###############
namespace
:
joi
.
string
(),
adaptive
:
joi
.
boolean
(),
checkpoint
:
joi
.
object
({
storageClass
:
joi
.
string
().
min
(
1
).
required
(),
...
...
ts/nni_manager/training_service/kubernetes/adl/adlApiClient.ts
View file @
593a275c
...
...
@@ -13,14 +13,17 @@ class AdlClientV1 extends KubernetesCRDClient {
/**
* constructor, to initialize adl CRD definition
*/
public
constructor
()
{
protected
readonly
namespace
:
string
;
public
constructor
(
namespace
:
string
)
{
super
();
this
.
namespace
=
namespace
;
this
.
crdSchema
=
JSON
.
parse
(
fs
.
readFileSync
(
'
./config/adl/adaptdl-crd-v1.json
'
,
'
utf8
'
));
this
.
client
.
addCustomResourceDefinition
(
this
.
crdSchema
);
}
protected
get
operator
():
any
{
return
this
.
client
.
apis
[
'
adaptdl.petuum.com
'
].
v1
.
namespaces
(
'
default
'
).
adaptdljobs
;
return
this
.
client
.
apis
[
'
adaptdl.petuum.com
'
].
v1
.
namespaces
(
this
.
namespace
).
adaptdljobs
;
}
public
get
containerName
():
string
{
...
...
@@ -29,7 +32,7 @@ class AdlClientV1 extends KubernetesCRDClient {
public
async
getKubernetesPods
(
jobName
:
string
):
Promise
<
any
>
{
let
result
:
Promise
<
any
>
;
const
response
=
await
this
.
client
.
api
.
v1
.
namespaces
(
'
default
'
).
pods
const
response
=
await
this
.
client
.
api
.
v1
.
namespaces
(
this
.
namespace
).
pods
.
get
({
qs
:
{
labelSelector
:
`adaptdl/job=
${
jobName
}
`
}
});
if
(
response
.
statusCode
&&
(
response
.
statusCode
>=
200
&&
response
.
statusCode
<=
299
))
{
result
=
Promise
.
resolve
(
response
.
body
);
...
...
@@ -47,8 +50,8 @@ class AdlClientFactory {
/**
* Factory method to generate operator client
*/
public
static
createClient
():
KubernetesCRDClient
{
return
new
AdlClientV1
();
public
static
createClient
(
namespace
:
string
):
KubernetesCRDClient
{
return
new
AdlClientV1
(
namespace
);
}
}
...
...
ts/nni_manager/training_service/kubernetes/adl/adlConfig.ts
View file @
593a275c
...
...
@@ -58,6 +58,8 @@ export class AdlTrialConfig extends KubernetesTrialConfig {
public
readonly
image
:
string
;
public
readonly
namespace
?:
string
;
public
readonly
imagePullSecrets
?:
ImagePullSecretConfig
[];
public
readonly
nfs
?:
NFSConfig
;
...
...
@@ -72,7 +74,8 @@ export class AdlTrialConfig extends KubernetesTrialConfig {
constructor
(
codeDir
:
string
,
command
:
string
,
gpuNum
:
number
,
image
:
string
,
imagePullSecrets
?:
ImagePullSecretConfig
[],
image
:
string
,
namespace
?:
string
,
imagePullSecrets
?:
ImagePullSecretConfig
[],
nfs
?:
NFSConfig
,
checkpoint
?:
CheckpointConfig
,
cpuNum
?:
number
,
memorySize
?:
string
,
adaptive
?:
boolean
...
...
@@ -81,6 +84,7 @@ export class AdlTrialConfig extends KubernetesTrialConfig {
this
.
command
=
command
;
this
.
gpuNum
=
gpuNum
;
this
.
image
=
image
;
this
.
namespace
=
namespace
;
this
.
imagePullSecrets
=
imagePullSecrets
;
this
.
nfs
=
nfs
;
this
.
checkpoint
=
checkpoint
;
...
...
ts/nni_manager/training_service/kubernetes/adl/adlJobInfoCollector.ts
View file @
593a275c
...
...
@@ -16,21 +16,21 @@ export class AdlJobInfoCollector extends KubernetesJobInfoCollector {
super
(
jobMap
);
}
protected
async
retrieveSingleTrialJobInfo
(
kubernetesCRD
Client
:
AdlClientV1
|
undefined
,
protected
async
retrieveSingleTrialJobInfo
(
adl
Client
:
AdlClientV1
|
undefined
,
kubernetesTrialJob
:
KubernetesTrialJobDetail
):
Promise
<
void
>
{
if
(
!
this
.
statusesNeedToCheck
.
includes
(
kubernetesTrialJob
.
status
))
{
return
Promise
.
resolve
();
}
if
(
kubernetesCRD
Client
===
undefined
)
{
return
Promise
.
reject
(
'
kubernetesCRD
Client is undefined
'
);
if
(
adl
Client
===
undefined
)
{
return
Promise
.
reject
(
'
Adl
Client is undefined
'
);
}
let
kubernetesJobInfo
:
any
;
let
kubernetesPodsInfo
:
any
;
try
{
kubernetesJobInfo
=
await
kubernetesCRD
Client
.
getKubernetesJob
(
kubernetesTrialJob
.
kubernetesJobName
);
kubernetesPodsInfo
=
await
kubernetesCRD
Client
.
getKubernetesPods
(
kubernetesTrialJob
.
kubernetesJobName
);
kubernetesJobInfo
=
await
adl
Client
.
getKubernetesJob
(
kubernetesTrialJob
.
kubernetesJobName
);
kubernetesPodsInfo
=
await
adl
Client
.
getKubernetesPods
(
kubernetesTrialJob
.
kubernetesJobName
);
}
catch
(
error
)
{
// Notice: it maynot be a 'real' error since cancel trial job can also cause getKubernetesJob failed.
this
.
log
.
error
(
`Get job
${
kubernetesTrialJob
.
kubernetesJobName
}
info failed, error is
${
error
}
`
);
...
...
ts/nni_manager/training_service/kubernetes/adl/adlTrainingService.ts
View file @
593a275c
...
...
@@ -39,7 +39,6 @@ class AdlTrainingService extends KubernetesTrainingService implements Kubernetes
super
();
this
.
adlJobInfoCollector
=
new
AdlJobInfoCollector
(
this
.
trialJobsMap
);
this
.
experimentId
=
getExperimentId
();
this
.
kubernetesCRDClient
=
AdlClientFactory
.
createClient
();
this
.
configmapTemplateStr
=
fs
.
readFileSync
(
'
./config/adl/adaptdl-nni-configmap-template.json
'
,
'
utf8
'
);
this
.
jobTemplateStr
=
fs
.
readFileSync
(
'
./config/adl/adaptdljob-template.json
'
,
'
utf8
'
);
...
...
@@ -294,15 +293,34 @@ python3 -m nni.tools.trial_tool.trial_keeper --trial_command '{8}' \
return
Promise
.
resolve
(
runScript
);
}
public
async
cleanUp
():
Promise
<
void
>
{
super
.
cleanUp
();
// Delete Tensorboard deployment
try
{
await
this
.
genericK8sClient
.
deleteDeployment
(
"
adaptdl-tensorboard-
"
+
this
.
experimentId
.
toLowerCase
());
this
.
log
.
info
(
'
tensorboard deployment deleted
'
);
}
catch
(
error
)
{
this
.
log
.
error
(
`tensorboard deployment deletion failed:
${
error
.
message
}
`
);
}
}
public
async
setClusterMetadata
(
key
:
string
,
value
:
string
):
Promise
<
void
>
{
this
.
log
.
info
(
'
SetCluster
'
+
key
+
'
,
'
+
value
);
switch
(
key
)
{
case
TrialConfigMetadataKey
.
NNI_MANAGER_IP
:
this
.
nniManagerIpConfig
=
<
NNIManagerIpConfig
>
JSON
.
parse
(
value
);
break
;
case
TrialConfigMetadataKey
.
TRIAL_CONFIG
:
case
TrialConfigMetadataKey
.
TRIAL_CONFIG
:
{
this
.
adlTrialConfig
=
<
AdlTrialConfig
>
JSON
.
parse
(
value
);
let
namespace
:
string
=
'
default
'
;
if
(
this
.
adlTrialConfig
.
namespace
!==
undefined
)
{
namespace
=
this
.
adlTrialConfig
.
namespace
;
}
this
.
genericK8sClient
.
setNamespace
=
namespace
;
this
.
kubernetesCRDClient
=
AdlClientFactory
.
createClient
(
namespace
);
break
;
}
case
TrialConfigMetadataKey
.
VERSION_CHECK
:
this
.
versionCheck
=
(
value
===
'
true
'
||
value
===
'
True
'
);
break
;
...
...
ts/nni_manager/training_service/kubernetes/kubernetesApiClient.ts
View file @
593a275c
...
...
@@ -8,17 +8,22 @@ import { Client1_10, config } from 'kubernetes-client';
import
{
getLogger
,
Logger
}
from
'
../../common/log
'
;
/**
* Generic
t
Kubernetes client, target version >= 1.9
* Generic Kubernetes client, target version >= 1.9
*/
class
GeneralK8sClient
{
protected
readonly
client
:
any
;
protected
readonly
log
:
Logger
=
getLogger
();
protected
namespace
:
string
=
'
default
'
;
constructor
()
{
this
.
client
=
new
Client1_10
({
config
:
config
.
fromKubeconfig
(),
version
:
'
1.9
'
});
this
.
client
.
loadSpec
();
}
public
set
setNamespace
(
namespace
:
string
)
{
this
.
namespace
=
namespace
;
}
private
matchStorageClass
(
response
:
any
):
string
{
const
adlSupportedProvisioners
:
RegExp
[]
=
[
new
RegExp
(
"
microk8s.io/hostpath
"
),
...
...
@@ -60,7 +65,8 @@ class GeneralK8sClient {
public
async
createDeployment
(
deploymentManifest
:
any
):
Promise
<
string
>
{
let
result
:
Promise
<
string
>
;
const
response
:
any
=
await
this
.
client
.
apis
.
apps
.
v1
.
namespaces
(
'
default
'
).
deployments
.
post
({
body
:
deploymentManifest
})
const
response
:
any
=
await
this
.
client
.
apis
.
apps
.
v1
.
namespaces
(
this
.
namespace
)
.
deployments
.
post
({
body
:
deploymentManifest
})
if
(
response
.
statusCode
&&
(
response
.
statusCode
>=
200
&&
response
.
statusCode
<=
299
))
{
result
=
Promise
.
resolve
(
response
.
body
.
metadata
.
uid
);
}
else
{
...
...
@@ -72,7 +78,7 @@ class GeneralK8sClient {
public
async
deleteDeployment
(
deploymentName
:
string
):
Promise
<
boolean
>
{
let
result
:
Promise
<
boolean
>
;
// TODO: change this hard coded deployment name after demo
const
response
:
any
=
await
this
.
client
.
apis
.
apps
.
v1
.
namespaces
(
'
default
'
)
const
response
:
any
=
await
this
.
client
.
apis
.
apps
.
v1
.
namespaces
(
this
.
namespace
)
.
deployment
(
deploymentName
).
delete
();
if
(
response
.
statusCode
&&
(
response
.
statusCode
>=
200
&&
response
.
statusCode
<=
299
))
{
result
=
Promise
.
resolve
(
true
);
...
...
@@ -84,7 +90,7 @@ class GeneralK8sClient {
public
async
createConfigMap
(
configMapManifest
:
any
):
Promise
<
boolean
>
{
let
result
:
Promise
<
boolean
>
;
const
response
:
any
=
await
this
.
client
.
api
.
v1
.
namespaces
(
'
default
'
)
const
response
:
any
=
await
this
.
client
.
api
.
v1
.
namespaces
(
this
.
namespace
)
.
configmaps
.
post
({
body
:
configMapManifest
});
if
(
response
.
statusCode
&&
(
response
.
statusCode
>=
200
&&
response
.
statusCode
<=
299
))
{
result
=
Promise
.
resolve
(
true
);
...
...
@@ -97,7 +103,7 @@ class GeneralK8sClient {
public
async
createPersistentVolumeClaim
(
pvcManifest
:
any
):
Promise
<
boolean
>
{
let
result
:
Promise
<
boolean
>
;
const
response
:
any
=
await
this
.
client
.
api
.
v1
.
namespaces
(
'
default
'
)
const
response
:
any
=
await
this
.
client
.
api
.
v1
.
namespaces
(
this
.
namespace
)
.
persistentvolumeclaims
.
post
({
body
:
pvcManifest
});
if
(
response
.
statusCode
&&
(
response
.
statusCode
>=
200
&&
response
.
statusCode
<=
299
))
{
result
=
Promise
.
resolve
(
true
);
...
...
@@ -109,8 +115,8 @@ class GeneralK8sClient {
public
async
createSecret
(
secretManifest
:
any
):
Promise
<
boolean
>
{
let
result
:
Promise
<
boolean
>
;
const
response
:
any
=
await
this
.
client
.
api
.
v1
.
namespaces
(
'
default
'
).
secrets
.
post
({
body
:
secretManifest
});
const
response
:
any
=
await
this
.
client
.
api
.
v1
.
namespaces
(
this
.
namespace
)
.
secrets
.
post
({
body
:
secretManifest
});
if
(
response
.
statusCode
&&
(
response
.
statusCode
>=
200
&&
response
.
statusCode
<=
299
))
{
result
=
Promise
.
resolve
(
true
);
}
else
{
...
...
ts/nni_manager/training_service/kubernetes/kubernetesTrainingService.ts
View file @
593a275c
...
...
@@ -209,13 +209,6 @@ abstract class KubernetesTrainingService {
return
Promise
.
reject
(
error
);
}
try
{
await
this
.
genericK8sClient
.
deleteDeployment
(
"
adaptdl-tensorboard-
"
+
getExperimentId
().
toLowerCase
())
this
.
log
.
info
(
'
tensorboard deployment deleted
'
)
}
catch
(
error
)
{
this
.
log
.
error
(
`tensorboard deployment deletion failed:
${
error
.
message
}
`
)
}
return
Promise
.
resolve
();
}
...
...
ts/nni_manager/training_service/local/gpuScheduler.ts
View file @
593a275c
...
...
@@ -58,12 +58,12 @@ class GPUScheduler {
return
[];
}
public
getSystemGpuCount
():
number
{
public
getSystemGpuCount
():
number
|
undefined
{
if
(
this
.
gpuSummary
!==
undefined
)
{
return
this
.
gpuSummary
.
gpuCount
;
}
return
0
;
return
undefined
;
}
public
async
stop
():
Promise
<
void
>
{
...
...
ts/nni_manager/training_service/local/localTrainingService.ts
View file @
593a275c
...
...
@@ -435,8 +435,8 @@ class LocalTrainingService implements TrainingService {
}
private
checkSpecifiedGpuIndices
():
void
{
const
gpuCount
:
number
=
this
.
gpuScheduler
.
getSystemGpuCount
();
if
(
this
.
designatedGpuIndices
!==
undefined
)
{
const
gpuCount
:
number
|
undefined
=
this
.
gpuScheduler
.
getSystemGpuCount
();
if
(
this
.
designatedGpuIndices
!==
undefined
&&
gpuCount
!==
undefined
)
{
for
(
const
index
of
this
.
designatedGpuIndices
)
{
if
(
index
>=
gpuCount
)
{
throw
new
Error
(
`Specified GPU index not found:
${
index
}
`
);
...
...
ts/webui/src/App.scss
View file @
593a275c
...
...
@@ -18,6 +18,7 @@
.headerCon
{
width
:
90%
;
min-width
:
1200px
;
max-width
:
1490px
;
margin
:
0
auto
;
}
...
...
@@ -28,6 +29,7 @@
.content
{
width
:
87%
;
min-height
:
calc
(
100vh
-
56
);
margin
:
0
auto
;
min-width
:
1200px
;
max-width
:
1490px
;
...
...
ts/webui/src/App.tsx
View file @
593a275c
...
...
@@ -2,6 +2,7 @@ import * as React from 'react';
import
{
Stack
}
from
'
@fluentui/react
'
;
import
{
COLUMN
}
from
'
./static/const
'
;
import
{
EXPERIMENT
,
TRIALS
}
from
'
./static/datamodel
'
;
import
{
isManagerExperimentPage
}
from
'
./static/function
'
;
import
NavCon
from
'
./components/NavCon
'
;
import
MessageInfo
from
'
./components/modals/MessageInfo
'
;
import
{
SlideNavBtns
}
from
'
./components/slideNav/SlideNavBtns
'
;
...
...
@@ -29,15 +30,15 @@ export const AppContext = React.createContext({
metricGraphMode
:
'
max
'
,
bestTrialEntries
:
'
10
'
,
maxDurationUnit
:
'
m
'
,
// eslint-disable-next-line @typescript-eslint/no-empty-function
, @typescript-eslint/no-unused-vars
changeColumn
:
(
val
:
string
[])
=>
{},
// eslint-disable-next-line @typescript-eslint/no-empty-function
, @typescript-eslint/no-unused-vars
changeMetricGraphMode
:
(
val
:
'
max
'
|
'
min
'
)
=>
{},
// eslint-disable-next-line @typescript-eslint/no-empty-function
, @typescript-eslint/no-unused-vars
changeMaxDurationUnit
:
(
val
:
string
)
=>
{},
// eslint-disable-next-line @typescript-eslint/no-empty-function
, @typescript-eslint/no-unused-vars
changeEntries
:
(
val
:
string
)
=>
{},
// eslint-disable-next-line @typescript-eslint/no-empty-function
, @typescript-eslint/no-unused-vars
// eslint-disable-next-line @typescript-eslint/no-empty-function
changeColumn
:
(
_
val
:
string
[])
:
void
=>
{},
// eslint-disable-next-line @typescript-eslint/no-empty-function
changeMetricGraphMode
:
(
_
val
:
'
max
'
|
'
min
'
)
:
void
=>
{},
// eslint-disable-next-line @typescript-eslint/no-empty-function
changeMaxDurationUnit
:
(
_
val
:
string
)
:
void
=>
{},
// eslint-disable-next-line @typescript-eslint/no-empty-function
changeEntries
:
(
_
val
:
string
)
:
void
=>
{},
// eslint-disable-next-line @typescript-eslint/no-empty-function
updateOverviewPage
:
()
=>
{}
});
...
...
@@ -139,7 +140,10 @@ class App extends React.Component<{}, AppState> {
{
errorWhere
:
TRIALS
.
latestMetricDataError
(),
errorMessage
:
TRIALS
.
getLatestMetricDataErrorMessage
()
},
{
errorWhere
:
TRIALS
.
metricDataRangeError
(),
errorMessage
:
TRIALS
.
metricDataRangeErrorMessage
()
}
];
return
(
<
React
.
Fragment
>
{
isManagerExperimentPage
()
?
null
:
(
<
Stack
className
=
'nni'
style
=
{
{
minHeight
:
window
.
innerHeight
}
}
>
<
div
className
=
'header'
>
<
div
className
=
'headerCon'
>
...
...
@@ -149,24 +153,7 @@ class App extends React.Component<{}, AppState> {
<
Stack
className
=
'contentBox'
>
<
Stack
className
=
'content'
>
{
/* search space & config */
}
<
AppContext
.
Provider
value
=
{
{
interval
,
columnList
,
changeColumn
:
this
.
changeColumn
,
experimentUpdateBroadcast
,
trialsUpdateBroadcast
,
metricGraphMode
,
maxDurationUnit
,
changeMaxDurationUnit
:
this
.
changeMaxDurationUnit
,
changeMetricGraphMode
:
this
.
changeMetricGraphMode
,
bestTrialEntries
,
changeEntries
:
this
.
changeEntries
,
updateOverviewPage
:
this
.
updateOverviewPage
}
}
>
<
SlideNavBtns
/>
</
AppContext
.
Provider
>
{
/* if api has error field, show error message */
}
{
errorList
.
map
(
(
item
,
key
)
=>
...
...
@@ -202,6 +189,8 @@ class App extends React.Component<{}, AppState> {
</
Stack
>
</
Stack
>
</
Stack
>
)
}
</
React
.
Fragment
>
);
}
...
...
ts/webui/src/components/NavCon.tsx
View file @
593a275c
import
*
as
React
from
'
react
'
;
import
axios
from
'
axios
'
;
import
{
WEBUIDOC
,
MANAGER_IP
}
from
'
../static/const
'
;
import
{
Stack
,
initializeIcons
,
StackItem
,
CommandBarButton
,
IContextualMenuProps
,
IStackTokens
,
IStackStyles
}
from
'
@fluentui/react
'
;
import
{
Stack
,
StackItem
,
CommandBarButton
,
IContextualMenuProps
}
from
'
@fluentui/react
'
;
import
{
Link
}
from
'
react-router-dom
'
;
import
{
infoIconAbout
,
timeIcon
,
disableUpdates
,
requency
,
closeTimer
,
ChevronRightMed
}
from
'
./buttons/Icon
'
;
import
ExperimentSummaryPanel
from
'
./modals/ExperimentSummaryPanel
'
;
import
{
infoIconAbout
,
timeIcon
,
disableUpdates
,
requency
,
closeTimer
}
from
'
./buttons/Icon
'
;
import
{
OVERVIEWTABS
,
DETAILTABS
,
NNILOGO
}
from
'
./stateless-component/NNItabs
'
;
import
{
EXPERIMENT
}
from
'
../static/datamodel
'
;
import
{
stackTokens
,
stackStyle
}
from
'
./NavConst
'
;
import
'
../static/style/nav/nav.scss
'
;
import
'
../static/style/icon.scss
'
;
initializeIcons
();
const
stackTokens
:
IStackTokens
=
{
childrenGap
:
15
};
const
stackStyle
:
IStackStyles
=
{
root
:
{
minWidth
:
400
,
height
:
56
,
display
:
'
flex
'
,
verticalAlign
:
'
center
'
}
};
interface
NavState
{
version
:
string
;
menuVisible
:
boolean
;
...
...
@@ -133,6 +114,7 @@ class NavCon extends React.Component<NavProps, NavState> {
};
return
(
<
Stack
horizontal
className
=
'nav'
>
<
React
.
Fragment
>
<
StackItem
grow
=
{
30
}
styles
=
{
{
root
:
{
minWidth
:
300
,
display
:
'
flex
'
,
verticalAlign
:
'
center
'
}
}
}
>
<
span
className
=
'desktop-logo'
>
{
NNILOGO
}
</
span
>
<
span
className
=
'left-right-margin'
>
{
OVERVIEWTABS
}
</
span
>
...
...
@@ -157,10 +139,16 @@ class NavCon extends React.Component<NavProps, NavState> {
</
div
>
<
CommandBarButton
iconProps
=
{
{
iconName
:
'
ShowResults
'
}
}
text
=
'
S
ummary'
text
=
'
Experiment s
ummary'
onClick
=
{
this
.
showExpcontent
}
/>
<
CommandBarButton
iconProps
=
{
infoIconAbout
}
text
=
'About'
menuProps
=
{
aboutProps
}
/>
<
Link
to
=
'/experiment'
className
=
'experiment'
>
<
div
className
=
'expNavTitle'
>
<
span
>
All experiment
</
span
>
{
ChevronRightMed
}
</
div
>
</
Link
>
</
Stack
>
</
StackItem
>
{
isvisibleExperimentDrawer
&&
(
...
...
@@ -169,6 +157,7 @@ class NavCon extends React.Component<NavProps, NavState> {
experimentProfile
=
{
EXPERIMENT
.
profile
}
/>
)
}
</
React
.
Fragment
>
</
Stack
>
);
}
...
...
ts/webui/src/components/NavConst.ts
0 → 100644
View file @
593a275c
import
{
IStackTokens
,
IStackStyles
}
from
'
@fluentui/react
'
;
const
stackTokens
:
IStackTokens
=
{
childrenGap
:
15
};
const
stackStyle
:
IStackStyles
=
{
root
:
{
minWidth
:
400
,
height
:
56
,
display
:
'
flex
'
,
verticalAlign
:
'
center
'
}
};
export
{
stackTokens
,
stackStyle
};
ts/webui/src/components/Overview.tsx
View file @
593a275c
...
...
@@ -6,7 +6,7 @@ import { AppContext } from '../App';
import
{
Title
}
from
'
./overview/Title
'
;
import
SuccessTable
from
'
./overview/table/SuccessTable
'
;
import
Accuracy
from
'
./overview/Accuracy
'
;
import
{
Re
BasicInfo
}
from
'
./overview/
experiment
/BasicInfo
'
;
import
{
BasicInfo
}
from
'
./overview/
params
/BasicInfo
'
;
import
{
ExpDuration
}
from
'
./overview/count/ExpDuration
'
;
import
{
ExpDurationContext
}
from
'
./overview/count/ExpDurationContext
'
;
import
{
TrialCount
}
from
'
./overview/count/TrialCount
'
;
...
...
@@ -86,7 +86,7 @@ class Overview extends React.Component<{}, OverviewState> {
<
Title
/>
</
TitleContext
.
Provider
>
<
BestMetricContext
.
Provider
value
=
{
{
bestAccuracy
:
bestAccuracy
}
}
>
<
Re
BasicInfo
/>
<
BasicInfo
/>
</
BestMetricContext
.
Provider
>
</
div
>
{
/* duration & trial numbers */
}
...
...
ts/webui/src/components/buttons/Icon.tsx
View file @
593a275c
...
...
@@ -19,6 +19,9 @@ const LineChart = <Icon iconName='LineChart' />;
const
Edit
=
<
Icon
iconName
=
'Edit'
/>;
const
CheckMark
=
<
Icon
iconName
=
'CheckMark'
/>;
const
Cancel
=
<
Icon
iconName
=
'Cancel'
/>;
const
ReplyAll
=
{
iconName
:
'
ReplyAll
'
};
const
RevToggleKey
=
{
iconName
:
'
RevToggleKey
'
};
const
ChevronRightMed
=
<
Icon
iconName
=
'ChevronRightMed'
/>;
export
{
infoIcon
,
...
...
@@ -37,5 +40,8 @@ export {
LineChart
,
Edit
,
CheckMark
,
Cancel
Cancel
,
ReplyAll
,
RevToggleKey
,
ChevronRightMed
};
ts/webui/src/components/managementExp/ExperimentManager.tsx
0 → 100644
View file @
593a275c
import
*
as
React
from
'
react
'
;
import
{
Stack
,
DetailsList
,
DefaultButton
,
Icon
,
SearchBox
,
IColumn
}
from
'
@fluentui/react
'
;
import
{
ExperimentsManager
}
from
'
../../static/model/experimentsManager
'
;
import
{
expformatTimestamp
,
copyAndSort
}
from
'
../../static/function
'
;
import
{
AllExperimentList
,
SortInfo
}
from
'
../../static/interface
'
;
import
MessageInfo
from
'
../modals/MessageInfo
'
;
import
{
compareDate
,
filterByStatusOrPlatform
,
getSortedSource
}
from
'
./expFunction
'
;
import
{
MAXSCREENCOLUMNWIDHT
,
MINSCREENCOLUMNWIDHT
}
from
'
./experimentConst
'
;
import
{
Hearder
}
from
'
./Header
'
;
import
NameColumn
from
'
./TrialIdColumn
'
;
import
FilterBtns
from
'
./FilterBtns
'
;
import
'
../../App.scss
'
;
import
'
../../static/style/nav/nav.scss
'
;
import
'
../../static/style/experiment/experiment.scss
'
;
import
'
../../static/style/overview/probar.scss
'
;
import
'
../../static/style/tableStatus.css
'
;
interface
ExpListState
{
columns
:
IColumn
[];
platform
:
string
[];
errorMessage
:
string
;
hideFilter
:
boolean
;
searchInputVal
:
string
;
selectedStatus
:
string
;
selectedPlatform
:
string
;
selectedStartDate
?:
Date
;
selectedEndDate
?:
Date
;
sortInfo
:
SortInfo
;
source
:
AllExperimentList
[];
originExperimentList
:
AllExperimentList
[];
searchSource
:
AllExperimentList
[];
}
class
Experiment
extends
React
.
Component
<
{},
ExpListState
>
{
constructor
(
props
)
{
super
(
props
);
this
.
state
=
{
platform
:
[],
columns
:
this
.
columns
,
errorMessage
:
''
,
hideFilter
:
true
,
searchInputVal
:
''
,
selectedStatus
:
''
,
selectedPlatform
:
''
,
source
:
[],
// data in table
originExperimentList
:
[],
// api /experiments-info
searchSource
:
[],
// search box search result
sortInfo
:
{
field
:
''
,
isDescend
:
false
}
};
}
async
componentDidMount
():
Promise
<
void
>
{
const
EXPERIMENTMANAGER
=
new
ExperimentsManager
();
await
EXPERIMENTMANAGER
.
init
();
const
result
=
EXPERIMENTMANAGER
.
getExperimentList
();
this
.
setState
(()
=>
({
source
:
result
,
originExperimentList
:
result
,
searchSource
:
result
,
platform
:
EXPERIMENTMANAGER
.
getPlatformList
(),
errorMessage
:
EXPERIMENTMANAGER
.
getExpErrorMessage
()
}));
}
render
():
React
.
ReactNode
{
const
{
platform
,
hideFilter
,
selectedStatus
,
source
,
selectedPlatform
,
selectedStartDate
,
selectedEndDate
,
errorMessage
}
=
this
.
state
;
return
(
<
Stack
className
=
'nni'
style
=
{
{
minHeight
:
window
.
innerHeight
}
}
>
<
Hearder
/>
{
errorMessage
!==
undefined
?
(
<
div
className
=
'warning'
>
<
MessageInfo
info
=
{
errorMessage
}
typeInfo
=
'error'
/>
</
div
>
)
:
null
}
<
Stack
className
=
'contentBox expBackground'
>
<
Stack
className
=
'content'
>
<
Stack
className
=
'experimentList'
>
<
Stack
className
=
'box'
horizontal
>
<
div
className
=
'search'
>
<
SearchBox
className
=
'search-input'
placeholder
=
'Search the experiment by name and ID'
onEscape
=
{
this
.
setOriginSource
.
bind
(
this
)
}
onClear
=
{
this
.
setOriginSource
.
bind
(
this
)
}
onChange
=
{
this
.
searchNameAndId
.
bind
(
this
)
}
/>
</
div
>
<
div
className
=
'filter'
>
<
DefaultButton
onClick
=
{
this
.
clickFilter
.
bind
(
this
)
}
className
=
{
`
${
!
hideFilter
?
'
filter-button-open
'
:
null
}
`
}
>
<
Icon
iconName
=
'Equalizer'
/>
<
span
className
=
'margin'
>
Filter
</
span
>
</
DefaultButton
>
</
div
>
</
Stack
>
<
Stack
className
=
{
`
${
hideFilter
?
'
hidden
'
:
''
}
filter-condition`
}
horizontal
gap
=
{
25
}
>
<
FilterBtns
platform
=
{
platform
}
selectedStatus
=
{
selectedStatus
}
selectedPlatform
=
{
selectedPlatform
}
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
selectedStartDate
=
{
selectedStartDate
!
}
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
selectedEndDate
=
{
selectedEndDate
!
}
selectStatus
=
{
this
.
selectStatus
.
bind
(
this
)
}
selectPlatform
=
{
this
.
selectPlatform
.
bind
(
this
)
}
getSelectedData
=
{
this
.
getSelectedData
.
bind
(
this
)
}
setSearchSource
=
{
this
.
setSearchSource
.
bind
(
this
)
}
/>
</
Stack
>
<
DetailsList
columns
=
{
this
.
columns
}
items
=
{
source
}
setKey
=
'set'
compact
=
{
true
}
selectionMode
=
{
0
}
// close selector function
className
=
'table'
/>
</
Stack
>
</
Stack
>
</
Stack
>
</
Stack
>
);
}
private
onColumnClick
=
(
_ev
:
React
.
MouseEvent
<
HTMLElement
>
,
getColumn
:
IColumn
):
void
=>
{
const
{
columns
,
source
}
=
this
.
state
;
const
newColumns
:
IColumn
[]
=
columns
.
slice
();
const
currColumn
:
IColumn
=
newColumns
.
filter
(
item
=>
getColumn
.
key
===
item
.
key
)[
0
];
newColumns
.
forEach
((
newCol
:
IColumn
)
=>
{
if
(
newCol
===
currColumn
)
{
currColumn
.
isSortedDescending
=
!
currColumn
.
isSortedDescending
;
currColumn
.
isSorted
=
true
;
}
else
{
newCol
.
isSorted
=
false
;
newCol
.
isSortedDescending
=
true
;
}
});
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const
newItems
=
copyAndSort
(
source
,
currColumn
.
fieldName
!
,
currColumn
.
isSortedDescending
);
this
.
setState
(()
=>
({
columns
:
newColumns
,
source
:
newItems
,
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
sortInfo
:
{
field
:
currColumn
.
fieldName
!
,
isDescend
:
currColumn
.
isSortedDescending
}
}));
};
private
columns
:
IColumn
[]
=
[
{
name
:
'
Name
'
,
key
:
'
experimentName
'
,
fieldName
:
'
experimentName
'
,
// required!
minWidth
:
MINSCREENCOLUMNWIDHT
,
maxWidth
:
MAXSCREENCOLUMNWIDHT
,
isResizable
:
true
,
data
:
'
number
'
,
onColumnClick
:
this
.
onColumnClick
,
onRender
:
(
item
:
any
):
React
.
ReactNode
=>
<
div
className
=
'succeed-padding'
>
{
item
.
experimentName
}
</
div
>
},
{
name
:
'
ID
'
,
key
:
'
id
'
,
fieldName
:
'
id
'
,
minWidth
:
MINSCREENCOLUMNWIDHT
,
maxWidth
:
MAXSCREENCOLUMNWIDHT
,
isResizable
:
true
,
className
:
'
tableHead leftTitle
'
,
data
:
'
string
'
,
onColumnClick
:
this
.
onColumnClick
,
onRender
:
(
item
:
any
):
React
.
ReactNode
=>
<
NameColumn
port
=
{
item
.
port
}
status
=
{
item
.
status
}
id
=
{
item
.
id
}
/>
},
{
name
:
'
Status
'
,
key
:
'
status
'
,
fieldName
:
'
status
'
,
minWidth
:
MINSCREENCOLUMNWIDHT
,
maxWidth
:
MAXSCREENCOLUMNWIDHT
,
isResizable
:
true
,
onColumnClick
:
this
.
onColumnClick
,
onRender
:
(
item
:
any
):
React
.
ReactNode
=>
(
<
div
className
=
{
`
${
item
.
status
}
commonStyle succeed-padding`
}
>
{
item
.
status
}
</
div
>
)
},
{
name
:
'
Port
'
,
key
:
'
port
'
,
fieldName
:
'
port
'
,
minWidth
:
MINSCREENCOLUMNWIDHT
-
15
,
maxWidth
:
MAXSCREENCOLUMNWIDHT
-
30
,
isResizable
:
true
,
data
:
'
number
'
,
onColumnClick
:
this
.
onColumnClick
,
onRender
:
(
item
:
any
):
React
.
ReactNode
=>
(
<
div
className
=
'succeed-padding'
>
<
div
>
{
item
.
port
!==
undefined
?
item
.
port
:
'
--
'
}
</
div
>
</
div
>
)
},
{
name
:
'
Platform
'
,
key
:
'
platform
'
,
fieldName
:
'
platform
'
,
minWidth
:
MINSCREENCOLUMNWIDHT
-
15
,
maxWidth
:
MAXSCREENCOLUMNWIDHT
-
30
,
isResizable
:
true
,
data
:
'
string
'
,
onColumnClick
:
this
.
onColumnClick
,
onRender
:
(
item
:
any
):
React
.
ReactNode
=>
<
div
className
=
'commonStyle succeed-padding'
>
{
item
.
platform
}
</
div
>
},
{
name
:
'
Start time
'
,
key
:
'
startTime
'
,
fieldName
:
'
startTime
'
,
minWidth
:
MINSCREENCOLUMNWIDHT
+
15
,
maxWidth
:
MAXSCREENCOLUMNWIDHT
+
30
,
isResizable
:
true
,
data
:
'
number
'
,
onColumnClick
:
this
.
onColumnClick
,
onRender
:
(
item
:
any
):
React
.
ReactNode
=>
(
<
div
className
=
'succeed-padding'
>
<
div
>
{
expformatTimestamp
(
item
.
startTime
)
}
</
div
>
</
div
>
)
},
{
name
:
'
End time
'
,
key
:
'
endTime
'
,
fieldName
:
'
endTime
'
,
minWidth
:
MINSCREENCOLUMNWIDHT
+
15
,
maxWidth
:
MAXSCREENCOLUMNWIDHT
+
30
,
isResizable
:
true
,
data
:
'
number
'
,
onColumnClick
:
this
.
onColumnClick
,
onRender
:
(
item
:
any
):
React
.
ReactNode
=>
(
<
div
className
=
'succeed-padding'
>
<
div
>
{
expformatTimestamp
(
item
.
endTime
)
}
</
div
>
</
div
>
)
}
];
private
clickFilter
(
_e
:
any
):
void
{
const
{
hideFilter
}
=
this
.
state
;
if
(
!
hideFilter
===
true
)
{
this
.
setSearchSource
();
}
this
.
setState
(()
=>
({
hideFilter
:
!
hideFilter
}));
}
private
setOriginSource
():
void
{
let
{
originExperimentList
}
=
this
.
state
;
const
{
sortInfo
}
=
this
.
state
;
if
(
originExperimentList
!==
undefined
)
{
originExperimentList
=
this
.
commonSelectString
(
originExperimentList
,
''
);
const
sortedData
=
getSortedSource
(
originExperimentList
,
sortInfo
);
this
.
setState
(()
=>
({
source
:
sortedData
}));
}
}
private
searchNameAndId
(
_event
,
newValue
):
void
{
const
{
originExperimentList
,
sortInfo
}
=
this
.
state
;
if
(
newValue
!==
undefined
)
{
if
(
newValue
===
''
)
{
this
.
setOriginSource
();
}
else
{
let
result
=
originExperimentList
.
filter
(
item
=>
item
.
experimentName
.
toLowerCase
().
includes
(
newValue
.
toLowerCase
())
||
item
.
id
.
toLowerCase
().
includes
(
newValue
.
toLowerCase
())
);
result
=
this
.
commonSelectString
(
result
,
''
);
const
sortedResult
=
getSortedSource
(
result
,
sortInfo
);
this
.
setState
(()
=>
({
source
:
sortedResult
,
searchSource
:
sortedResult
}));
}
this
.
setState
(()
=>
({
searchInputVal
:
newValue
}));
}
}
/***
* status, platform
* param
* data: searchSource
* field: no care selected filed
*/
private
commonSelectString
=
(
data
:
AllExperimentList
[],
field
:
string
):
AllExperimentList
[]
=>
{
const
{
selectedStatus
,
selectedPlatform
,
selectedStartDate
,
selectedEndDate
}
=
this
.
state
;
const
hasStatus
=
selectedStatus
===
''
?
false
:
true
;
const
hasPlatform
=
selectedPlatform
===
''
?
false
:
true
;
const
hasStartDate
=
selectedStartDate
===
undefined
?
false
:
true
;
const
hasEndDate
=
selectedEndDate
===
undefined
?
false
:
true
;
if
(
field
===
'
status
'
)
{
if
(
hasPlatform
)
{
data
=
filterByStatusOrPlatform
(
selectedPlatform
,
'
platform
'
,
data
);
}
}
if
(
field
===
'
platform
'
)
{
if
(
hasStatus
)
{
data
=
filterByStatusOrPlatform
(
selectedStatus
,
'
status
'
,
data
);
}
}
if
(
field
===
''
)
{
if
(
hasPlatform
)
{
data
=
filterByStatusOrPlatform
(
selectedPlatform
,
'
platform
'
,
data
);
}
if
(
hasStatus
)
{
data
=
filterByStatusOrPlatform
(
selectedStatus
,
'
status
'
,
data
);
}
}
if
(
hasStartDate
)
{
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
data
=
data
.
filter
(
temp
=>
compareDate
(
new
Date
(
temp
.
startTime
),
selectedStartDate
!
));
}
if
(
hasEndDate
)
{
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
data
=
data
.
filter
(
temp
=>
compareDate
(
new
Date
(
temp
.
endTime
),
selectedEndDate
!
));
}
return
data
;
};
// status platform startTime endTime
private
selectStatus
=
(
_event
:
React
.
FormEvent
<
HTMLDivElement
>
,
item
:
any
):
void
=>
{
if
(
item
!==
undefined
)
{
const
{
searchSource
,
sortInfo
}
=
this
.
state
;
let
result
=
filterByStatusOrPlatform
(
item
.
key
,
'
status
'
,
searchSource
);
result
=
this
.
commonSelectString
(
result
,
'
status
'
);
this
.
setState
({
selectedStatus
:
item
.
key
,
source
:
getSortedSource
(
result
,
sortInfo
)
});
}
};
private
selectPlatform
=
(
_event
:
React
.
FormEvent
<
HTMLDivElement
>
,
item
:
any
):
void
=>
{
if
(
item
!==
undefined
)
{
const
{
searchSource
,
sortInfo
}
=
this
.
state
;
let
result
=
filterByStatusOrPlatform
(
item
.
key
,
'
platform
'
,
searchSource
);
result
=
this
.
commonSelectString
(
result
,
'
platform
'
);
this
.
setState
({
selectedPlatform
:
item
.
key
,
source
:
getSortedSource
(
result
,
sortInfo
)
});
}
};
private
getSelectedData
(
type
:
string
,
date
:
Date
|
null
|
undefined
):
void
{
if
(
date
!==
null
&&
date
!==
undefined
)
{
const
{
selectedStatus
,
selectedPlatform
,
selectedStartDate
,
selectedEndDate
,
searchSource
,
sortInfo
}
=
this
.
state
;
const
hasStatus
=
selectedStatus
===
''
?
false
:
true
;
const
hasPlatform
=
selectedPlatform
===
''
?
false
:
true
;
const
hasStartDate
=
selectedStartDate
===
undefined
?
false
:
true
;
const
hasEndDate
=
selectedEndDate
===
undefined
?
false
:
true
;
let
result
;
if
(
type
===
'
start
'
)
{
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
result
=
searchSource
.
filter
(
item
=>
compareDate
(
new
Date
(
item
.
startTime
),
date
));
if
(
hasStatus
)
{
result
=
result
.
filter
(
temp
=>
temp
.
status
===
selectedStatus
);
}
if
(
hasPlatform
)
{
result
=
result
.
filter
(
temp
=>
temp
.
platform
===
selectedPlatform
);
}
if
(
hasEndDate
)
{
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
result
=
result
.
filter
(
temp
=>
compareDate
(
new
Date
(
temp
.
endTime
),
selectedEndDate
!
));
}
this
.
setState
(()
=>
({
source
:
getSortedSource
(
result
,
sortInfo
),
selectedStartDate
:
date
}));
}
else
{
result
=
searchSource
.
filter
(
item
=>
compareDate
(
new
Date
(
item
.
endTime
),
date
));
if
(
hasStatus
)
{
result
=
result
.
filter
(
temp
=>
temp
.
status
===
selectedStatus
);
}
if
(
hasPlatform
)
{
result
=
result
.
filter
(
temp
=>
temp
.
platform
===
selectedPlatform
);
}
if
(
hasStartDate
)
{
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
result
=
result
.
filter
(
temp
=>
compareDate
(
new
Date
(
temp
.
startTime
),
selectedStartDate
!
));
}
this
.
setState
(()
=>
({
source
:
getSortedSource
(
result
,
sortInfo
),
selectedEndDate
:
date
}));
}
}
}
// reset
private
setSearchSource
():
void
{
const
{
sortInfo
,
searchInputVal
,
originExperimentList
}
=
this
.
state
;
// hert re-search data for fix this status: filter first -> searchBox search result null -> close filter
const
result
=
originExperimentList
.
filter
(
item
=>
item
.
experimentName
.
toLowerCase
().
includes
(
searchInputVal
.
toLowerCase
())
||
item
.
id
.
toLowerCase
().
includes
(
searchInputVal
.
toLowerCase
())
);
this
.
setState
(()
=>
({
source
:
getSortedSource
(
result
,
sortInfo
),
selectedStatus
:
''
,
selectedPlatform
:
''
,
selectedStartDate
:
undefined
,
selectedEndDate
:
undefined
}));
}
}
export
default
Experiment
;
ts/webui/src/components/managementExp/FilterBtns.tsx
0 → 100644
View file @
593a275c
import
*
as
React
from
'
react
'
;
import
{
DefaultButton
,
Icon
,
Dropdown
,
DatePicker
,
DayOfWeek
}
from
'
@fluentui/react
'
;
import
{
EXPERIMENTSTATUS
}
from
'
../../static/const
'
;
import
{
fillOptions
}
from
'
./expFunction
'
;
interface
FilterBtnsProps
{
platform
:
string
[];
selectedStatus
:
string
;
selectedPlatform
:
string
;
selectedStartDate
:
Date
;
selectedEndDate
:
Date
;
selectStatus
:
(
_event
:
React
.
FormEvent
<
HTMLDivElement
>
,
item
:
any
)
=>
void
;
selectPlatform
:
(
_event
:
React
.
FormEvent
<
HTMLDivElement
>
,
item
:
any
)
=>
void
;
getSelectedData
:
(
type
:
string
,
date
:
Date
|
null
|
undefined
)
=>
void
;
setSearchSource
:
()
=>
void
;
}
class
FilterBtns
extends
React
.
Component
<
FilterBtnsProps
,
{}
>
{
constructor
(
props
:
FilterBtnsProps
)
{
super
(
props
);
}
render
():
React
.
ReactNode
{
const
{
platform
,
selectedStatus
,
selectedPlatform
,
selectedStartDate
,
selectedEndDate
,
selectStatus
,
selectPlatform
,
getSelectedData
,
setSearchSource
}
=
this
.
props
;
return
(
<
React
.
Fragment
>
<
Dropdown
label
=
'Status'
selectedKey
=
{
selectedStatus
}
onChange
=
{
selectStatus
.
bind
(
this
)
}
placeholder
=
'Select an option'
options
=
{
fillOptions
(
EXPERIMENTSTATUS
)
}
className
=
'filter-condition-status'
/>
<
Dropdown
label
=
'Platform'
selectedKey
=
{
selectedPlatform
}
onChange
=
{
selectPlatform
.
bind
(
this
)
}
placeholder
=
'Select an option'
options
=
{
fillOptions
(
platform
)
}
className
=
'filter-condition-platform'
/>
<
DatePicker
label
=
'Start time'
firstDayOfWeek
=
{
DayOfWeek
.
Sunday
}
showMonthPickerAsOverlay
=
{
true
}
placeholder
=
'Select a date...'
ariaLabel
=
'Select a date'
value
=
{
selectedStartDate
}
onSelectDate
=
{
getSelectedData
.
bind
(
this
,
'
start
'
)
}
/>
<
DatePicker
label
=
'End time'
firstDayOfWeek
=
{
DayOfWeek
.
Sunday
}
showMonthPickerAsOverlay
=
{
true
}
placeholder
=
'Select a date...'
ariaLabel
=
'Select a date'
value
=
{
selectedEndDate
}
onSelectDate
=
{
getSelectedData
.
bind
(
this
,
'
end
'
)
}
/>
<
DefaultButton
onClick
=
{
setSearchSource
.
bind
(
this
)
}
className
=
'reset'
>
<
Icon
iconName
=
'Refresh'
/>
<
span
className
=
'margin'
>
Reset
</
span
>
</
DefaultButton
>
</
React
.
Fragment
>
);
}
}
export
default
FilterBtns
;
ts/webui/src/components/managementExp/Header.tsx
0 → 100644
View file @
593a275c
import
React
from
'
react
'
;
import
{
Link
}
from
'
react-router-dom
'
;
import
{
Stack
,
StackItem
,
CommandBarButton
}
from
'
@fluentui/react
'
;
import
{
RevToggleKey
}
from
'
../buttons/Icon
'
;
import
{
NNILOGO
}
from
'
../stateless-component/NNItabs
'
;
import
{
stackTokens
,
stackStyle
}
from
'
../NavConst
'
;
export
const
Hearder
=
():
any
=>
(
<
div
className
=
'header'
>
<
div
className
=
'headerCon'
>
<
Stack
className
=
'nav'
horizontal
>
<
StackItem
grow
=
{
30
}
styles
=
{
{
root
:
{
minWidth
:
300
,
display
:
'
flex
'
,
verticalAlign
:
'
center
'
}
}
}
>
<
span
className
=
'desktop-logo'
>
{
NNILOGO
}
</
span
>
<
span
className
=
'logoTitle'
>
Neural Network Intelligence
</
span
>
</
StackItem
>
<
StackItem
grow
=
{
70
}
className
=
'navOptions'
>
<
Stack
horizontal
horizontalAlign
=
'end'
tokens
=
{
stackTokens
}
styles
=
{
stackStyle
}
>
<
Link
to
=
'/oview'
className
=
'experiment'
>
<
CommandBarButton
iconProps
=
{
RevToggleKey
}
text
=
'Back to the experiment'
/>
</
Link
>
</
Stack
>
</
StackItem
>
</
Stack
>
</
div
>
</
div
>
);
ts/webui/src/components/managementExp/TrialIdColumn.tsx
0 → 100644
View file @
593a275c
import
*
as
React
from
'
react
'
;
interface
TrialIdColumnProps
{
port
:
number
;
id
:
string
;
status
:
string
;
}
class
TrialIdColumn
extends
React
.
Component
<
TrialIdColumnProps
,
{}
>
{
constructor
(
props
:
TrialIdColumnProps
)
{
super
(
props
);
}
render
():
React
.
ReactNode
{
const
{
port
,
id
,
status
}
=
this
.
props
;
const
hostname
=
window
.
location
.
hostname
;
const
protocol
=
window
.
location
.
protocol
;
const
webuiPortal
=
`
${
protocol
}
//
${
hostname
}
:
${
port
}
/oview`
;
return
(
<
div
className
=
'succeed-padding ellipsis'
>
{
status
===
'
STOPPED
'
?
(
<
div
>
{
id
}
</
div
>
)
:
(
<
a
href
=
{
webuiPortal
}
className
=
'link'
target
=
'_blank'
rel
=
'noopener noreferrer'
>
{
id
}
</
a
>
)
}
</
div
>
);
}
}
export
default
TrialIdColumn
;
Prev
1
2
3
4
5
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