|
|
|
@@ -2,8 +2,10 @@ import KFIcon from '@/components/KFIcon'; |
|
|
|
import PageTitle from '@/components/PageTitle'; |
|
|
|
import { ExperimentStatus, TensorBoardStatus } from '@/enums'; |
|
|
|
import { useCacheState } from '@/hooks/useCacheState'; |
|
|
|
import { useServerTime } from '@/hooks/useServerTime'; |
|
|
|
import { |
|
|
|
deleteExperimentById, |
|
|
|
editExperimentInsReq, |
|
|
|
getExperiment, |
|
|
|
getExperimentById, |
|
|
|
getQueryByExperimentId, |
|
|
|
@@ -17,6 +19,7 @@ import { getWorkflow } from '@/services/pipeline/index.js'; |
|
|
|
import themes from '@/styles/theme.less'; |
|
|
|
import { ExperimentCompleted } from '@/utils/constant'; |
|
|
|
import { to } from '@/utils/promise'; |
|
|
|
import SessionStorage from '@/utils/sessionStorage'; |
|
|
|
import tableCellRender, { TableCellValueType } from '@/utils/table'; |
|
|
|
import { modalConfirm } from '@/utils/ui'; |
|
|
|
import { App, Button, ConfigProvider, Dropdown, Input, Space, Table, Tooltip } from 'antd'; |
|
|
|
@@ -28,7 +31,6 @@ import AddExperimentModal from './components/AddExperimentModal'; |
|
|
|
import ExperimentInstanceList from './components/ExperimentInstanceList'; |
|
|
|
import styles from './index.less'; |
|
|
|
import { experimentStatusInfo } from './status'; |
|
|
|
import { useServerTime } from '@/hooks/useServerTime'; |
|
|
|
|
|
|
|
// 定时器 |
|
|
|
const timerIds = new Map(); |
|
|
|
@@ -39,7 +41,7 @@ function Experiment() { |
|
|
|
const [workflowList, setWorkflowList] = useState([]); |
|
|
|
const [experimentId, setExperimentId] = useState(null); |
|
|
|
const [experimentInsList, setExperimentInsList] = useState([]); |
|
|
|
const [expandedRowKeys, setExpandedRowKeys] = useState(null); |
|
|
|
const [expandedRowKeys, setExpandedRowKeys] = useState([]); |
|
|
|
const [total, setTotal] = useState(0); |
|
|
|
const [isAdd, setIsAdd] = useState(true); |
|
|
|
const [isModalOpen, setIsModalOpen] = useState(false); |
|
|
|
@@ -59,29 +61,137 @@ function Experiment() { |
|
|
|
const timerRef = useRef(); |
|
|
|
|
|
|
|
// 获取实验列表 |
|
|
|
const getExperimentList = useCallback(async () => { |
|
|
|
const getExperimentList = useCallback( |
|
|
|
async (skipLoading = false) => { |
|
|
|
const params = { |
|
|
|
page: pagination.current - 1, |
|
|
|
size: pagination.pageSize, |
|
|
|
name: searchText || undefined, |
|
|
|
}; |
|
|
|
const [res] = await to(getExperiment(params, skipLoading)); |
|
|
|
if (res && res.data && Array.isArray(res.data.content)) { |
|
|
|
setExperimentList( |
|
|
|
res.data.content.map((item) => { |
|
|
|
return { ...item, key: item.id }; |
|
|
|
}), |
|
|
|
); |
|
|
|
|
|
|
|
setTotal(res.data.totalElements); |
|
|
|
} |
|
|
|
}, |
|
|
|
[pagination, searchText], |
|
|
|
); |
|
|
|
|
|
|
|
// 刷新实验列表状态, |
|
|
|
// 目前是直接刷新实验列表,后续需要优化,只刷新状态 |
|
|
|
const refreshExperimentList = useCallback( |
|
|
|
(skipLoading = false) => { |
|
|
|
getExperimentList(skipLoading); |
|
|
|
}, |
|
|
|
[getExperimentList], |
|
|
|
); |
|
|
|
|
|
|
|
// 获取 TensorBoard 状态 |
|
|
|
const getTensorBoardStatus = useCallback(async (experimentIn) => { |
|
|
|
const params = { |
|
|
|
page: pagination.current - 1, |
|
|
|
size: pagination.pageSize, |
|
|
|
name: searchText || undefined, |
|
|
|
namespace: experimentIn.nodes_result.tensorboard_log.namespace, |
|
|
|
path: experimentIn.nodes_result.tensorboard_log.path, |
|
|
|
pvc_name: experimentIn.nodes_result.tensorboard_log.pvc_name, |
|
|
|
}; |
|
|
|
const [res] = await to(getExperiment(params)); |
|
|
|
if (res && res.data && Array.isArray(res.data.content)) { |
|
|
|
setExperimentList( |
|
|
|
res.data.content.map((item) => { |
|
|
|
return { ...item, key: item.id }; |
|
|
|
}), |
|
|
|
); |
|
|
|
|
|
|
|
setTotal(res.data.totalElements); |
|
|
|
const [res] = await to(getTensorBoardStatusReq(params)); |
|
|
|
if (res && res.data) { |
|
|
|
setExperimentInsList((prevList) => { |
|
|
|
return prevList.map((item) => { |
|
|
|
if (item.id === experimentIn.id) { |
|
|
|
return { |
|
|
|
...item, |
|
|
|
tensorBoardStatus: res.data.status, |
|
|
|
tensorboardUrl: res.data.url, |
|
|
|
}; |
|
|
|
} |
|
|
|
return item; |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
let timerId = timerIds.get(experimentIn.id); |
|
|
|
if (timerId) { |
|
|
|
clearTimeout(timerId); |
|
|
|
timerIds.delete(experimentIn.id); |
|
|
|
} |
|
|
|
timerId = setTimeout(() => { |
|
|
|
getTensorBoardStatus(experimentIn); |
|
|
|
}, 10 * 1000); |
|
|
|
timerIds.set(experimentIn.id, timerId); |
|
|
|
} |
|
|
|
}, [pagination, searchText]); |
|
|
|
}, []); |
|
|
|
|
|
|
|
// 刷新实验列表状态, |
|
|
|
// 目前是直接刷新实验列表,后续需要优化,只刷新状态 |
|
|
|
const refreshExperimentList = useCallback(() => { |
|
|
|
getExperimentList(); |
|
|
|
}, [getExperimentList]); |
|
|
|
// 获取实验实例列表 |
|
|
|
const getExperimentInsList = useCallback( |
|
|
|
async (experimentId, page, size = 5, skipLoading = false) => { |
|
|
|
const params = { |
|
|
|
experimentId: experimentId, |
|
|
|
page: page, |
|
|
|
size: size, |
|
|
|
}; |
|
|
|
const [res, error] = await to(getQueryByExperimentId(params, skipLoading)); |
|
|
|
if (res && res.data) { |
|
|
|
const { content = [], totalElements = 0 } = res.data; |
|
|
|
try { |
|
|
|
const list = content.map((v) => { |
|
|
|
const nodes_result = v.nodes_result ? JSON.parse(v.nodes_result) : {}; |
|
|
|
return { |
|
|
|
...v, |
|
|
|
nodes_result, |
|
|
|
}; |
|
|
|
}); |
|
|
|
if (page === 0) { |
|
|
|
setExperimentInsList(list); |
|
|
|
clearExperimentInTimers(); |
|
|
|
} else { |
|
|
|
setExperimentInsList((prev) => [...prev, ...list]); |
|
|
|
} |
|
|
|
setExperimentInsTotal(totalElements); |
|
|
|
// 获取 TensorBoard 状态 |
|
|
|
list.forEach((item) => { |
|
|
|
if (item.nodes_result?.tensorboard_log) { |
|
|
|
getTensorBoardStatus(item); |
|
|
|
} |
|
|
|
}); |
|
|
|
} catch (error) { |
|
|
|
console.error('JSON parse error: ', error); |
|
|
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
[getTensorBoardStatus], |
|
|
|
); |
|
|
|
|
|
|
|
// 刷新实验实例列表 |
|
|
|
const refreshExperimentIns = useCallback( |
|
|
|
(experimentId, skipLoading = false) => { |
|
|
|
const length = experimentInsList.length; |
|
|
|
getExperimentInsList(experimentId, 0, length, skipLoading); |
|
|
|
}, |
|
|
|
[experimentInsList, getExperimentInsList], |
|
|
|
); |
|
|
|
|
|
|
|
// 更新实验状态 |
|
|
|
const editExperimentIns = useCallback( |
|
|
|
async (experimentId, experimentInsId, status, argo_ins_name, argo_ins_ns) => { |
|
|
|
const params = { |
|
|
|
experiment_id: experimentId, |
|
|
|
id: experimentInsId, |
|
|
|
status: status, |
|
|
|
argo_ins_name, |
|
|
|
argo_ins_ns, |
|
|
|
}; |
|
|
|
const [res, error] = await to(editExperimentInsReq(params)); |
|
|
|
if (res && res.data) { |
|
|
|
refreshExperimentIns(experimentId, true); |
|
|
|
refreshExperimentList(true); |
|
|
|
} |
|
|
|
}, |
|
|
|
[refreshExperimentIns, refreshExperimentList], |
|
|
|
); |
|
|
|
|
|
|
|
// 获取流水线列表 |
|
|
|
useEffect(() => { |
|
|
|
@@ -104,52 +214,66 @@ function Experiment() { |
|
|
|
clearExperimentInTimers(); |
|
|
|
}; |
|
|
|
}, []); |
|
|
|
|
|
|
|
// 获取实验列表 |
|
|
|
useEffect(() => { |
|
|
|
getExperimentList(); |
|
|
|
}, [getExperimentList]); |
|
|
|
|
|
|
|
// 新增,删除版本时,重置分页,然后刷新版本列表 |
|
|
|
// 更新实验实例状态 |
|
|
|
useEffect(() => { |
|
|
|
const handleMessage = (e) => { |
|
|
|
const { type, payload } = e.data; |
|
|
|
if (type === ExperimentCompleted) { |
|
|
|
const { id, status, finish_time } = payload; |
|
|
|
|
|
|
|
// 修改实例的状态和结束时间 |
|
|
|
setExperimentInsList((prev) => |
|
|
|
prev.map((v) => |
|
|
|
v.id === id |
|
|
|
? { |
|
|
|
...v, |
|
|
|
status: status, |
|
|
|
finish_time: finish_time, |
|
|
|
} |
|
|
|
: v, |
|
|
|
), |
|
|
|
const { experimentId, experimentInsId, status, finishTime } = payload; |
|
|
|
const currentIns = experimentInsList.find((v) => v.id === experimentInsId); |
|
|
|
console.log( |
|
|
|
'实验实例状态变化', |
|
|
|
currentIns?.status, |
|
|
|
status, |
|
|
|
experimentId, |
|
|
|
experimentInsId, |
|
|
|
finishTime, |
|
|
|
); |
|
|
|
|
|
|
|
if (timerRef.current) { |
|
|
|
clearTimeout(timerRef.current); |
|
|
|
timerRef.current = undefined; |
|
|
|
if ( |
|
|
|
!currentIns || |
|
|
|
currentIns.status === ExperimentStatus.Terminated || |
|
|
|
currentIns.status === status |
|
|
|
) { |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
timerRef.current = setTimeout(() => { |
|
|
|
refreshExperimentList(); |
|
|
|
}, 10000); |
|
|
|
editExperimentIns( |
|
|
|
experimentId, |
|
|
|
experimentInsId, |
|
|
|
status, |
|
|
|
currentIns.argo_ins_name, |
|
|
|
currentIns.argo_ins_ns, |
|
|
|
); |
|
|
|
|
|
|
|
// refreshExperimentList(true); |
|
|
|
// refreshExperimentIns(experimentId); |
|
|
|
|
|
|
|
// 修改实例的状态和结束时间 |
|
|
|
// setExperimentInsList((prev) => |
|
|
|
// prev.map((v) => |
|
|
|
// v.id === experimentInsId |
|
|
|
// ? { |
|
|
|
// ...v, |
|
|
|
// status: status, |
|
|
|
// finish_time: finishTime, |
|
|
|
// } |
|
|
|
// : v, |
|
|
|
// ), |
|
|
|
// ); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
window.addEventListener('message', handleMessage); |
|
|
|
return () => { |
|
|
|
window.removeEventListener('message', handleMessage); |
|
|
|
if (timerRef.current) { |
|
|
|
clearTimeout(timerRef.current); |
|
|
|
timerRef.current = undefined; |
|
|
|
} |
|
|
|
}; |
|
|
|
}, [refreshExperimentList]); |
|
|
|
}, [experimentInsList, editExperimentIns]); |
|
|
|
|
|
|
|
// 搜索 |
|
|
|
const onSearch = (value) => { |
|
|
|
@@ -160,44 +284,6 @@ function Experiment() { |
|
|
|
})); |
|
|
|
}; |
|
|
|
|
|
|
|
// 获取实验实例列表 |
|
|
|
const getQueryByExperiment = async (experimentId, page, size = 5) => { |
|
|
|
const params = { |
|
|
|
experimentId: experimentId, |
|
|
|
page: page, |
|
|
|
size: size, |
|
|
|
}; |
|
|
|
const [res, error] = await to(getQueryByExperimentId(params)); |
|
|
|
if (res && res.data) { |
|
|
|
const { content = [], totalElements = 0 } = res.data; |
|
|
|
setExpandedRowKeys(experimentId); |
|
|
|
try { |
|
|
|
const list = content.map((v) => { |
|
|
|
const nodes_result = v.nodes_result ? JSON.parse(v.nodes_result) : {}; |
|
|
|
return { |
|
|
|
...v, |
|
|
|
nodes_result, |
|
|
|
}; |
|
|
|
}); |
|
|
|
if (page === 0) { |
|
|
|
setExperimentInsList(list); |
|
|
|
clearExperimentInTimers(); |
|
|
|
} else { |
|
|
|
setExperimentInsList((prev) => [...prev, ...list]); |
|
|
|
} |
|
|
|
setExperimentInsTotal(totalElements); |
|
|
|
// 获取 TensorBoard 状态 |
|
|
|
list.forEach((item) => { |
|
|
|
if (item.nodes_result?.tensorboard_log) { |
|
|
|
getTensorBoardStatus(item); |
|
|
|
} |
|
|
|
}); |
|
|
|
} catch (error) { |
|
|
|
console.error('JSON parse error: ', error); |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
// 运行 TensorBoard |
|
|
|
const runTensorBoard = async (experimentIn) => { |
|
|
|
const params = { |
|
|
|
@@ -217,49 +303,16 @@ function Experiment() { |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
// 获取 TensorBoard 状态 |
|
|
|
const getTensorBoardStatus = async (experimentIn) => { |
|
|
|
const params = { |
|
|
|
namespace: experimentIn.nodes_result.tensorboard_log.namespace, |
|
|
|
path: experimentIn.nodes_result.tensorboard_log.path, |
|
|
|
pvc_name: experimentIn.nodes_result.tensorboard_log.pvc_name, |
|
|
|
}; |
|
|
|
const [res] = await to(getTensorBoardStatusReq(params)); |
|
|
|
if (res && res.data) { |
|
|
|
setExperimentInsList((prevList) => { |
|
|
|
return prevList.map((item) => { |
|
|
|
if (item.id === experimentIn.id) { |
|
|
|
return { |
|
|
|
...item, |
|
|
|
tensorBoardStatus: res.data.status, |
|
|
|
tensorboardUrl: res.data.url, |
|
|
|
}; |
|
|
|
} |
|
|
|
return item; |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
let timerId = timerIds.get(experimentIn.id); |
|
|
|
if (timerId) { |
|
|
|
clearTimeout(timerId); |
|
|
|
timerIds.delete(experimentIn.id); |
|
|
|
} |
|
|
|
timerId = setTimeout(() => { |
|
|
|
getTensorBoardStatus(experimentIn); |
|
|
|
}, 10 * 1000); |
|
|
|
timerIds.set(experimentIn.id, timerId); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
// 展开实例 |
|
|
|
const expandChange = (e, record) => { |
|
|
|
const expandChange = (expanded, record) => { |
|
|
|
clearExperimentInTimers(); |
|
|
|
setExperimentInsList([]); |
|
|
|
if (record.id === expandedRowKeys) { |
|
|
|
setExpandedRowKeys(null); |
|
|
|
} else { |
|
|
|
getQueryByExperiment(record.id, 0, 5); |
|
|
|
if (expanded) { |
|
|
|
setExpandedRowKeys([record.id]); |
|
|
|
getExperimentInsList(record.id, 0, 5); |
|
|
|
refreshExperimentList(); |
|
|
|
} else { |
|
|
|
setExpandedRowKeys([]); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
@@ -337,8 +390,9 @@ function Experiment() { |
|
|
|
const [res] = await to(runExperiments(id)); |
|
|
|
if (res) { |
|
|
|
message.success('运行成功'); |
|
|
|
setExpandedRowKeys([id]); |
|
|
|
refreshExperimentList(); |
|
|
|
getQueryByExperiment(id, 0, 5); |
|
|
|
getExperimentInsList(id, 0, 5); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
@@ -372,14 +426,16 @@ function Experiment() { |
|
|
|
experimentIn.tensorBoardStatus === TensorBoardStatus.Running && |
|
|
|
experimentIn.tensorboardUrl |
|
|
|
) { |
|
|
|
window.open(experimentIn.tensorboardUrl, '_blank'); |
|
|
|
const url = experimentIn.tensorboardUrl; |
|
|
|
SessionStorage.setItem(SessionStorage.tensorBoardUrlKey, url); |
|
|
|
navigateToUrl(`/pipeline/experiment/visual`); |
|
|
|
// window.open(experimentIn.tensorboardUrl, '_blank'); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
// 实验实例终止 |
|
|
|
const handleInstanceTerminate = async (experimentIn) => { |
|
|
|
// 刷新实验列表 |
|
|
|
refreshExperimentList(); |
|
|
|
// 修改实例的状态和结束时间 |
|
|
|
setExperimentInsList((prevList) => { |
|
|
|
return prevList.map((item) => { |
|
|
|
if (item.id === experimentIn.id) { |
|
|
|
@@ -392,6 +448,9 @@ function Experiment() { |
|
|
|
return item; |
|
|
|
}); |
|
|
|
}); |
|
|
|
// 刷新实验列表和实例列表 |
|
|
|
refreshExperimentList(true); |
|
|
|
refreshExperimentIns(experimentIn.experiment_id); |
|
|
|
}; |
|
|
|
|
|
|
|
// 实验对比菜单 |
|
|
|
@@ -413,16 +472,10 @@ function Experiment() { |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
// 刷新实验实例列表 |
|
|
|
const refreshExperimentIns = (experimentId) => { |
|
|
|
const length = experimentInsList.length; |
|
|
|
getQueryByExperiment(experimentId, 0, length); |
|
|
|
}; |
|
|
|
|
|
|
|
// 加载更多实验实例 |
|
|
|
const loadMoreExperimentIns = () => { |
|
|
|
const page = Math.round(experimentInsList.length / 5); |
|
|
|
getQueryByExperiment(expandedRowKeys, page, 5); |
|
|
|
getExperimentInsList(expandedRowKeys[0], page, 5); |
|
|
|
}; |
|
|
|
|
|
|
|
// 处理删除 |
|
|
|
@@ -607,7 +660,7 @@ function Experiment() { |
|
|
|
></ExperimentInstanceList> |
|
|
|
), |
|
|
|
onExpand: expandChange, |
|
|
|
expandedRowKeys: [expandedRowKeys], |
|
|
|
expandedRowKeys: expandedRowKeys, |
|
|
|
}} |
|
|
|
/> |
|
|
|
</div> |
|
|
|
|