From cadf3a5564e7ada81455a85c94033bbaf65abba9 Mon Sep 17 00:00:00 2001 From: Lijiaoa <61399850+Lijiaoa@users.noreply.github.com> Date: Fri, 6 May 2022 16:53:06 +0800 Subject: [PATCH] Refactor log components(remove log collection) (#4794) --- .../components/common/ExpandableDetails.tsx | 22 -- .../ExpandableDetails/ExpandableIndex.tsx | 22 ++ .../common/ExpandableDetails/OpenRow.tsx | 168 ++++++++++++++++ .../common/ExpandableDetails/PaiTrialLog.tsx | 26 +++ .../common/ExpandableDetails/TrialLog.tsx | 27 +++ .../src/components/common/LogPathChild.tsx | 32 --- ts/webui/src/components/common/OpenRow.tsx | 190 ------------------ .../src/components/common/PaiTrialChild.tsx | 41 ---- .../src/components/common/PaiTrialLog.tsx | 55 ----- ts/webui/src/components/common/TrialLog.tsx | 25 --- .../experiment/overview/Overview.tsx | 2 +- .../overview/table/SuccessTable.tsx | 2 +- .../experiment/trialdetail/TrialsDetail.tsx | 1 - .../trialdetail/table/TableList.tsx | 4 +- ts/webui/src/static/interface.ts | 5 - ts/webui/src/static/model/experiment.ts | 4 - ts/webui/src/static/style/common/common.scss | 5 +- ts/webui/src/static/style/logPath.scss | 18 -- ts/webui/src/static/style/openRow.scss | 13 +- ts/webui/src/static/style/table.scss | 25 ++- 20 files changed, 269 insertions(+), 418 deletions(-) delete mode 100644 ts/webui/src/components/common/ExpandableDetails.tsx create mode 100644 ts/webui/src/components/common/ExpandableDetails/ExpandableIndex.tsx create mode 100644 ts/webui/src/components/common/ExpandableDetails/OpenRow.tsx create mode 100644 ts/webui/src/components/common/ExpandableDetails/PaiTrialLog.tsx create mode 100644 ts/webui/src/components/common/ExpandableDetails/TrialLog.tsx delete mode 100644 ts/webui/src/components/common/LogPathChild.tsx delete mode 100644 ts/webui/src/components/common/OpenRow.tsx delete mode 100644 ts/webui/src/components/common/PaiTrialChild.tsx delete mode 100644 ts/webui/src/components/common/PaiTrialLog.tsx delete mode 100644 ts/webui/src/components/common/TrialLog.tsx delete mode 100644 ts/webui/src/static/style/logPath.scss diff --git a/ts/webui/src/components/common/ExpandableDetails.tsx b/ts/webui/src/components/common/ExpandableDetails.tsx deleted file mode 100644 index 61be05755..000000000 --- a/ts/webui/src/components/common/ExpandableDetails.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import * as React from 'react'; -import { DetailsRow, IDetailsRowBaseProps } from '@fluentui/react'; -import OpenRow from './OpenRow'; - -interface ExpandableDetailsProps { - detailsProps: IDetailsRowBaseProps; - isExpand: boolean; -} - -class ExpandableDetails extends React.Component { - render(): React.ReactNode { - const { detailsProps, isExpand } = this.props; - return ( -
- - {isExpand && } -
- ); - } -} - -export default ExpandableDetails; diff --git a/ts/webui/src/components/common/ExpandableDetails/ExpandableIndex.tsx b/ts/webui/src/components/common/ExpandableDetails/ExpandableIndex.tsx new file mode 100644 index 000000000..f45683503 --- /dev/null +++ b/ts/webui/src/components/common/ExpandableDetails/ExpandableIndex.tsx @@ -0,0 +1,22 @@ +import * as React from 'react'; +import PropTypes from 'prop-types'; +import { DetailsRow } from '@fluentui/react'; +import OpenRow from './OpenRow'; +import '@style/table.scss'; + +const ExpandableDetails = (props): any => { + const { detailsProps, isExpand } = props; + return ( +
+ + {isExpand && } +
+ ); +}; + +ExpandableDetails.propTypes = { + detailsProps: PropTypes.object, + isExpand: PropTypes.bool +}; + +export default ExpandableDetails; diff --git a/ts/webui/src/components/common/ExpandableDetails/OpenRow.tsx b/ts/webui/src/components/common/ExpandableDetails/OpenRow.tsx new file mode 100644 index 000000000..4c8ec7eca --- /dev/null +++ b/ts/webui/src/components/common/ExpandableDetails/OpenRow.tsx @@ -0,0 +1,168 @@ +import React, { useState } from 'react'; +import PropTypes from 'prop-types'; +import { Stack, PrimaryButton, Pivot, PivotItem, DefaultButton } from '@fluentui/react'; +import * as copy from 'copy-to-clipboard'; +import JSONTree from 'react-json-tree'; +import { Trial } from '@model/trial'; +import { MANAGER_IP, RETIARIIPARAMETERS } from '@static/const'; +import { EXPERIMENT, TRIALS } from '@static/datamodel'; +import { reformatRetiariiParameter } from '@static/function'; +import PaiTrialLog from './PaiTrialLog'; +import TrialLog from './TrialLog'; +import MessageInfo from '../MessageInfo'; +import PanelMonacoEditor from '../PanelMonacoEditor'; +import '@style/experiment/overview/overview.scss'; + +/** + * netron URL must be synchronized with ts/nni_manager/rest_server/index.ts`. + * Remember to update it if the value is changed or this file is moved. + **/ + +const OpenRow = (props): any => { + const [typeInfo, setTypeInfo] = useState(''); + const [info, setInfo] = useState(''); + const [isHidenInfo, setHideninfo] = useState(true); + const [showRetiaParamPanel, setShowRetiaparamPanel] = useState(false); + const trialId = props.trialId; + const trial = TRIALS.getTrial(trialId); + const logPathRow = trial.info.logPath || "This trial's log path is not available."; + const originParameters = trial.description.parameters; + const hasVisualHyperParams = RETIARIIPARAMETERS in originParameters; + + const hideMessageInfo = (): void => { + setHideninfo(true); + }; + + const hideRetiaParam = (): void => { + setShowRetiaparamPanel(false); + }; + + const isshowRetiaParamPanel = (): void => { + setShowRetiaparamPanel(true); + }; + + /** + * info: message content + * typeInfo: message type: success | error... + * continuousTime: show time, 2000ms + */ + const getCopyStatus = (info: string, typeInfo: string): void => { + setTypeInfo(typeInfo); + setInfo(info); + setHideninfo(false); + setTimeout(hideMessageInfo, 2000); + }; + + const copyParams = (trial: Trial): void => { + // get copy parameters + const params = JSON.stringify(reformatRetiariiParameter(trial.description.parameters as any), null, 4); + if (copy.default(params)) { + getCopyStatus('Successfully copy parameters to clipboard in form of python dict !', 'success'); + } else { + getCopyStatus('Failed !', 'error'); + } + }; + + const openTrialLog = (filename: string): void => { + window.open(`${MANAGER_IP}/trial-file/${props.trialId}/${filename}`); + }; + + const openModelOnnx = (): void => { + // TODO: netron might need prefix. + window.open(`/netron/index.html?url=${MANAGER_IP}/trial-file/${props.trialId}/model.onnx`); + }; + + return ( + + + + + {trial.info.hyperParameters !== undefined ? ( + + + true} // default expandNode + getItemString={() => null} // remove the {} items + data={reformatRetiariiParameter(originParameters as any)} + /> + + + + {hasVisualHyperParams && ( + + )} + {/* copy success | failed message info */} + {!isHidenInfo && } + {showRetiaParamPanel && ( + + )} + + + ) : ( + + Error: + {`This trial's parameters are not available.'`} + + )} + + + { + // FIXME: this should not be handled in web UI side + EXPERIMENT.trainingServicePlatform !== 'local' ? ( + + ) : ( +
+ + {/* view trial log */} +
+ + + +
+
+ ) + } +
+ {EXPERIMENT.metadata.tag.includes('retiarii') ? ( + +
+
Visualize models with 3rd-party tools.
+ +
+
+ ) : null} +
+
+
+ ); +}; + +OpenRow.propTypes = { + trialId: PropTypes.string +}; + +export default OpenRow; diff --git a/ts/webui/src/components/common/ExpandableDetails/PaiTrialLog.tsx b/ts/webui/src/components/common/ExpandableDetails/PaiTrialLog.tsx new file mode 100644 index 000000000..6b7131f19 --- /dev/null +++ b/ts/webui/src/components/common/ExpandableDetails/PaiTrialLog.tsx @@ -0,0 +1,26 @@ +import * as React from 'react'; +import PropTypes from 'prop-types'; +import TrialLog from './TrialLog'; + +const PaitrialLog = (props): any => { + const { logStr } = props; + const isHasNFSLog = logStr.indexOf(',') !== -1 ? true : false; + return ( +
+ {isHasNFSLog ? ( +
+ + +
+ ) : ( + + )} +
+ ); +}; + +PaitrialLog.propTypes = { + logStr: PropTypes.string +}; + +export default PaitrialLog; diff --git a/ts/webui/src/components/common/ExpandableDetails/TrialLog.tsx b/ts/webui/src/components/common/ExpandableDetails/TrialLog.tsx new file mode 100644 index 000000000..9a2bc8b54 --- /dev/null +++ b/ts/webui/src/components/common/ExpandableDetails/TrialLog.tsx @@ -0,0 +1,27 @@ +import * as React from 'react'; +import PropTypes from 'prop-types'; + +const TrialLog = (props): any => { + const { logStr, logName } = props; + const isHyperlink = logStr.toLowerCase().startsWith('http'); + + return ( +
+ {logName} + {isHyperlink ? ( + + {logStr} + + ) : ( + {logStr} + )} +
+ ); +}; + +TrialLog.propTypes = { + logStr: PropTypes.string, + logName: PropTypes.string +}; + +export default TrialLog; diff --git a/ts/webui/src/components/common/LogPathChild.tsx b/ts/webui/src/components/common/LogPathChild.tsx deleted file mode 100644 index e20b4a20f..000000000 --- a/ts/webui/src/components/common/LogPathChild.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import * as React from 'react'; - -interface LogpathChildProps { - eachLogpath: string; - logName: string; -} - -class LogPathChild extends React.Component { - constructor(props: LogpathChildProps) { - super(props); - } - - render(): React.ReactNode { - const { eachLogpath, logName } = this.props; - const isLink = /^http/gi.test(eachLogpath); - - return ( -
- {logName} - {isLink ? ( - - {eachLogpath} - - ) : ( - {eachLogpath} - )} -
- ); - } -} - -export default LogPathChild; diff --git a/ts/webui/src/components/common/OpenRow.tsx b/ts/webui/src/components/common/OpenRow.tsx deleted file mode 100644 index 7b89db378..000000000 --- a/ts/webui/src/components/common/OpenRow.tsx +++ /dev/null @@ -1,190 +0,0 @@ -import * as React from 'react'; -import * as copy from 'copy-to-clipboard'; -import { Stack, PrimaryButton, Pivot, PivotItem, DefaultButton } from '@fluentui/react'; -import JSONTree from 'react-json-tree'; -import { Trial } from '@model/trial'; -import { MANAGER_IP, RETIARIIPARAMETERS } from '@static/const'; -import { EXPERIMENT, TRIALS } from '@static/datamodel'; -import { reformatRetiariiParameter } from '@static/function'; -import PaiTrialLog from './PaiTrialLog'; -import TrialLog from './TrialLog'; -import MessageInfo from './MessageInfo'; -import PanelMonacoEditor from './PanelMonacoEditor'; -import '@style/experiment/overview/overview.scss'; -import '@style/openRow.scss'; - -/** - * netron URL must be synchronized with ts/nni_manager/rest_server/index.ts`. - * Remember to update it if the value is changed or this file is moved. - **/ - -interface OpenRowProps { - trialId: string; -} - -interface OpenRowState { - typeInfo: string; - info: string; - isHidenInfo: boolean; - showRetiaParamPanel: boolean; -} - -class OpenRow extends React.Component { - constructor(props: OpenRowProps) { - super(props); - this.state = { - typeInfo: '', - info: '', - isHidenInfo: true, - showRetiaParamPanel: false - }; - } - - hideMessageInfo = (): void => { - this.setState(() => ({ isHidenInfo: true })); - }; - - hideRetiaParam = (): void => { - this.setState(() => ({ showRetiaParamPanel: false })); - }; - - isshowRetiaParamPanel = (): void => { - this.setState(() => ({ showRetiaParamPanel: true })); - }; - - /** - * info: message content - * typeInfo: message type: success | error... - * continuousTime: show time, 2000ms - */ - getCopyStatus = (info: string, typeInfo: string): void => { - this.setState(() => ({ info, typeInfo, isHidenInfo: false })); - setTimeout(this.hideMessageInfo, 2000); - }; - - copyParams = (trial: Trial): void => { - // get copy parameters - const params = JSON.stringify(reformatRetiariiParameter(trial.description.parameters as any), null, 4); - if (copy.default(params)) { - this.getCopyStatus('Success copy parameters to clipboard in form of python dict !', 'success'); - } else { - this.getCopyStatus('Failed !', 'error'); - } - }; - - openTrialLog = (filename: string): void => { - window.open(`${MANAGER_IP}/trial-file/${this.props.trialId}/${filename}`); - }; - - openModelOnnx = (): void => { - // TODO: netron might need prefix. - window.open(`/netron/index.html?url=${MANAGER_IP}/trial-file/${this.props.trialId}/model.onnx`); - }; - - render(): React.ReactNode { - const { isHidenInfo, typeInfo, info, showRetiaParamPanel } = this.state; - const trialId = this.props.trialId; - const trial = TRIALS.getTrial(trialId); - const logPathRow = trial.info.logPath || "This trial's log path is not available."; - const originParameters = trial.description.parameters; - const hasVisualHyperParams = RETIARIIPARAMETERS in originParameters; - return ( - - - - - {trial.info.hyperParameters !== undefined ? ( - - - true} // default expandNode - getItemString={(): null => null} // remove the {} items - data={reformatRetiariiParameter(originParameters as any)} - /> - - - - {hasVisualHyperParams && ( - - )} - {/* copy success | failed message info */} - {!isHidenInfo && } - {showRetiaParamPanel && ( - - )} - - - ) : ( - - Error: - {`This trial's parameters are not available.'`} - - )} - - - { - // FIXME: this should not be handled in web UI side - EXPERIMENT.trainingServicePlatform !== 'local' ? ( - - ) : ( -
- - {/* view each trial log in drawer*/} -
-
- - - -
-
-
- ) - } -
- {EXPERIMENT.metadata.tag.includes('retiarii') ? ( - -
-
Visualize models with 3rd-party tools.
- -
-
- ) : null} -
-
-
- ); - } -} - -export default OpenRow; diff --git a/ts/webui/src/components/common/PaiTrialChild.tsx b/ts/webui/src/components/common/PaiTrialChild.tsx deleted file mode 100644 index 254e5c778..000000000 --- a/ts/webui/src/components/common/PaiTrialChild.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import * as React from 'react'; -import { DOWNLOAD_IP } from '@static/const'; -import LogPathChild from './LogPathChild'; - -interface PaiTrialChildProps { - logString: string; - id: string; - logCollect: boolean; -} - -class PaiTrialChild extends React.Component { - constructor(props: PaiTrialChildProps) { - super(props); - } - - render(): React.ReactNode { - const { logString, id, logCollect } = this.props; - return ( -
- {logString === '' ? null : ( -
- {logCollect ? ( - - Trial stdout - - ) : ( - - )} -
- )} -
- ); - } -} - -export default PaiTrialChild; diff --git a/ts/webui/src/components/common/PaiTrialLog.tsx b/ts/webui/src/components/common/PaiTrialLog.tsx deleted file mode 100644 index d3ccef1ff..000000000 --- a/ts/webui/src/components/common/PaiTrialLog.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import * as React from 'react'; -import { DOWNLOAD_IP } from '@static/const'; -import PaiTrialChild from './PaiTrialChild'; -import LogPathChild from './LogPathChild'; - -interface PaitrialLogProps { - logStr: string; - id: string; - logCollection: boolean; -} - -class PaitrialLog extends React.Component { - constructor(props: PaitrialLogProps) { - super(props); - } - - render(): React.ReactNode { - const { logStr, id, logCollection } = this.props; - const isTwopath = logStr.indexOf(',') !== -1 ? true : false; - return ( -
-
- {isTwopath ? ( -
- {logCollection ? ( - - ) : ( -
- - -
- )} -
- ) : ( - - )} -
-
- ); - } -} - -export default PaitrialLog; diff --git a/ts/webui/src/components/common/TrialLog.tsx b/ts/webui/src/components/common/TrialLog.tsx deleted file mode 100644 index 126ab8711..000000000 --- a/ts/webui/src/components/common/TrialLog.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import * as React from 'react'; -import LogPathChild from './LogPathChild'; - -interface TrialLogProps { - logStr: string; - id: string; -} - -class TrialLog extends React.Component { - constructor(props: TrialLogProps) { - super(props); - } - - render(): React.ReactNode { - const { logStr } = this.props; - - return ( -
- -
- ); - } -} - -export default TrialLog; diff --git a/ts/webui/src/components/experiment/overview/Overview.tsx b/ts/webui/src/components/experiment/overview/Overview.tsx index 49d37ade9..d93de9727 100644 --- a/ts/webui/src/components/experiment/overview/Overview.tsx +++ b/ts/webui/src/components/experiment/overview/Overview.tsx @@ -16,7 +16,7 @@ import { TitleContext } from './TitleContext'; import { itemStyleSucceed, entriesOption } from './overviewConst'; import '@style/experiment/overview/overview.scss'; import '@style/experiment/overview/topTrial.scss'; -import '@style/logPath.scss'; +import '@style/table.scss'; /** * single experiment diff --git a/ts/webui/src/components/experiment/overview/table/SuccessTable.tsx b/ts/webui/src/components/experiment/overview/table/SuccessTable.tsx index 64a445714..799cac8c8 100644 --- a/ts/webui/src/components/experiment/overview/table/SuccessTable.tsx +++ b/ts/webui/src/components/experiment/overview/table/SuccessTable.tsx @@ -14,7 +14,7 @@ import { ScrollbarVisibility } from '@fluentui/react'; import DefaultMetric from './DefaultMetric'; -import OpenRow from '@components/common/OpenRow'; +import OpenRow from '@/components/common/ExpandableDetails/OpenRow'; import CopyButton from '@components/common/CopyButton'; import { convertDuration, copyAndSort } from '@static/function'; import { TRIALS } from '@static/datamodel'; diff --git a/ts/webui/src/components/experiment/trialdetail/TrialsDetail.tsx b/ts/webui/src/components/experiment/trialdetail/TrialsDetail.tsx index 54628d627..94c5ee0fd 100644 --- a/ts/webui/src/components/experiment/trialdetail/TrialsDetail.tsx +++ b/ts/webui/src/components/experiment/trialdetail/TrialsDetail.tsx @@ -8,7 +8,6 @@ import Para from './chart/Para'; import Intermediate from './chart/Intermediate'; import TableList from './table/TableList'; import '@style/button.scss'; -import '@style/logPath.scss'; import '@style/openRow.scss'; import '@style/pagination.scss'; import '@style/experiment/overview/overviewTitle.scss'; diff --git a/ts/webui/src/components/experiment/trialdetail/table/TableList.tsx b/ts/webui/src/components/experiment/trialdetail/table/TableList.tsx index ba8dbe5d0..724a32149 100644 --- a/ts/webui/src/components/experiment/trialdetail/table/TableList.tsx +++ b/ts/webui/src/components/experiment/trialdetail/table/TableList.tsx @@ -23,14 +23,14 @@ import { } from '@static/function'; import { TableObj, SortInfo, SearchItems } from '@static/interface'; import { blocked, copy, LineChart, tableListIcon } from '@components/fluent/Icon'; -import Search from './tableFunction/search/Search'; import Customize from './tableFunction/CustomizedTrial'; import TensorboardUI from './tableFunction/tensorboard/TensorboardUI'; +import Search from './tableFunction/search/Search'; +import ExpandableDetails from '@components/common/ExpandableDetails/ExpandableIndex'; import ChangeColumnComponent from '../ChangeColumnComponent'; import Compare from './tableFunction/Compare'; import KillJobIndex from './tableFunction/killJob/KillJobIndex'; import { getTrialsBySearchFilters } from './tableFunction/search/searchFunction'; -import ExpandableDetails from '@components/common/ExpandableDetails'; import PaginationTable from '@components/common/PaginationTable'; import CopyButton from '@components/common/CopyButton'; diff --git a/ts/webui/src/static/interface.ts b/ts/webui/src/static/interface.ts index 86c8d7a8a..e6c6f5d5d 100644 --- a/ts/webui/src/static/interface.ts +++ b/ts/webui/src/static/interface.ts @@ -147,13 +147,8 @@ interface TrialJobInfo { hyperParameters?: string[]; logPath?: string; finalMetricData?: MetricDataRecord[]; - stderrPath?: string; } -//interface ClusterItem { -// command?: string; -//} - interface ExperimentProfile { params: ExperimentConfig; id: string; diff --git a/ts/webui/src/static/model/experiment.ts b/ts/webui/src/static/model/experiment.ts index 0bf940c6b..9b5f5ae52 100644 --- a/ts/webui/src/static/model/experiment.ts +++ b/ts/webui/src/static/model/experiment.ts @@ -182,10 +182,6 @@ class Experiment { return new SearchSpace('', '', this.searchSpace); } - get logCollectionEnabled(): boolean { - return false; - } - get status(): string { if (!this.statusField) { // throw Error('Experiment status not initialized'); diff --git a/ts/webui/src/static/style/common/common.scss b/ts/webui/src/static/style/common/common.scss index 9c2c4ad40..67f4cf250 100644 --- a/ts/webui/src/static/style/common/common.scss +++ b/ts/webui/src/static/style/common/common.scss @@ -7,17 +7,18 @@ $themeBlue: #0071bc; } .link { + color: $themeBlue; outline: none; text-decoration: none; &:hover { - color: #0071bc !important; + color: $themeBlue; text-decoration: underline; } &:active, &:visited { - color: #0071bc; + color: $themeBlue; } } diff --git a/ts/webui/src/static/style/logPath.scss b/ts/webui/src/static/style/logPath.scss deleted file mode 100644 index a217094e0..000000000 --- a/ts/webui/src/static/style/logPath.scss +++ /dev/null @@ -1,18 +0,0 @@ -.logpath { - margin-left: 10px; - font-size: 14px; - - .logName { - color: #268bd2; - margin-right: 5px; - } - - .error { - color: #cb4b16; - } -} - -.logHref:hover { - color: blue; - text-decoration: underline; -} diff --git a/ts/webui/src/static/style/openRow.scss b/ts/webui/src/static/style/openRow.scss index c0363df17..551ea21da 100644 --- a/ts/webui/src/static/style/openRow.scss +++ b/ts/webui/src/static/style/openRow.scss @@ -17,18 +17,7 @@ $bgColor: #f2f2f2; } } -.trialLog { - white-space: normal; - color: #212121; -} - -#trialLogContent { - .logcontent { - height: 100%; - } -} - -#description { +.description { padding: 0 10px; .bgHyper ul { diff --git a/ts/webui/src/static/style/table.scss b/ts/webui/src/static/style/table.scss index d5fb29527..f2fa5c8aa 100644 --- a/ts/webui/src/static/style/table.scss +++ b/ts/webui/src/static/style/table.scss @@ -1,7 +1,7 @@ /* react-json-tree background */ -#description ul, +.description ul, #allList ul { - background: none !important; + background: none; } .tabScroll { @@ -46,11 +46,6 @@ } } -/* office-fabric-ui */ -.ms-DetailsRow { - height: 30px; -} - .detail-table { padding-top: 5px; } @@ -78,3 +73,19 @@ $checkboxwidth: 17px; } } } + +/* trial table: open row style */ +.logpath { + margin: 10px 0 10px 10px; + font-size: 14px; + + .logName { + color: #268bd2; + margin-right: 5px; + } + + .error { + color: #cb4b16; + } +} +