Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
OpenDAS
nni
Commits
3b90b9d9
Commit
3b90b9d9
authored
Oct 26, 2020
by
liuzhe
Browse files
Merge branch 'master' into v2.0-merge
parents
e21a6984
77dac12b
Changes
143
Hide whitespace changes
Inline
Side-by-side
Showing
20 changed files
with
418 additions
and
181 deletions
+418
-181
test/nni_test/nnitest/generate_ts_config.py
test/nni_test/nnitest/generate_ts_config.py
+3
-0
test/pipelines/pipelines-it-remote-linux-to-linux.yml
test/pipelines/pipelines-it-remote-linux-to-linux.yml
+1
-1
test/pipelines/pipelines-it-remote-windows-to-linux.yml
test/pipelines/pipelines-it-remote-windows-to-linux.yml
+1
-1
ts/nni_manager/training_service/remote_machine/extends/linuxCommands.ts
.../training_service/remote_machine/extends/linuxCommands.ts
+4
-0
ts/nni_manager/training_service/remote_machine/extends/windowsCommands.ts
...raining_service/remote_machine/extends/windowsCommands.ts
+4
-0
ts/nni_manager/training_service/remote_machine/osCommands.ts
ts/nni_manager/training_service/remote_machine/osCommands.ts
+1
-0
ts/nni_manager/training_service/remote_machine/shellExecutor.ts
..._manager/training_service/remote_machine/shellExecutor.ts
+6
-0
ts/nni_manager/training_service/reusable/environments/remoteEnvironmentService.ts
...service/reusable/environments/remoteEnvironmentService.ts
+34
-31
ts/nni_manager/training_service/reusable/trialDispatcher.ts
ts/nni_manager/training_service/reusable/trialDispatcher.ts
+11
-8
ts/nni_manager/yarn.lock
ts/nni_manager/yarn.lock
+60
-27
ts/webui/src/App.scss
ts/webui/src/App.scss
+1
-1
ts/webui/src/App.tsx
ts/webui/src/App.tsx
+32
-2
ts/webui/src/components/Overview.tsx
ts/webui/src/components/Overview.tsx
+28
-8
ts/webui/src/components/TrialsDetail.tsx
ts/webui/src/components/TrialsDetail.tsx
+1
-1
ts/webui/src/components/modals/CustomizedTrial.tsx
ts/webui/src/components/modals/CustomizedTrial.tsx
+4
-1
ts/webui/src/components/modals/Killjob.tsx
ts/webui/src/components/modals/Killjob.tsx
+6
-2
ts/webui/src/components/overview/command/Command1.tsx
ts/webui/src/components/overview/command/Command1.tsx
+5
-28
ts/webui/src/components/overview/command/Command2.tsx
ts/webui/src/components/overview/command/Command2.tsx
+65
-0
ts/webui/src/components/overview/count/EditExperimentParam.tsx
...bui/src/components/overview/count/EditExperimentParam.tsx
+122
-51
ts/webui/src/components/overview/count/ExpDuration.tsx
ts/webui/src/components/overview/count/ExpDuration.tsx
+29
-19
No files found.
test/nni_test/nnitest/generate_ts_config.py
View file @
3b90b9d9
...
...
@@ -86,6 +86,8 @@ def update_training_service_config(args):
config
[
args
.
ts
][
'machineList'
][
0
][
'port'
]
=
args
.
remote_port
if
args
.
remote_pwd
is
not
None
:
config
[
args
.
ts
][
'machineList'
][
0
][
'passwd'
]
=
args
.
remote_pwd
if
args
.
remote_reuse
is
not
None
:
config
[
args
.
ts
][
'remoteConfig'
][
'reuse'
]
=
args
.
remote_reuse
.
lower
()
==
'true'
dump_yml_content
(
TRAINING_SERVICE_FILE
,
config
)
...
...
@@ -119,6 +121,7 @@ if __name__ == '__main__':
parser
.
add_argument
(
"--remote_pwd"
,
type
=
str
)
parser
.
add_argument
(
"--remote_host"
,
type
=
str
)
parser
.
add_argument
(
"--remote_port"
,
type
=
int
)
parser
.
add_argument
(
"--remote_reuse"
,
type
=
str
)
args
=
parser
.
parse_args
()
update_training_service_config
(
args
)
test/pipelines/pipelines-it-remote-linux-to-linux.yml
View file @
3b90b9d9
...
...
@@ -62,7 +62,7 @@ jobs:
-
script
:
|
set -e
cd test
python3 nni_test/nnitest/generate_ts_config.py --ts remote --remote_user $(docker_user) --remote_host $(remote_host) \
python3 nni_test/nnitest/generate_ts_config.py --ts remote
--remote_reuse $(remote_reuse)
--remote_user $(docker_user) --remote_host $(remote_host) \
--remote_port $(cat port) --remote_pwd $(docker_pwd) --nni_manager_ip $(nni_manager_ip)
cat config/training_service.yml
PATH=$HOME/.local/bin:$PATH python3 nni_test/nnitest/run_tests.py --config config/integration_tests.yml --ts remote
...
...
test/pipelines/pipelines-it-remote-windows-to-linux.yml
View file @
3b90b9d9
...
...
@@ -48,7 +48,7 @@ jobs:
displayName
:
'
Get
docker
port'
-
powershell
:
|
cd test
python nni_test/nnitest/generate_ts_config.py --ts remote --remote_user $(docker_user) --remote_host $(remote_host) --remote_port $(Get-Content port) --remote_pwd $(docker_pwd) --nni_manager_ip $(nni_manager_ip)
python nni_test/nnitest/generate_ts_config.py --ts remote
--remote_reuse $(remote_reuse)
--remote_user $(docker_user) --remote_host $(remote_host) --remote_port $(Get-Content port) --remote_pwd $(docker_pwd) --nni_manager_ip $(nni_manager_ip)
Get-Content config/training_service.yml
python nni_test/nnitest/run_tests.py --config config/integration_tests.yml --ts remote --exclude cifar10
displayName
:
'
integration
test'
...
...
ts/nni_manager/training_service/remote_machine/extends/linuxCommands.ts
View file @
3b90b9d9
...
...
@@ -136,6 +136,10 @@ class LinuxCommands extends OsCommands {
return
`
${
preCommand
}
&&
${
command
}
`
;
}
}
public
fileExistCommand
(
filePath
:
string
):
string
{
return
`test -e
${
filePath
}
&& echo True || echo False`
;
}
}
export
{
LinuxCommands
};
ts/nni_manager/training_service/remote_machine/extends/windowsCommands.ts
View file @
3b90b9d9
...
...
@@ -130,6 +130,10 @@ class WindowsCommands extends OsCommands {
return
`
${
preCommand
}
&& set prePath=%path% &&
${
command
}
`
;
}
}
public
fileExistCommand
(
filePath
:
string
):
string
{
return
`powershell Test-Path
${
filePath
}
-PathType Leaf`
;
}
}
export
{
WindowsCommands
};
ts/nni_manager/training_service/remote_machine/osCommands.ts
View file @
3b90b9d9
...
...
@@ -29,6 +29,7 @@ abstract class OsCommands {
public
abstract
extractFile
(
tarFileName
:
string
,
targetFolder
:
string
):
string
;
public
abstract
executeScript
(
script
:
string
,
isFile
:
boolean
):
string
;
public
abstract
addPreCommand
(
preCommand
:
string
|
undefined
,
command
:
string
|
undefined
):
string
|
undefined
;
public
abstract
fileExistCommand
(
filePath
:
string
):
string
|
undefined
;
public
joinPath
(...
paths
:
string
[]):
string
{
let
dir
:
string
=
paths
.
filter
((
path
:
any
)
=>
path
!==
''
).
join
(
this
.
pathSpliter
);
...
...
ts/nni_manager/training_service/remote_machine/shellExecutor.ts
View file @
3b90b9d9
...
...
@@ -238,6 +238,12 @@ class ShellExecutor {
return
commandResult
.
exitCode
==
0
;
}
public
async
fileExist
(
filePath
:
string
):
Promise
<
boolean
>
{
const
commandText
=
this
.
osCommands
&&
this
.
osCommands
.
fileExistCommand
(
filePath
);
const
commandResult
=
await
this
.
execute
(
commandText
);
return
commandResult
.
stdout
!==
undefined
&&
commandResult
.
stdout
.
trim
()
===
'
True
'
;
}
public
async
extractFile
(
tarFileName
:
string
,
targetFolder
:
string
):
Promise
<
boolean
>
{
const
commandText
=
this
.
osCommands
&&
this
.
osCommands
.
extractFile
(
tarFileName
,
targetFolder
);
const
commandResult
=
await
this
.
execute
(
commandText
);
...
...
ts/nni_manager/training_service/reusable/environments/remoteEnvironmentService.ts
View file @
3b90b9d9
...
...
@@ -137,40 +137,43 @@ export class RemoteEnvironmentService extends EnvironmentService {
private
async
refreshEnvironment
(
environment
:
EnvironmentInformation
):
Promise
<
void
>
{
const
executor
=
await
this
.
getExecutor
(
environment
.
id
);
const
jobpidPath
:
string
=
`
${
environment
.
runnerWorkingFolder
}
/pid`
;
const
runnerReturnCodeFilePath
:
string
=
`
${
environment
.
runnerWorkingFolder
}
/code`
;
if
(
fs
.
existsSync
(
jobpidPath
))
{
/* eslint-disable require-atomic-updates */
try
{
const
isAlive
=
await
executor
.
isProcessAlive
(
jobpidPath
);
// if the process of jobpid is not alive any more
if
(
!
isAlive
)
{
const
remoteEnvironment
:
RemoteMachineEnvironmentInformation
=
environment
as
RemoteMachineEnvironmentInformation
;
if
(
remoteEnvironment
.
rmMachineMeta
===
undefined
)
{
throw
new
Error
(
`
${
remoteEnvironment
.
id
}
machine meta not initialized!`
);
}
this
.
log
.
info
(
`pid in
${
remoteEnvironment
.
rmMachineMeta
.
ip
}
:
${
jobpidPath
}
is not alive!`
);
if
(
fs
.
existsSync
(
runnerReturnCodeFilePath
))
{
const
runnerReturnCode
:
string
=
await
executor
.
getRemoteFileContent
(
runnerReturnCodeFilePath
);
const
match
:
RegExpMatchArray
|
null
=
runnerReturnCode
.
trim
()
.
match
(
/^-
?(\d
+
)\s
+
(\d
+
)
$/
);
if
(
match
!==
null
)
{
const
{
1
:
code
}
=
match
;
// Update trial job's status based on result code
if
(
parseInt
(
code
,
10
)
===
0
)
{
environment
.
setStatus
(
'
SUCCEEDED
'
);
}
else
{
environment
.
setStatus
(
'
FAILED
'
);
}
this
.
releaseEnvironmentResource
(
environment
);
}
const
jobpidPath
:
string
=
`
${
environment
.
runnerWorkingFolder
}
/pid`
;
const
runnerReturnCodeFilePath
:
string
=
`
${
environment
.
runnerWorkingFolder
}
/code`
;
/* eslint-disable require-atomic-updates */
try
{
// check if pid file exist
const
pidExist
=
await
executor
.
fileExist
(
jobpidPath
);
if
(
!
pidExist
)
{
return
;
}
const
isAlive
=
await
executor
.
isProcessAlive
(
jobpidPath
);
environment
.
status
=
'
RUNNING
'
;
// if the process of jobpid is not alive any more
if
(
!
isAlive
)
{
const
remoteEnvironment
:
RemoteMachineEnvironmentInformation
=
environment
as
RemoteMachineEnvironmentInformation
;
if
(
remoteEnvironment
.
rmMachineMeta
===
undefined
)
{
throw
new
Error
(
`
${
remoteEnvironment
.
id
}
machine meta not initialized!`
);
}
this
.
log
.
info
(
`pid in
${
remoteEnvironment
.
rmMachineMeta
.
ip
}
:
${
jobpidPath
}
is not alive!`
);
if
(
fs
.
existsSync
(
runnerReturnCodeFilePath
))
{
const
runnerReturnCode
:
string
=
await
executor
.
getRemoteFileContent
(
runnerReturnCodeFilePath
);
const
match
:
RegExpMatchArray
|
null
=
runnerReturnCode
.
trim
()
.
match
(
/^-
?(\d
+
)\s
+
(\d
+
)
$/
);
if
(
match
!==
null
)
{
const
{
1
:
code
}
=
match
;
// Update trial job's status based on result code
if
(
parseInt
(
code
,
10
)
===
0
)
{
environment
.
setStatus
(
'
SUCCEEDED
'
);
}
else
{
environment
.
setStatus
(
'
FAILED
'
);
}
this
.
releaseEnvironmentResource
(
environment
);
}
}
catch
(
error
)
{
this
.
releaseEnvironmentResource
(
environment
);
this
.
log
.
error
(
`Update job status exception, error is
${
error
.
message
}
`
);
}
}
}
catch
(
error
)
{
this
.
log
.
error
(
`Update job status exception, error is
${
error
.
message
}
`
);
}
}
public
async
refreshEnvironmentsStatus
(
environments
:
EnvironmentInformation
[]):
Promise
<
void
>
{
...
...
@@ -245,6 +248,7 @@ export class RemoteEnvironmentService extends EnvironmentService {
'
envs
'
,
environment
.
id
)
environment
.
command
=
`cd
${
environment
.
runnerWorkingFolder
}
&& \
${
environment
.
command
}
--job_pid_file
${
environment
.
runnerWorkingFolder
}
/pid \
1>
${
environment
.
runnerWorkingFolder
}
/trialrunner_stdout 2>
${
environment
.
runnerWorkingFolder
}
/trialrunner_stderr \
&& echo $?
\`
date +%s%3N
\`
>
${
environment
.
runnerWorkingFolder
}
/code`
;
return
Promise
.
resolve
(
true
);
}
...
...
@@ -266,7 +270,6 @@ ${environment.command} --job_pid_file ${environment.runnerWorkingFolder}/pid \
// Execute command in remote machine
executor
.
executeScript
(
executor
.
joinPath
(
environment
.
runnerWorkingFolder
,
executor
.
getScriptName
(
"
run
"
)),
true
,
false
);
environment
.
status
=
'
RUNNING
'
;
if
(
environment
.
rmMachineMeta
===
undefined
)
{
throw
new
Error
(
`
${
environment
.
id
}
rmMachineMeta not initialized!`
);
}
...
...
ts/nni_manager/training_service/reusable/trialDispatcher.ts
View file @
3b90b9d9
...
...
@@ -663,19 +663,22 @@ class TrialDispatcher implements TrainingService {
trial
.
status
=
"
RUNNING
"
;
await
this
.
commandChannel
.
sendCommand
(
trial
.
environment
,
NEW_TRIAL_JOB
,
trial
.
settings
);
}
/**
* release the trial assigned environment resources
* @param trial
*/
private
releaseEnvironment
(
trial
:
TrialDetail
):
void
{
if
(
undefined
===
trial
.
environment
)
{
throw
new
Error
(
`TrialDispatcher: environment is not assigned to trial
${
trial
.
id
}
, and cannot be released!`
);
}
if
(
trial
.
environment
.
runningTrialCount
<=
0
)
{
throw
new
Error
(
`TrialDispatcher: environment
${
trial
.
environment
.
id
}
has no counted running trial!`
);
if
(
trial
.
environment
!==
undefined
)
{
if
(
trial
.
environment
.
runningTrialCount
<=
0
)
{
throw
new
Error
(
`TrialDispatcher: environment
${
trial
.
environment
.
id
}
has no counted running trial!`
);
}
trial
.
environment
.
runningTrialCount
--
;
trial
.
environment
=
undefined
;
}
if
(
true
===
this
.
enableGpuScheduler
)
{
this
.
gpuScheduler
.
removeGpuReservation
(
trial
);
}
trial
.
environment
.
runningTrialCount
--
;
trial
.
environment
=
undefined
;
}
private
async
handleMetricData
(
trialId
:
string
,
data
:
any
):
Promise
<
void
>
{
...
...
ts/nni_manager/yarn.lock
View file @
3b90b9d9
...
...
@@ -1096,10 +1096,10 @@ cli-width@^2.0.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639"
cliui@^7.0.
0
:
version "7.0.
1
"
resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.
1
.tgz#
a4cb67aad45cd83d8d05128fc9f4d8fbb887e6b3
"
integrity sha512-
rcvHOWyGyid6I1WjT/3NatKj2kDt9OdSHSXpyLXaMWFbKpGACNW8pRhhdPUq9MWUOdwn8Rz9AVETjF4105rZZQ
==
cliui@^7.0.
2
:
version "7.0.
3
"
resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.
3
.tgz#
ef180f26c8d9bff3927ee52428bfec2090427981
"
integrity sha512-
Gj3QHTkVMPKqwP3f7B4KPkBZRMR9r4rfi5bXFpg1a+Svvj8l7q5CnkBkVQzfxT5DFSsGk2+PascOgL0JYkL2kw
==
dependencies:
string-width "^4.2.0"
strip-ansi "^6.0.0"
...
...
@@ -1336,7 +1336,7 @@ debug@^3.1.0:
dependencies:
ms "^2.1.1"
debuglog@^1.0.1:
debuglog@*,
debuglog@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492"
...
...
@@ -1606,10 +1606,10 @@ es6-promisify@^5.0.0:
dependencies:
es6-promise "^4.0.3"
escalade@^3.
0.2
:
version "3.1.
0
"
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.
0
.tgz#
e8e2d7c7a8b76f6ee64c2181d6b8151441602d4e
"
integrity sha512-
mAk+hPSO8fLDkhV7V0dXazH5pDc6MrjBTPyD3VeKzxnVFjH1MIxbCdqGZB9O8+EwWakZs3ZCbDS4IpRt79V1ig
==
escalade@^3.
1.1
:
version "3.1.
1
"
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.
1
.tgz#
d8cfdc7000965c5a0174b4a82eaa5c0552742e40
"
integrity sha512-
k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw
==
escape-html@~1.0.3:
version "1.0.3"
...
...
@@ -2392,7 +2392,7 @@ import-lazy@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43"
imurmurhash@^0.1.4:
imurmurhash@*,
imurmurhash@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
integrity sha1-khi5srkoojixPcT7a21XbyMUU+o=
...
...
@@ -3074,6 +3074,11 @@ lockfile@^1.0.4:
dependencies:
signal-exit "^3.0.2"
lodash._baseindexof@*:
version "3.1.0"
resolved "https://registry.yarnpkg.com/lodash._baseindexof/-/lodash._baseindexof-3.1.0.tgz#fe52b53a1c6761e42618d654e4a25789ed61822c"
integrity sha1-/lK1OhxnYeQmGNZU5KJXie1hgiw=
lodash._baseuniq@~4.6.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/lodash._baseuniq/-/lodash._baseuniq-4.6.0.tgz#0ebb44e456814af7905c6212fa2c9b2d51b841e8"
...
...
@@ -3081,10 +3086,32 @@ lodash._baseuniq@~4.6.0:
lodash._createset "~4.0.0"
lodash._root "~3.0.0"
lodash._bindcallback@*:
version "3.0.1"
resolved "https://registry.yarnpkg.com/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz#e531c27644cf8b57a99e17ed95b35c748789392e"
integrity sha1-5THCdkTPi1epnhftlbNcdIeJOS4=
lodash._cacheindexof@*:
version "3.0.2"
resolved "https://registry.yarnpkg.com/lodash._cacheindexof/-/lodash._cacheindexof-3.0.2.tgz#3dc69ac82498d2ee5e3ce56091bafd2adc7bde92"
integrity sha1-PcaayCSY0u5ePOVgkbr9Ktx73pI=
lodash._createcache@*:
version "3.1.2"
resolved "https://registry.yarnpkg.com/lodash._createcache/-/lodash._createcache-3.1.2.tgz#56d6a064017625e79ebca6b8018e17440bdcf093"
integrity sha1-VtagZAF2JeeevKa4AY4XRAvc8JM=
dependencies:
lodash._getnative "^3.0.0"
lodash._createset@~4.0.0:
version "4.0.3"
resolved "https://registry.yarnpkg.com/lodash._createset/-/lodash._createset-4.0.3.tgz#0f4659fbb09d75194fa9e2b88a6644d363c9fe26"
lodash._getnative@*, lodash._getnative@^3.0.0:
version "3.9.1"
resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5"
integrity sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=
lodash._root@~3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/lodash._root/-/lodash._root-3.0.1.tgz#fba1c4524c19ee9a5f8136b4609f017cf4ded692"
...
...
@@ -3133,6 +3160,11 @@ lodash.pick@^4.4.0:
version "4.4.0"
resolved "https://registry.yarnpkg.com/lodash.pick/-/lodash.pick-4.4.0.tgz#52f05610fff9ded422611441ed1fc123a03001b3"
lodash.restparam@*:
version "3.6.1"
resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805"
integrity sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU=
lodash.unescape@4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/lodash.unescape/-/lodash.unescape-4.0.1.tgz#bf2249886ce514cda112fae9218cdc065211fc9c"
...
...
@@ -3723,8 +3755,9 @@ npm-run-path@^2.0.0:
path-key "^2.0.0"
npm-user-validate@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/npm-user-validate/-/npm-user-validate-1.0.0.tgz#8ceca0f5cea04d4e93519ef72d0557a75122e951"
version "1.0.1"
resolved "https://registry.yarnpkg.com/npm-user-validate/-/npm-user-validate-1.0.1.tgz#31428fc5475fe8416023f178c0ab47935ad8c561"
integrity sha512-uQwcd/tY+h1jnEaze6cdX/LrhWhoBxfSknxentoqmIuStxUExxjWd3ULMLFPiFUrZKbOVMowH6Jq2FRWfmhcEw==
npm@5.1.0, npm@>=6.14.8:
version "6.14.8"
...
...
@@ -5724,10 +5757,10 @@ y18n@^4.0.0:
resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b"
integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==
y18n@^5.0.
1
:
version "5.0.
1
"
resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.
1
.tgz#
1ad2a7eddfa8bce7caa2e1f6b5da96c39d99d571
"
integrity sha512-
/jJ831jEs4vGDbYPQp4yGKDYPSCCEQ45uZWJHE1AoYBzqdZi8+LDWas0z4HrmJXmKdpFsTiowSHXdxyFhpmdMg
==
y18n@^5.0.
2
:
version "5.0.
4
"
resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.
4
.tgz#
0ab2db89dd5873b5ec4682d8e703e833373ea897
"
integrity sha512-
deLOfD+RvFgrpAmSZgfGdWYE+OKyHcVHaRQ7NphG/63scpRvTHHeQMAxGGvaLVGJ+HYVcCXlzcTK0ZehFf+eHQ
==
yallist@^2.1.2:
version "2.1.2"
...
...
@@ -5746,10 +5779,10 @@ yallist@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
yargs-parser@13.1.2, yargs-parser@>=20.2.0, yargs-parser@^20.
0.0
:
version "20.2.
2
"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.
2
.tgz#
84562c6b1c41ccec2f13d346c7dd83f8d1a0dc70
"
integrity sha512-
XmrpXaTl6noDsf1dKpBuUNCOHqjs0g3jRMXf/ztRxdOmb+er8kE5z5b55Lz3p5u2T8KJ59ENBnASS8/iapVJ5g
==
yargs-parser@13.1.2, yargs-parser@>=20.2.0, yargs-parser@^20.
2.2
:
version "20.2.
3
"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.
3
.tgz#
92419ba867b858c868acf8bae9bf74af0dd0ce26
"
integrity sha512-
emOFRT9WVHw03QSvN5qor9QQT9+sw5vwxfYweivSMHTcAXPefwVae2FjO7JJjj8hCE4CzPOPeFM83VwT29HCww
==
yargs-unparser@1.6.1:
version "1.6.1"
...
...
@@ -5763,17 +5796,17 @@ yargs-unparser@1.6.1:
yargs "^14.2.3"
yargs@13.3.2, yargs@>=16.0.3, yargs@^11.0.0, yargs@^14.2.3, yargs@^15.0.2, yargs@^8.0.2:
version "16.
0.3
"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.
0.3
.tgz#
7a919b9e43c90f80d4a142a89795e85399a7e54c
"
integrity sha512-
6+nLw8xa9uK1BOEOykaiYAJVh6/CjxWXK/q9b5FpRgNslt8s22F2xMBqVIKgCRjNgGvGPBy8Vog7WN7yh4amtA
==
version "16.
1.0
"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.
1.0
.tgz#
fc333fe4791660eace5a894b39d42f851cd48f2a
"
integrity sha512-
upWFJOmDdHN0syLuESuvXDmrRcWd1QafJolHskzaw79uZa7/x53gxQKiR07W59GWY1tFhhU/Th9DrtSfpS782g
==
dependencies:
cliui "^7.0.
0
"
escalade "^3.
0.2
"
cliui "^7.0.
2
"
escalade "^3.
1.1
"
get-caller-file "^2.0.5"
require-directory "^2.1.1"
string-width "^4.2.0"
y18n "^5.0.
1
"
yargs-parser "^20.
0.0
"
y18n "^5.0.
2
"
yargs-parser "^20.
2.2
"
yn@^2.0.0:
version "2.0.0"
...
...
ts/webui/src/App.scss
View file @
3b90b9d9
...
...
@@ -53,7 +53,7 @@
.ms-Callout-main
{
p
{
font-weight
:
500
;
color
:
#
333
;
color
:
#
fff
;
}
}
...
...
ts/webui/src/App.tsx
View file @
3b90b9d9
...
...
@@ -12,6 +12,7 @@ interface AppState {
columnList
:
string
[];
experimentUpdateBroadcast
:
number
;
trialsUpdateBroadcast
:
number
;
maxDurationUnit
:
string
;
metricGraphMode
:
'
max
'
|
'
min
'
;
// tuner's optimize_mode filed
isillegalFinal
:
boolean
;
expWarningMessage
:
string
;
...
...
@@ -26,11 +27,14 @@ export const AppContext = React.createContext({
trialsUpdateBroadcast
:
0
,
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
updateOverviewPage
:
()
=>
{}
...
...
@@ -47,6 +51,7 @@ class App extends React.Component<{}, AppState> {
experimentUpdateBroadcast
:
0
,
trialsUpdateBroadcast
:
0
,
metricGraphMode
:
'
max
'
,
maxDurationUnit
:
'
m
'
,
isillegalFinal
:
false
,
expWarningMessage
:
''
,
bestTrialEntries
:
'
10
'
,
...
...
@@ -91,6 +96,11 @@ class App extends React.Component<{}, AppState> {
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
...
...
@@ -114,7 +124,8 @@ class App extends React.Component<{}, AppState> {
metricGraphMode
,
isillegalFinal
,
expWarningMessage
,
bestTrialEntries
bestTrialEntries
,
maxDurationUnit
}
=
this
.
state
;
if
(
experimentUpdateBroadcast
===
0
||
trialsUpdateBroadcast
===
0
)
{
return
null
;
// TODO: render a loading page
...
...
@@ -137,7 +148,24 @@ class App extends React.Component<{}, AppState> {
<
Stack
className
=
'contentBox'
>
<
Stack
className
=
'content'
>
{
/* search space & config */
}
<
TrialConfigButton
/>
<
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
}
}
>
<
TrialConfigButton
/>
</
AppContext
.
Provider
>
{
/* if api has error field, show error message */
}
{
errorList
.
map
(
(
item
,
key
)
=>
...
...
@@ -160,6 +188,8 @@ class App extends React.Component<{}, AppState> {
experimentUpdateBroadcast
,
trialsUpdateBroadcast
,
metricGraphMode
,
maxDurationUnit
,
changeMaxDurationUnit
:
this
.
changeMaxDurationUnit
,
changeMetricGraphMode
:
this
.
changeMetricGraphMode
,
bestTrialEntries
,
changeEntries
:
this
.
changeEntries
,
...
...
ts/webui/src/components/Overview.tsx
View file @
3b90b9d9
...
...
@@ -10,7 +10,8 @@ import { ReBasicInfo } from './overview/experiment/BasicInfo';
import
{
ExpDuration
}
from
'
./overview/count/ExpDuration
'
;
import
{
ExpDurationContext
}
from
'
./overview/count/ExpDurationContext
'
;
import
{
TrialCount
}
from
'
./overview/count/TrialCount
'
;
import
{
Command
}
from
'
./overview/experiment/Command
'
;
import
{
Command1
}
from
'
./overview/command/Command1
'
;
import
{
Command2
}
from
'
./overview/command/Command2
'
;
import
{
TitleContext
}
from
'
./overview/TitleContext
'
;
import
{
itemStyle1
,
itemStyleSucceed
,
itemStyle2
,
entriesOption
}
from
'
./overview/overviewConst
'
;
import
'
../static/style/overview/overview.scss
'
;
...
...
@@ -66,7 +67,13 @@ class Overview extends React.Component<{}, OverviewState> {
return
(
<
AppContext
.
Consumer
>
{
(
value
):
React
.
ReactNode
=>
{
const
{
metricGraphMode
,
bestTrialEntries
,
updateOverviewPage
}
=
value
;
const
{
metricGraphMode
,
bestTrialEntries
,
maxDurationUnit
,
updateOverviewPage
,
changeMaxDurationUnit
}
=
value
;
const
maxActive
=
metricGraphMode
===
'
max
'
?
'
active
'
:
''
;
const
minActive
=
metricGraphMode
===
'
min
'
?
'
active
'
:
''
;
return
(
...
...
@@ -88,18 +95,29 @@ class Overview extends React.Component<{}, OverviewState> {
<
Title
/>
</
TitleContext
.
Provider
>
<
ExpDurationContext
.
Provider
value
=
{
{
maxExecDuration
,
execDuration
,
updateOverviewPage
}
}
value
=
{
{
maxExecDuration
,
execDuration
,
updateOverviewPage
,
maxDurationUnit
,
changeMaxDurationUnit
}
}
>
<
ExpDuration
/>
</
ExpDurationContext
.
Provider
>
</
div
>
<
div
className
=
'empty'
/>
<
div
className
=
'trialCount'
>
<
TitleContext
.
Provider
value
=
{
{
text
:
'
Trial numbers
'
,
icon
:
'
NumberSymbol
'
}
}
>
<
Title
/>
</
TitleContext
.
Provider
>
<
ExpDurationContext
.
Provider
value
=
{
{
maxExecDuration
,
execDuration
,
updateOverviewPage
}
}
value
=
{
{
maxExecDuration
,
execDuration
,
updateOverviewPage
,
maxDurationUnit
,
changeMaxDurationUnit
}
}
>
<
TrialCount
/>
</
ExpDurationContext
.
Provider
>
...
...
@@ -114,7 +132,6 @@ class Overview extends React.Component<{}, OverviewState> {
</
TitleContext
.
Provider
>
</
div
>
<
div
className
=
'topTrialTitle'
>
{
/* <Stack horizontal horizontalAlign='space-between'> */
}
<
Stack
horizontal
horizontalAlign
=
'end'
>
<
DefaultButton
onClick
=
{
this
.
clickMaxTop
}
...
...
@@ -152,8 +169,11 @@ class Overview extends React.Component<{}, OverviewState> {
</
Stack
>
<
SuccessTable
trialIds
=
{
bestTrials
.
map
(
trial
=>
trial
.
info
.
id
)
}
/>
</
div
>
<
div
className
=
'overviewCommand'
>
<
Command
/>
<
div
className
=
'overviewCommand1'
>
<
Command1
/>
</
div
>
<
div
className
=
'overviewCommand2'
>
<
Command2
/>
</
div
>
<
div
className
=
'overviewChart'
>
<
Stack
horizontal
>
...
...
ts/webui/src/components/TrialsDetail.tsx
View file @
3b90b9d9
...
...
@@ -82,7 +82,7 @@ class TrialsDetail extends React.Component<{}, TrialDetailState> {
</
Pivot
>
</
div
>
{
/* trial table list */
}
<
div
style
=
{
{
backgroundColor
:
'
#fff
'
}
}
>
<
div
style
=
{
{
backgroundColor
:
'
#fff
'
,
marginTop
:
10
}
}
>
<
TableList
tableSource
=
{
source
}
trialsUpdateBroadcast
=
{
this
.
context
.
trialsUpdateBroadcast
}
...
...
ts/webui/src/components/modals/CustomizedTrial.tsx
View file @
3b90b9d9
...
...
@@ -60,7 +60,10 @@ class Customize extends React.Component<CustomizeProps, CustomizeState> {
Object
.
keys
(
customized
).
map
(
item
=>
{
if
(
item
!==
'
tag
'
)
{
// unified data type
if
(
typeof
copyTrialParameter
[
item
]
===
'
number
'
&&
typeof
customized
[
item
]
===
'
string
'
)
{
if
(
(
typeof
copyTrialParameter
[
item
]
===
'
number
'
&&
typeof
customized
[
item
]
===
'
string
'
)
||
(
typeof
copyTrialParameter
[
item
]
===
'
boolean
'
&&
typeof
customized
[
item
]
===
'
string
'
)
)
{
customized
[
item
]
=
JSON
.
parse
(
customized
[
item
]);
}
if
(
searchSpace
[
item
]
===
undefined
)
{
...
...
ts/webui/src/components/modals/Killjob.tsx
View file @
3b90b9d9
...
...
@@ -112,11 +112,15 @@ class KillJob extends React.Component<KillJobProps, KillJobState> {
setInitialFocus
=
{
true
}
>
<
div
className
=
{
styles
.
header
}
>
<
p
className
=
{
styles
.
title
}
>
Kill trial
</
p
>
<
p
className
=
{
styles
.
title
}
style
=
{
{
color
:
'
#333
'
}
}
>
Kill trial
</
p
>
</
div
>
<
div
className
=
{
styles
.
inner
}
>
<
div
>
<
p
className
=
{
styles
.
subtext
}
>
{
prompString
}
</
p
>
<
p
className
=
{
styles
.
subtext
}
style
=
{
{
color
:
'
#333
'
}
}
>
{
prompString
}
</
p
>
</
div
>
</
div
>
<
FocusZone
>
...
...
ts/webui/src/components/overview/
experiment
/Command.tsx
→
ts/webui/src/components/overview/
command
/Command
1
.tsx
View file @
3b90b9d9
import
React
from
'
react
'
;
import
{
TooltipHost
,
Stack
}
from
'
@fluentui/react
'
;
import
{
EXPERIMENT
}
from
'
../../../static/datamodel
'
;
import
'
../../../static/style/overview/command.scss
'
;
export
const
Command
=
():
any
=>
{
const
clusterMetaData
=
EXPERIMENT
.
profile
.
params
.
clusterMetaData
;
export
const
Command1
=
():
any
=>
{
const
tuner
=
EXPERIMENT
.
profile
.
params
.
tuner
;
const
advisor
=
EXPERIMENT
.
profile
.
params
.
advisor
;
const
assessor
=
EXPERIMENT
.
profile
.
params
.
assessor
;
let
title
=
''
;
let
builtinName
=
''
;
let
trialCommand
=
'
unknown
'
;
if
(
tuner
!==
undefined
)
{
title
=
title
.
concat
(
'
Tuner
'
);
if
(
tuner
.
builtinTunerName
!==
undefined
)
{
...
...
@@ -29,35 +26,15 @@ export const Command = (): any => {
builtinName
=
builtinName
.
concat
(
assessor
.
builtinAssessorName
);
}
}
if
(
clusterMetaData
!==
undefined
)
{
for
(
const
item
of
clusterMetaData
)
{
if
(
item
.
key
===
'
command
'
)
{
trialCommand
=
item
.
value
;
}
}
}
return
(
<
div
className
=
'
command
basic'
>
<
div
className
=
'command1'
>
<
p
>
Training platform
</
p
>
<
div
className
=
'basic'
>
<
div
>
<
p
className
=
'command'
>
Training platform
</
p
>
<
div
className
=
'nowrap'
>
{
EXPERIMENT
.
profile
.
params
.
trainingServicePlatform
}
</
div
>
<
p
className
=
'lineMargin'
>
{
title
}
</
p
>
<
div
className
=
'nowrap'
>
{
builtinName
}
</
div
>
</
div
>
<
Stack
className
=
'command2'
>
<
p
>
Log directory
</
p
>
<
div
className
=
'nowrap'
>
<
TooltipHost
content
=
{
EXPERIMENT
.
profile
.
logDir
||
'
unknown
'
}
className
=
'nowrap'
>
{
EXPERIMENT
.
profile
.
logDir
||
'
unknown
'
}
</
TooltipHost
>
</
div
>
<
p
className
=
'lineMargin'
>
Trial command
</
p
>
<
div
className
=
'nowrap'
>
<
TooltipHost
content
=
{
trialCommand
||
'
unknown
'
}
className
=
'nowrap'
>
{
trialCommand
||
'
unknown
'
}
</
TooltipHost
>
</
div
>
</
Stack
>
</
div
>
);
};
ts/webui/src/components/overview/command/Command2.tsx
0 → 100644
View file @
3b90b9d9
import
React
from
'
react
'
;
import
{
TooltipHost
,
DirectionalHint
}
from
'
@fluentui/react
'
;
import
{
EXPERIMENT
}
from
'
../../../static/datamodel
'
;
import
{
TOOLTIP_BACKGROUND_COLOR
}
from
'
../../../static/const
'
;
import
'
../../../static/style/overview/command.scss
'
;
export
const
Command2
=
():
any
=>
{
const
clusterMetaData
=
EXPERIMENT
.
profile
.
params
.
clusterMetaData
;
let
trialCommand
=
'
unknown
'
;
if
(
clusterMetaData
!==
undefined
)
{
for
(
const
item
of
clusterMetaData
)
{
if
(
item
.
key
===
'
command
'
)
{
trialCommand
=
item
.
value
as
string
;
}
if
(
item
.
key
===
'
trial_config
'
)
{
if
(
typeof
item
.
value
===
'
object
'
&&
'
command
'
in
item
.
value
)
{
trialCommand
=
item
.
value
.
command
as
string
;
}
}
}
}
return
(
<
div
className
=
'basic'
>
<
p
className
=
'command'
>
Log directory
</
p
>
<
div
className
=
'nowrap'
>
<
TooltipHost
content
=
{
EXPERIMENT
.
profile
.
logDir
||
'
unknown
'
}
className
=
'nowrap'
directionalHint
=
{
DirectionalHint
.
bottomCenter
}
tooltipProps
=
{
{
calloutProps
:
{
styles
:
{
beak
:
{
background
:
TOOLTIP_BACKGROUND_COLOR
},
beakCurtain
:
{
background
:
TOOLTIP_BACKGROUND_COLOR
},
calloutMain
:
{
background
:
TOOLTIP_BACKGROUND_COLOR
}
}
}
}
}
>
{
EXPERIMENT
.
profile
.
logDir
||
'
unknown
'
}
</
TooltipHost
>
</
div
>
<
p
className
=
'lineMargin'
>
Trial command
</
p
>
<
div
className
=
'nowrap'
>
<
TooltipHost
content
=
{
trialCommand
||
'
unknown
'
}
className
=
'nowrap'
directionalHint
=
{
DirectionalHint
.
bottomCenter
}
tooltipProps
=
{
{
calloutProps
:
{
styles
:
{
beak
:
{
background
:
TOOLTIP_BACKGROUND_COLOR
},
beakCurtain
:
{
background
:
TOOLTIP_BACKGROUND_COLOR
},
calloutMain
:
{
background
:
TOOLTIP_BACKGROUND_COLOR
}
}
}
}
}
>
{
trialCommand
||
'
unknown
'
}
</
TooltipHost
>
</
div
>
</
div
>
);
};
ts/webui/src/components/overview/count/EditExperimentParam.tsx
View file @
3b90b9d9
import
React
,
{
useState
,
useCallback
,
useContext
}
from
'
react
'
;
import
axios
from
'
axios
'
;
import
{
Dropdown
}
from
'
@fluentui/react
'
;
import
{
EXPERIMENT
}
from
'
../../../static/datamodel
'
;
import
{
AppContext
}
from
'
../../../App
'
;
import
{
EditExpeParamContext
}
from
'
./context
'
;
import
{
MANAGER_IP
}
from
'
../../../static/c
onst
'
;
import
{
convertTimeToSecond
}
from
'
../../../static/
function
'
;
import
{
durationUnit
}
from
'
../overviewC
onst
'
;
import
{
MANAGER_IP
,
MAX_TRIAL_NUMBERS
}
from
'
../../../static/
const
'
;
import
{
Edit
,
CheckMark
,
Cancel
}
from
'
../../buttons/Icon
'
;
import
MessageInfo
from
'
../../modals/MessageInfo
'
;
import
'
../../../static/style/overview/count.scss
'
;
...
...
@@ -28,12 +30,14 @@ export const EditExperimentParam = (): any => {
const
{
title
,
field
,
editType
,
maxExecDuration
,
maxTrialNum
,
trialConcurrency
,
updateOverviewPage
}
=
useContext
(
EditExpeParamContext
);
const
{
maxDurationUnit
,
changeMaxDurationUnit
}
=
useContext
(
AppContext
);
const
[
unit
,
setUnit
]
=
useState
(
maxDurationUnit
);
let
defaultVal
=
''
;
let
editVal
=
''
;
if
(
title
===
'
Max duration
'
)
{
defaultVal
=
maxExecDuration
;
editVal
=
maxExecDuration
;
}
else
if
(
title
===
'
Max trial numbers
'
)
{
}
else
if
(
title
===
MAX_TRIAL_NUMBERS
)
{
defaultVal
=
maxTrialNum
.
toString
();
editVal
=
maxTrialNum
.
toString
();
}
else
{
...
...
@@ -46,32 +50,64 @@ export const EditExperimentParam = (): any => {
setEditValInput
(
event
.
target
.
value
);
}
function
cancelEdit
():
void
{
setEditValInput
(
defaultVal
);
showPencil
();
function
showMessageInfo
(
info
:
string
,
typeInfo
:
string
):
any
{
setInfo
(
info
);
setTypeInfo
(
typeInfo
);
showSucceedInfo
();
setTimeout
(
hideSucceedInfo
,
2000
);
}
function
updateUnit
(
event
:
React
.
FormEvent
<
HTMLDivElement
>
,
item
:
any
):
void
{
if
(
item
!==
undefined
)
{
setUnit
(
item
.
key
);
}
}
async
function
confirmEdit
():
Promise
<
void
>
{
const
isMaxDuration
=
title
===
'
Max duration
'
;
const
newProfile
=
Object
.
assign
({},
EXPERIMENT
.
profile
);
let
beforeParam
=
''
;
if
(
!
isMaxDuration
&&
!
editInputVal
.
match
(
/^
[
1-9
]\d
*$/
))
{
showMessageInfo
(
'
Please enter a positive integer!
'
,
'
error
'
);
return
;
if
(
isMaxDuration
)
{
if
(
!
editInputVal
.
match
(
/^
\d
+
(?=\.{0,1}\d
+$|$
)
/
))
{
showMessageInfo
(
'
Please enter a number!
'
,
'
error
'
);
setEditValInput
(
defaultVal
);
return
;
}
}
else
{
if
(
!
editInputVal
.
match
(
/^
[
1-9
]\d
*$/
))
{
showMessageInfo
(
'
Please enter a positive integer!
'
,
'
error
'
);
setEditValInput
(
defaultVal
);
return
;
}
}
if
(
isMaxDuration
)
{
beforeParam
=
maxExecDuration
;
}
else
if
(
title
===
'
Max trial numbers
'
)
{
}
else
if
(
title
===
MAX_TRIAL_NUMBERS
)
{
beforeParam
=
maxTrialNum
.
toString
();
}
else
{
beforeParam
=
trialConcurrency
.
toString
();
}
if
(
editInputVal
===
beforeParam
)
{
showMessageInfo
(
`Trial
${
field
}
has not changed`
,
'
error
'
);
return
;
if
(
isMaxDuration
)
{
if
(
maxDurationUnit
===
unit
)
{
showMessageInfo
(
`Trial
${
field
}
has not changed`
,
'
error
'
);
return
;
}
}
else
{
showMessageInfo
(
`Trial
${
field
}
has not changed`
,
'
error
'
);
return
;
}
}
if
(
isMaxDuration
)
{
newProfile
.
params
[
field
]
=
convertTimeToSecond
(
editInputVal
);
const
maxDura
=
JSON
.
parse
(
editInputVal
);
if
(
unit
===
'
m
'
)
{
newProfile
.
params
[
field
]
=
maxDura
*
60
;
}
else
if
(
unit
===
'
h
'
)
{
newProfile
.
params
[
field
]
=
maxDura
*
3600
;
}
else
{
newProfile
.
params
[
field
]
=
maxDura
*
24
*
60
*
60
;
}
}
else
{
newProfile
.
params
[
field
]
=
parseInt
(
editInputVal
,
10
);
}
...
...
@@ -82,7 +118,8 @@ export const EditExperimentParam = (): any => {
params
:
{
update_type
:
editType
}
});
if
(
res
.
status
===
200
)
{
showMessageInfo
(
`Successfully updated
${
field
}
`
,
'
success
'
);
showMessageInfo
(
`Successfully updated experiment's
${
field
}
`
,
'
success
'
);
changeMaxDurationUnit
(
unit
);
}
}
catch
(
error
)
{
if
(
error
.
response
&&
error
.
response
.
data
.
error
)
{
...
...
@@ -94,54 +131,88 @@ export const EditExperimentParam = (): any => {
}
else
{
showMessageInfo
(
`Failed to update trial
${
field
}
\nUnknown error`
,
'
error
'
);
}
setEditValInput
(
defaultVal
);
}
showPencil
();
updateOverviewPage
();
}
function
showMessageInfo
(
info
:
string
,
typeInfo
:
string
):
any
{
setInfo
(
info
);
setTypeInfo
(
typeInfo
);
showSucceedInfo
();
setTimeout
(
hideSucceedInfo
,
2000
);
function
cancelEdit
():
void
{
setEditValInput
(
defaultVal
);
showPencil
();
setUnit
(
maxDurationUnit
);
}
function
convertUnit
(
val
:
string
):
string
{
if
(
val
===
'
d
'
)
{
return
'
day
'
;
}
else
if
(
val
===
'
h
'
)
{
return
'
hour
'
;
}
else
if
(
val
===
'
m
'
)
{
return
'
min
'
;
}
else
{
return
val
;
}
}
return
(
<
EditExpeParam
Context
.
Consumer
>
{
(
value
):
React
.
ReactNode
=>
{
<
App
Context
.
Consumer
>
{
(
value
s
):
React
.
ReactNode
=>
{
return
(
<
React
.
Fragment
>
<
p
>
{
value
.
title
}
</
p
>
<
div
>
<
input
className
=
{
`
${
value
.
field
}
durationInput`
}
ref
=
{
DurationInputRef
}
disabled
=
{
isShowPencil
?
true
:
false
}
value
=
{
editInputVal
}
onChange
=
{
setInputVal
}
/>
{
isShowPencil
&&
(
<
span
className
=
'edit'
onClick
=
{
hidePencil
}
>
{
Edit
}
</
span
>
)
}
{
!
isShowPencil
&&
(
<
span
className
=
'series'
>
<
span
className
=
'confirm'
onClick
=
{
confirmEdit
}
>
{
CheckMark
}
</
span
>
<
span
className
=
'cancel'
onClick
=
{
cancelEdit
}
>
{
Cancel
}
</
span
>
</
span
>
)
}
<
EditExpeParamContext
.
Consumer
>
{
(
value
):
React
.
ReactNode
=>
{
let
editClassName
=
''
;
if
(
value
.
field
===
'
maxExecDuration
'
)
{
editClassName
=
isShowPencil
?
'
noEditDuration
'
:
'
editDuration
'
;
}
return
(
<
React
.
Fragment
>
<
div
className
=
{
`
${
editClassName
}
editparam`
}
>
<
span
>
{
value
.
title
}
</
span
>
<
input
className
=
{
`
${
value
.
field
}
editparam-Input`
}
ref
=
{
DurationInputRef
}
disabled
=
{
isShowPencil
?
true
:
false
}
value
=
{
editInputVal
}
onChange
=
{
setInputVal
}
/>
{
isShowPencil
&&
title
===
'
Max duration
'
&&
(
<
span
>
{
convertUnit
(
values
.
maxDurationUnit
)
}
</
span
>
)
}
{
!
isShowPencil
&&
title
===
'
Max duration
'
&&
(
<
Dropdown
selectedKey
=
{
unit
}
options
=
{
durationUnit
}
className
=
'editparam-dropdown'
onChange
=
{
updateUnit
}
/>
)
}
{
isShowPencil
&&
(
<
span
className
=
'edit'
onClick
=
{
hidePencil
}
>
{
Edit
}
</
span
>
)
}
{
!
isShowPencil
&&
(
<
span
className
=
'series'
>
<
span
className
=
'confirm'
onClick
=
{
confirmEdit
}
>
{
CheckMark
}
</
span
>
<
span
className
=
'cancel'
onClick
=
{
cancelEdit
}
>
{
Cancel
}
</
span
>
</
span
>
)
}
{
isShowSucceedInfo
&&
<
MessageInfo
className
=
'info'
typeInfo
=
{
typeInfo
}
info
=
{
info
}
/>
}
</
div
>
</
React
.
Fragment
>
{
isShowSucceedInfo
&&
(
<
MessageInfo
className
=
'info'
typeInfo
=
{
typeInfo
}
info
=
{
info
}
/>
)
}
</
div
>
</
React
.
Fragment
>
);
}
}
</
EditExpeParamContext
.
Consumer
>
);
}
}
</
EditExpeParam
Context
.
Consumer
>
</
App
Context
.
Consumer
>
);
};
ts/webui/src/components/overview/count/ExpDuration.tsx
View file @
3b90b9d9
import
React
from
'
react
'
;
import
{
Stack
,
TooltipHost
,
ProgressIndicator
}
from
'
@fluentui/react
'
;
import
{
Stack
,
ProgressIndicator
,
TooltipHost
,
DirectionalHint
}
from
'
@fluentui/react
'
;
import
{
EXPERIMENT
}
from
'
../../../static/datamodel
'
;
import
{
CONTROLTYPE
}
from
'
../../../static/const
'
;
import
{
convertDuration
}
from
'
../../../static/function
'
;
import
{
CONTROLTYPE
,
TOOLTIP_BACKGROUND_COLOR
}
from
'
../../../static/const
'
;
import
{
convertDuration
,
convertTimeAsUnit
}
from
'
../../../static/function
'
;
import
{
EditExperimentParam
}
from
'
./EditExperimentParam
'
;
import
{
ExpDurationContext
}
from
'
./ExpDurationContext
'
;
import
{
EditExpeParamContext
}
from
'
./context
'
;
import
{
durationItem1
,
durationItem2
}
from
'
./commonStyle
'
;
import
'
../../../static/style/overview/count.scss
'
;
const
itemStyle1
:
React
.
CSSProperties
=
{
width
:
'
62%
'
,
height
:
80
};
const
itemStyle2
:
React
.
CSSProperties
=
{
width
:
'
63%
'
,
height
:
80
,
textAlign
:
'
right
'
};
export
const
ExpDuration
=
():
any
=>
(
<
ExpDurationContext
.
Consumer
>
{
(
value
):
React
.
ReactNode
=>
{
const
{
maxExecDuration
,
execDuration
,
updateOverviewPage
}
=
value
;
const
{
maxExecDuration
,
execDuration
,
maxDurationUnit
,
updateOverviewPage
}
=
value
;
const
tooltip
=
maxExecDuration
-
execDuration
;
const
maxExecDurationStr
=
convertDuration
(
maxExecDuration
);
const
percent
=
execDuration
/
maxExecDuration
;
const
execDurationStr
=
convertDuration
(
execDuration
);
const
maxExecDurationStr
=
convertTimeAsUnit
(
maxDurationUnit
,
maxExecDuration
).
toString
();
return
(
<
Stack
horizontal
className
=
'ExpDuration'
>
<
div
style
=
{
itemStyle1
}
>
<
TooltipHost
content
=
{
`
${
convertDuration
(
tooltip
)}
remaining`
}
>
<
ProgressIndicator
percentComplete
=
{
percent
}
barHeight
=
{
15
}
/>
<
div
style
=
{
durationItem1
}
>
<
TooltipHost
content
=
{
`
${
convertDuration
(
tooltip
)}
remaining`
}
directionalHint
=
{
DirectionalHint
.
bottomCenter
}
tooltipProps
=
{
{
calloutProps
:
{
styles
:
{
beak
:
{
background
:
TOOLTIP_BACKGROUND_COLOR
},
beakCurtain
:
{
background
:
TOOLTIP_BACKGROUND_COLOR
},
calloutMain
:
{
background
:
TOOLTIP_BACKGROUND_COLOR
}
}
}
}
}
>
<
ProgressIndicator
className
=
{
EXPERIMENT
.
status
}
percentComplete
=
{
percent
}
barHeight
=
{
15
}
/>
</
TooltipHost
>
{
/* execDuration / maxDuration: 20min / 1h */
}
<
div
className
=
'exp-progress'
>
<
span
className
=
{
`
${
EXPERIMENT
.
status
}
bold`
}
>
{
execDurationStr
}
</
span
>
<
span
className
=
'joiner'
>
/
</
span
>
<
span
>
{
`
${
maxExecDurationStr
}
${
maxDurationUnit
}
`
}
</
span
>
</
div
>
</
div
>
<
div
style
=
{
itemStyle2
}
>
<
Stack
horizontal
></
Stack
>
<
div
style
=
{
durationItem2
}
>
<
EditExpeParamContext
.
Provider
value
=
{
{
editType
:
CONTROLTYPE
[
0
],
...
...
Prev
1
2
3
4
5
6
7
8
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