| @@ -11,6 +11,7 @@ import { getAccessToken } from './access'; | |||||
| import ErrorBoundary from './components/ErrorBoundary'; | import ErrorBoundary from './components/ErrorBoundary'; | ||||
| import './dayjsConfig'; | import './dayjsConfig'; | ||||
| import { removeAllPageCacheState } from './hooks/useCacheState'; | import { removeAllPageCacheState } from './hooks/useCacheState'; | ||||
| import { globalGetSeverTime } from './hooks/useServerTime'; | |||||
| import { | import { | ||||
| getRemoteMenu, | getRemoteMenu, | ||||
| getRoutersInfo, | getRoutersInfo, | ||||
| @@ -29,6 +30,7 @@ export { requestConfig as request } from './requestConfig'; | |||||
| export async function getInitialState(): Promise<GlobalInitialState> { | export async function getInitialState(): Promise<GlobalInitialState> { | ||||
| const fetchUserInfo = async () => { | const fetchUserInfo = async () => { | ||||
| try { | try { | ||||
| globalGetSeverTime(); | |||||
| const response = await getUserInfo(); | const response = await getUserInfo(); | ||||
| return { | return { | ||||
| ...response.user, | ...response.user, | ||||
| @@ -1,4 +1,3 @@ | |||||
| import { AvailableRange } from '@/enums'; | |||||
| import { type CodeConfigData } from '@/pages/CodeConfig/List'; | import { type CodeConfigData } from '@/pages/CodeConfig/List'; | ||||
| import { Flex, Typography } from 'antd'; | import { Flex, Typography } from 'antd'; | ||||
| import classNames from 'classnames'; | import classNames from 'classnames'; | ||||
| @@ -24,12 +23,12 @@ function CodeConfigItem({ item, onClick }: CodeConfigItemProps) { | |||||
| <div | <div | ||||
| className={classNames( | className={classNames( | ||||
| styles['code-config-item__tag'], | styles['code-config-item__tag'], | ||||
| item.code_repo_vis === AvailableRange.Public | |||||
| item.is_public | |||||
| ? styles['code-config-item__tag--public'] | ? styles['code-config-item__tag--public'] | ||||
| : styles['code-config-item__tag--private'], | : styles['code-config-item__tag--private'], | ||||
| )} | )} | ||||
| > | > | ||||
| {item.code_repo_vis === AvailableRange.Public ? '公开' : '私有'} | |||||
| {item.is_public ? '公开' : '私有'} | |||||
| </div> | </div> | ||||
| </Flex> | </Flex> | ||||
| <Typography.Paragraph | <Typography.Paragraph | ||||
| @@ -0,0 +1,35 @@ | |||||
| import { useServerTime } from '@/hooks/useServerTime'; | |||||
| import { elapsedTime } from '@/utils/date'; | |||||
| import React, { useEffect, useState } from 'react'; | |||||
| type RunDurationProps = { | |||||
| createTime?: string; | |||||
| finishTime?: string; | |||||
| className?: string; | |||||
| style?: React.CSSProperties; | |||||
| }; | |||||
| function RunDuration({ createTime, finishTime, className, style }: RunDurationProps) { | |||||
| const [now] = useServerTime(); | |||||
| const [currentTime, setCurrentTime] = useState<Date>(now()); | |||||
| // 定时刷新耗时 | |||||
| useEffect(() => { | |||||
| if (finishTime) { | |||||
| setCurrentTime(new Date(finishTime)); | |||||
| } else { | |||||
| const timer = setInterval(() => { | |||||
| setCurrentTime(now()); | |||||
| }, 1000); | |||||
| return () => { | |||||
| clearInterval(timer); | |||||
| }; | |||||
| } | |||||
| }, [finishTime, now]); | |||||
| return ( | |||||
| <span className={className} style={style}> | |||||
| {elapsedTime(createTime, currentTime)} | |||||
| </span> | |||||
| ); | |||||
| } | |||||
| export default RunDuration; | |||||
| @@ -39,9 +39,9 @@ export enum TensorBoardStatus { | |||||
| // 镜像版本状态 | // 镜像版本状态 | ||||
| export enum MirrorVersionStatus { | export enum MirrorVersionStatus { | ||||
| Available = 'available', // 可用 | |||||
| Building = 'building', // 构建中 | |||||
| Failed = 'failed', // 构建中 | |||||
| Available = 'Available', // 可用 | |||||
| Building = 'Building', // 构建中 | |||||
| Failed = 'Failed', // 失败 | |||||
| } | } | ||||
| // 服务运行状态 | // 服务运行状态 | ||||
| @@ -1,11 +1,13 @@ | |||||
| import { parseJsonText } from '@/utils'; | import { parseJsonText } from '@/utils'; | ||||
| import { useCallback, useRef } from 'react'; | |||||
| import { useEffect } from 'react'; | |||||
| import { ExperimentStatus } from '@/enums'; | |||||
| import { NodeStatus } from '@/types'; | |||||
| export const useSSE = (onMessage: (data: any) => void) => { | |||||
| const evtSourceRef = useRef<EventSource | null>(null); | |||||
| const setupSSE = useCallback( | |||||
| (name: string, namespace: string) => { | |||||
| export type MessageHandler = (experimentInsId: number, status: string, finishedAt: string, nodes: Record<string, NodeStatus>) => void | |||||
| export const useSSE = (experimentInsId: number, status: ExperimentStatus, name: string, namespace: string, onMessage: MessageHandler) => { | |||||
| const isRunning = status === ExperimentStatus.Pending || status === ExperimentStatus.Running | |||||
| useEffect(() => { | |||||
| if (isRunning) { | |||||
| const { origin } = location; | const { origin } = location; | ||||
| const params = encodeURIComponent(`metadata.namespace=${namespace},metadata.name=${name}`); | const params = encodeURIComponent(`metadata.namespace=${namespace},metadata.name=${name}`); | ||||
| const evtSource = new EventSource( | const evtSource = new EventSource( | ||||
| @@ -18,11 +20,10 @@ export const useSSE = (onMessage: (data: any) => void) => { | |||||
| return; | return; | ||||
| } | } | ||||
| const dataJson = parseJsonText(data); | const dataJson = parseJsonText(data); | ||||
| if (dataJson) { | |||||
| const nodes = dataJson?.result?.object?.status?.nodes; | |||||
| if (nodes) { | |||||
| onMessage(nodes); | |||||
| } | |||||
| const statusData = dataJson?.result?.object?.status; | |||||
| if (statusData) { | |||||
| const { finishedAt, phase, nodes } = statusData; | |||||
| onMessage(experimentInsId, phase, finishedAt, nodes); | |||||
| } | } | ||||
| }; | }; | ||||
| @@ -30,17 +31,10 @@ export const useSSE = (onMessage: (data: any) => void) => { | |||||
| console.error('SSE error: ', error); | console.error('SSE error: ', error); | ||||
| }; | }; | ||||
| evtSourceRef.current = evtSource; | |||||
| }, | |||||
| [onMessage], | |||||
| ); | |||||
| const closeSSE = useCallback(() => { | |||||
| if (evtSourceRef.current) { | |||||
| evtSourceRef.current.close(); | |||||
| evtSourceRef.current = null; | |||||
| return () => { | |||||
| evtSource.close(); | |||||
| } | |||||
| } | } | ||||
| }, []); | |||||
| return [setupSSE, closeSSE]; | |||||
| }, [experimentInsId, isRunning, name, namespace, onMessage]); | |||||
| }; | }; | ||||
| @@ -0,0 +1,53 @@ | |||||
| /* | |||||
| * @Author: 赵伟 | |||||
| * @Date: 2024-10-10 08:51:41 | |||||
| * @Description: 服务器时间 hook | |||||
| */ | |||||
| import { getSeverTimeReq } from '@/services/experiment'; | |||||
| import { to } from '@/utils/promise'; | |||||
| import { useCallback, useEffect, useState } from 'react'; | |||||
| let globalTimeOffset: number | undefined = undefined; | |||||
| export const globalGetSeverTime = async () => { | |||||
| const requestStartTime = Date.now(); | |||||
| const [res] = await to(getSeverTimeReq()); | |||||
| const requestEndTime = Date.now(); | |||||
| const requestDuration = (requestEndTime - requestStartTime) / 2; | |||||
| if (res && res.data) { | |||||
| const serverDate = new Date(res.data); | |||||
| const timeOffset = serverDate.getTime() + requestDuration - requestEndTime; | |||||
| globalTimeOffset = timeOffset; | |||||
| return timeOffset; | |||||
| } | |||||
| }; | |||||
| export const now = () => { | |||||
| return new Date(Date.now() + (globalTimeOffset ?? 0)); | |||||
| }; | |||||
| /** 获取服务器时间 */ | |||||
| export function useServerTime() { | |||||
| const [timeOffset, setTimeOffset] = useState<number>(globalTimeOffset ?? 0); | |||||
| useEffect(() => { | |||||
| const getSeverTime = async () => { | |||||
| const [res] = await to(globalGetSeverTime()); | |||||
| if (res) { | |||||
| setTimeOffset(res); | |||||
| } | |||||
| }; | |||||
| // 获取服务器时间,防止第一次加载时,请求失败 | |||||
| if (!globalTimeOffset) { | |||||
| getSeverTime(); | |||||
| } | |||||
| }, []); | |||||
| const now = useCallback(() => { | |||||
| return new Date(Date.now() + timeOffset); | |||||
| }, [timeOffset]); | |||||
| return [now] as const; | |||||
| } | |||||
| @@ -1,10 +1,24 @@ | |||||
| { | { | ||||
| "id": "4511326", | "id": "4511326", | ||||
| "name": "智能材料科研平台-导航", | |||||
| "name": "复杂智能软件-导航", | |||||
| "font_family": "iconfont", | "font_family": "iconfont", | ||||
| "css_prefix_text": "icon-", | "css_prefix_text": "icon-", | ||||
| "description": "", | "description": "", | ||||
| "glyphs": [ | "glyphs": [ | ||||
| { | |||||
| "icon_id": "42495274", | |||||
| "name": "知识图谱-active", | |||||
| "font_class": "zhishitupu-icon-active", | |||||
| "unicode": "e63e", | |||||
| "unicode_decimal": 58942 | |||||
| }, | |||||
| { | |||||
| "icon_id": "42495275", | |||||
| "name": "知识图谱", | |||||
| "font_class": "zhishitupu-icon", | |||||
| "unicode": "e63f", | |||||
| "unicode_decimal": 58943 | |||||
| }, | |||||
| { | { | ||||
| "icon_id": "41643218", | "icon_id": "41643218", | ||||
| "name": "模型开发", | "name": "模型开发", | ||||
| @@ -106,13 +106,13 @@ function ActiveLearnInstance() { | |||||
| if (dataJson) { | if (dataJson) { | ||||
| const nodes = dataJson?.result?.object?.status?.nodes; | const nodes = dataJson?.result?.object?.status?.nodes; | ||||
| if (nodes) { | if (nodes) { | ||||
| // 节点 | |||||
| setNodes(nodes); | |||||
| const workflowStatus = Object.values(nodes).find((node: any) => | const workflowStatus = Object.values(nodes).find((node: any) => | ||||
| node.displayName.startsWith(NodePrefix), | node.displayName.startsWith(NodePrefix), | ||||
| ) as NodeStatus; | ) as NodeStatus; | ||||
| // 节点 | |||||
| setNodes(nodes); | |||||
| // 设置工作流状态 | // 设置工作流状态 | ||||
| if (workflowStatus) { | if (workflowStatus) { | ||||
| setWorkflowStatus(workflowStatus); | setWorkflowStatus(workflowStatus); | ||||
| @@ -153,6 +153,7 @@ function ActiveLearnInstance() { | |||||
| className={styles['active-learn-instance__basic']} | className={styles['active-learn-instance__basic']} | ||||
| info={experimentInfo} | info={experimentInfo} | ||||
| runStatus={workflowStatus} | runStatus={workflowStatus} | ||||
| instanceStatus={instanceInfo?.status} | |||||
| isInstance | isInstance | ||||
| /> | /> | ||||
| ), | ), | ||||
| @@ -1,5 +1,5 @@ | |||||
| import ConfigInfo, { type BasicInfoData } from '@/components/ConfigInfo'; | import ConfigInfo, { type BasicInfoData } from '@/components/ConfigInfo'; | ||||
| import { AutoMLTaskType, autoMLTaskTypeOptions } from '@/enums'; | |||||
| import { AutoMLTaskType, autoMLTaskTypeOptions, ExperimentStatus } from '@/enums'; | |||||
| import { useComputingResource } from '@/hooks/useComputingResource'; | import { useComputingResource } from '@/hooks/useComputingResource'; | ||||
| import { | import { | ||||
| classifierAlgorithms, | classifierAlgorithms, | ||||
| @@ -9,9 +9,8 @@ import { | |||||
| regressorAlgorithms, | regressorAlgorithms, | ||||
| } from '@/pages/ActiveLearn/components/CreateForm/utils'; | } from '@/pages/ActiveLearn/components/CreateForm/utils'; | ||||
| import { ActiveLearnData } from '@/pages/ActiveLearn/types'; | import { ActiveLearnData } from '@/pages/ActiveLearn/types'; | ||||
| import { experimentStatusInfo } from '@/pages/Experiment/status'; | |||||
| import ExperimentRunBasic from '@/pages/AutoML/components/ExperimentRunBasic'; | |||||
| import { type NodeStatus } from '@/types'; | import { type NodeStatus } from '@/types'; | ||||
| import { elapsedTime } from '@/utils/date'; | |||||
| import { | import { | ||||
| formatBoolean, | formatBoolean, | ||||
| formatCodeConfig, | formatCodeConfig, | ||||
| @@ -21,7 +20,6 @@ import { | |||||
| formatMirror, | formatMirror, | ||||
| formatModel, | formatModel, | ||||
| } from '@/utils/format'; | } from '@/utils/format'; | ||||
| import { Flex } from 'antd'; | |||||
| import classNames from 'classnames'; | import classNames from 'classnames'; | ||||
| import { useMemo } from 'react'; | import { useMemo } from 'react'; | ||||
| import styles from './index.less'; | import styles from './index.less'; | ||||
| @@ -31,9 +29,16 @@ type BasicInfoProps = { | |||||
| className?: string; | className?: string; | ||||
| isInstance?: boolean; | isInstance?: boolean; | ||||
| runStatus?: NodeStatus; | runStatus?: NodeStatus; | ||||
| instanceStatus?: ExperimentStatus; | |||||
| }; | }; | ||||
| function BasicInfo({ info, className, runStatus, isInstance = false }: BasicInfoProps) { | |||||
| function BasicInfo({ | |||||
| info, | |||||
| className, | |||||
| runStatus, | |||||
| instanceStatus, | |||||
| isInstance = false, | |||||
| }: BasicInfoProps) { | |||||
| const getResourceDescription = useComputingResource()[1]; | const getResourceDescription = useComputingResource()[1]; | ||||
| const basicDatas: BasicInfoData[] = useMemo(() => { | const basicDatas: BasicInfoData[] = useMemo(() => { | ||||
| if (!info) { | if (!info) { | ||||
| @@ -149,7 +154,7 @@ function BasicInfo({ info, className, runStatus, isInstance = false }: BasicInfo | |||||
| value: info.dataset_py, | value: info.dataset_py, | ||||
| }, | }, | ||||
| { | { | ||||
| label: '数据集类名', | |||||
| label: '数据集处理类名', | |||||
| value: info.dataset_class_name, | value: info.dataset_class_name, | ||||
| }, | }, | ||||
| { | { | ||||
| @@ -205,56 +210,13 @@ function BasicInfo({ info, className, runStatus, isInstance = false }: BasicInfo | |||||
| ]; | ]; | ||||
| }, [info, getResourceDescription]); | }, [info, getResourceDescription]); | ||||
| const instanceDatas = useMemo(() => { | |||||
| if (!info || !runStatus) { | |||||
| return []; | |||||
| } | |||||
| return [ | |||||
| { | |||||
| label: '启动时间', | |||||
| value: formatDate(info.create_time), | |||||
| ellipsis: true, | |||||
| }, | |||||
| { | |||||
| label: '执行时长', | |||||
| value: elapsedTime(info.create_time, runStatus.finishedAt), | |||||
| ellipsis: true, | |||||
| }, | |||||
| { | |||||
| label: '状态', | |||||
| value: ( | |||||
| <Flex align="center"> | |||||
| <img | |||||
| style={{ width: '17px', marginRight: '7px' }} | |||||
| src={experimentStatusInfo[runStatus.phase]?.icon} | |||||
| draggable={false} | |||||
| alt="" | |||||
| /> | |||||
| <div | |||||
| style={{ | |||||
| color: experimentStatusInfo[runStatus?.phase]?.color, | |||||
| fontSize: '15px', | |||||
| lineHeight: 1.6, | |||||
| }} | |||||
| > | |||||
| {experimentStatusInfo[runStatus?.phase]?.label} | |||||
| </div> | |||||
| </Flex> | |||||
| ), | |||||
| ellipsis: true, | |||||
| }, | |||||
| ]; | |||||
| }, [runStatus, info]); | |||||
| return ( | return ( | ||||
| <div className={classNames(styles['active-learn-basic'], className)}> | <div className={classNames(styles['active-learn-basic'], className)}> | ||||
| {isInstance && runStatus && ( | {isInstance && runStatus && ( | ||||
| <ConfigInfo | |||||
| title="运行信息" | |||||
| datas={instanceDatas} | |||||
| labelWidth={70} | |||||
| style={{ marginBottom: '20px' }} | |||||
| <ExperimentRunBasic | |||||
| create_time={info?.create_time} | |||||
| runStatus={runStatus} | |||||
| instanceStatus={instanceStatus} | |||||
| /> | /> | ||||
| )} | )} | ||||
| {!isInstance && ( | {!isInstance && ( | ||||
| @@ -101,16 +101,16 @@ function ExecuteConfig() { | |||||
| <Row gutter={8}> | <Row gutter={8}> | ||||
| <Col span={10}> | <Col span={10}> | ||||
| <Form.Item | <Form.Item | ||||
| label="数据集类名" | |||||
| label="数据集处理类名" | |||||
| name="dataset_class_name" | name="dataset_class_name" | ||||
| rules={[ | rules={[ | ||||
| { | { | ||||
| required: true, | required: true, | ||||
| message: '请输入数据集类名', | |||||
| message: '请输入数据集处理类名', | |||||
| }, | }, | ||||
| ]} | ]} | ||||
| > | > | ||||
| <Input placeholder="请输入数据集类名" maxLength={64} showCount allowClear /> | |||||
| <Input placeholder="请输入数据集处理类名" maxLength={64} showCount allowClear /> | |||||
| </Form.Item> | </Form.Item> | ||||
| </Col> | </Col> | ||||
| </Row> | </Row> | ||||
| @@ -110,13 +110,13 @@ function AutoMLInstance() { | |||||
| if (dataJson) { | if (dataJson) { | ||||
| const nodes = dataJson?.result?.object?.status?.nodes; | const nodes = dataJson?.result?.object?.status?.nodes; | ||||
| if (nodes) { | if (nodes) { | ||||
| // 节点 | |||||
| setNodes(nodes); | |||||
| const workflowStatus = Object.values(nodes).find((node: any) => | const workflowStatus = Object.values(nodes).find((node: any) => | ||||
| node.displayName.startsWith(NodePrefix), | node.displayName.startsWith(NodePrefix), | ||||
| ) as NodeStatus; | ) as NodeStatus; | ||||
| // 节点 | |||||
| setNodes(nodes); | |||||
| if (workflowStatus) { | if (workflowStatus) { | ||||
| setWorkflowStatus(workflowStatus); | setWorkflowStatus(workflowStatus); | ||||
| @@ -156,6 +156,7 @@ function AutoMLInstance() { | |||||
| className={styles['auto-ml-instance__basic']} | className={styles['auto-ml-instance__basic']} | ||||
| info={autoMLInfo} | info={autoMLInfo} | ||||
| runStatus={workflowStatus} | runStatus={workflowStatus} | ||||
| instanceStatus={instanceInfo?.status} | |||||
| isInstance | isInstance | ||||
| /> | /> | ||||
| ), | ), | ||||
| @@ -2,19 +2,18 @@ import ConfigInfo, { type BasicInfoData } from '@/components/ConfigInfo'; | |||||
| import { | import { | ||||
| AutoMLTaskType, | AutoMLTaskType, | ||||
| AutoMLType, | AutoMLType, | ||||
| ExperimentStatus, | |||||
| autoMLEnsembleClassOptions, | autoMLEnsembleClassOptions, | ||||
| autoMLTaskTypeOptions, | autoMLTaskTypeOptions, | ||||
| } from '@/enums'; | } from '@/enums'; | ||||
| import { useComputingResource } from '@/hooks/useComputingResource'; | import { useComputingResource } from '@/hooks/useComputingResource'; | ||||
| import { AutoMLData } from '@/pages/AutoML/types'; | import { AutoMLData } from '@/pages/AutoML/types'; | ||||
| import { experimentStatusInfo } from '@/pages/Experiment/status'; | |||||
| import { type NodeStatus } from '@/types'; | import { type NodeStatus } from '@/types'; | ||||
| import { parseJsonText } from '@/utils'; | import { parseJsonText } from '@/utils'; | ||||
| import { elapsedTime } from '@/utils/date'; | |||||
| import { formatBoolean, formatDataset, formatDate, formatEnum } from '@/utils/format'; | import { formatBoolean, formatDataset, formatDate, formatEnum } from '@/utils/format'; | ||||
| import { Flex } from 'antd'; | |||||
| import classNames from 'classnames'; | import classNames from 'classnames'; | ||||
| import { useMemo } from 'react'; | import { useMemo } from 'react'; | ||||
| import ExperimentRunBasic from '../ExperimentRunBasic'; | |||||
| import styles from './index.less'; | import styles from './index.less'; | ||||
| // 格式化优化方向 | // 格式化优化方向 | ||||
| @@ -40,9 +39,16 @@ type AutoMLBasicProps = { | |||||
| className?: string; | className?: string; | ||||
| isInstance?: boolean; | isInstance?: boolean; | ||||
| runStatus?: NodeStatus; | runStatus?: NodeStatus; | ||||
| instanceStatus?: ExperimentStatus; | |||||
| }; | }; | ||||
| function AutoMLBasic({ info, className, runStatus, isInstance = false }: AutoMLBasicProps) { | |||||
| function AutoMLBasic({ | |||||
| info, | |||||
| className, | |||||
| runStatus, | |||||
| instanceStatus, | |||||
| isInstance = false, | |||||
| }: AutoMLBasicProps) { | |||||
| const getResourceDescription = useComputingResource()[1]; | const getResourceDescription = useComputingResource()[1]; | ||||
| const basicDatas: BasicInfoData[] = useMemo(() => { | const basicDatas: BasicInfoData[] = useMemo(() => { | ||||
| if (!info) { | if (!info) { | ||||
| @@ -284,53 +290,13 @@ function AutoMLBasic({ info, className, runStatus, isInstance = false }: AutoMLB | |||||
| ]; | ]; | ||||
| }, [info]); | }, [info]); | ||||
| const instanceDatas = useMemo(() => { | |||||
| if (!info || !runStatus) { | |||||
| return []; | |||||
| } | |||||
| return [ | |||||
| { | |||||
| label: '启动时间', | |||||
| value: formatDate(info.create_time), | |||||
| }, | |||||
| { | |||||
| label: '执行时长', | |||||
| value: elapsedTime(info.create_time, runStatus.finishedAt), | |||||
| }, | |||||
| { | |||||
| label: '状态', | |||||
| value: ( | |||||
| <Flex align="center"> | |||||
| <img | |||||
| style={{ width: '17px', marginRight: '7px' }} | |||||
| src={experimentStatusInfo[runStatus.phase]?.icon} | |||||
| draggable={false} | |||||
| alt="" | |||||
| /> | |||||
| <div | |||||
| style={{ | |||||
| color: experimentStatusInfo[runStatus?.phase]?.color, | |||||
| fontSize: '15px', | |||||
| lineHeight: 1.6, | |||||
| }} | |||||
| > | |||||
| {experimentStatusInfo[runStatus?.phase]?.label} | |||||
| </div> | |||||
| </Flex> | |||||
| ), | |||||
| }, | |||||
| ]; | |||||
| }, [runStatus, info]); | |||||
| return ( | return ( | ||||
| <div className={classNames(styles['auto-ml-basic'], className)}> | <div className={classNames(styles['auto-ml-basic'], className)}> | ||||
| {isInstance && runStatus && ( | {isInstance && runStatus && ( | ||||
| <ConfigInfo | |||||
| title="运行信息" | |||||
| datas={instanceDatas} | |||||
| labelWidth={70} | |||||
| style={{ marginBottom: '20px' }} | |||||
| <ExperimentRunBasic | |||||
| create_time={info?.create_time} | |||||
| runStatus={runStatus} | |||||
| instanceStatus={instanceStatus} | |||||
| /> | /> | ||||
| )} | )} | ||||
| {!isInstance && ( | {!isInstance && ( | ||||
| @@ -1,3 +1,5 @@ | |||||
| @cellWidth: calc(100% + 32px + 33px - 48px - 200px - 344px); | |||||
| .tableExpandBox { | .tableExpandBox { | ||||
| display: flex; | display: flex; | ||||
| align-items: center; | align-items: center; | ||||
| @@ -11,22 +13,22 @@ | |||||
| } | } | ||||
| .check { | .check { | ||||
| width: calc((100% + 32px + 33px) / 5 / 2); | |||||
| width: calc(@cellWidth * 3 / 20); // 15% | |||||
| } | } | ||||
| .index { | .index { | ||||
| width: calc((100% + 32px + 33px) / 5 / 2); | |||||
| width: calc(@cellWidth * 3 / 20); // 15% | |||||
| } | } | ||||
| .description { | .description { | ||||
| display: flex; | display: flex; | ||||
| flex: 1; | |||||
| align-items: center; | align-items: center; | ||||
| width: calc(@cellWidth / 2); // 50% | |||||
| } | } | ||||
| .startTime { | .startTime { | ||||
| .singleLine(); | .singleLine(); | ||||
| width: 200px; | |||||
| width: calc(@cellWidth / 5); // 20% | |||||
| } | } | ||||
| .status { | .status { | ||||
| @@ -1,20 +1,19 @@ | |||||
| import KFIcon from '@/components/KFIcon'; | import KFIcon from '@/components/KFIcon'; | ||||
| import { ExperimentStatus } from '@/enums'; | import { ExperimentStatus } from '@/enums'; | ||||
| import { useCheck } from '@/hooks/useCheck'; | import { useCheck } from '@/hooks/useCheck'; | ||||
| import { experimentStatusInfo } from '@/pages/Experiment/status'; | |||||
| import themes from '@/styles/theme.less'; | import themes from '@/styles/theme.less'; | ||||
| import { type ExperimentInstance } from '@/types'; | import { type ExperimentInstance } from '@/types'; | ||||
| import { elapsedTime, formatDate } from '@/utils/date'; | |||||
| import { to } from '@/utils/promise'; | import { to } from '@/utils/promise'; | ||||
| import { modalConfirm } from '@/utils/ui'; | import { modalConfirm } from '@/utils/ui'; | ||||
| import { DoubleRightOutlined } from '@ant-design/icons'; | import { DoubleRightOutlined } from '@ant-design/icons'; | ||||
| import { App, Button, Checkbox, ConfigProvider, Typography } from 'antd'; | |||||
| import { App, Button, Checkbox, ConfigProvider } from 'antd'; | |||||
| import classNames from 'classnames'; | import classNames from 'classnames'; | ||||
| import { useEffect, useMemo } from 'react'; | import { useEffect, useMemo } from 'react'; | ||||
| import { ExperimentListType, experimentListConfig } from '../ExperimentList/config'; | import { ExperimentListType, experimentListConfig } from '../ExperimentList/config'; | ||||
| import styles from './index.less'; | import styles from './index.less'; | ||||
| import ExperimentInstanceComponent from './instance'; | |||||
| type ExperimentInstanceProps = { | |||||
| type ExperimentInstanceListProps = { | |||||
| type: ExperimentListType; | type: ExperimentListType; | ||||
| experimentInsList?: ExperimentInstance[]; | experimentInsList?: ExperimentInstance[]; | ||||
| experimentInsTotal: number; | experimentInsTotal: number; | ||||
| @@ -24,7 +23,7 @@ type ExperimentInstanceProps = { | |||||
| onLoadMore?: () => void; | onLoadMore?: () => void; | ||||
| }; | }; | ||||
| function ExperimentInstanceComponent({ | |||||
| function ExperimentInstanceList({ | |||||
| type, | type, | ||||
| experimentInsList, | experimentInsList, | ||||
| experimentInsTotal, | experimentInsTotal, | ||||
| @@ -32,7 +31,7 @@ function ExperimentInstanceComponent({ | |||||
| onRemove, | onRemove, | ||||
| onTerminate, | onTerminate, | ||||
| onLoadMore, | onLoadMore, | ||||
| }: ExperimentInstanceProps) { | |||||
| }: ExperimentInstanceListProps) { | |||||
| const { message } = App.useApp(); | const { message } = App.useApp(); | ||||
| const allIntanceIds = useMemo(() => { | const allIntanceIds = useMemo(() => { | ||||
| return experimentInsList?.map((item) => item.id) || []; | return experimentInsList?.map((item) => item.id) || []; | ||||
| @@ -171,28 +170,14 @@ function ExperimentInstanceComponent({ | |||||
| > | > | ||||
| {index + 1} | {index + 1} | ||||
| </a> | </a> | ||||
| <div className={styles.description}> | |||||
| {elapsedTime(item.create_time, item.finish_time)} | |||||
| </div> | |||||
| <div className={styles.startTime}> | |||||
| <Typography.Text ellipsis={{ tooltip: formatDate(item.create_time) }}> | |||||
| {formatDate(item.create_time)} | |||||
| </Typography.Text> | |||||
| </div> | |||||
| <div className={styles.statusBox}> | |||||
| <img | |||||
| style={{ width: '17px', marginRight: '7px' }} | |||||
| src={experimentStatusInfo[item.status as ExperimentStatus]?.icon} | |||||
| draggable={false} | |||||
| alt="" | |||||
| /> | |||||
| <span | |||||
| style={{ color: experimentStatusInfo[item.status as ExperimentStatus]?.color }} | |||||
| className={styles.statusIcon} | |||||
| > | |||||
| {experimentStatusInfo[item.status as ExperimentStatus]?.label} | |||||
| </span> | |||||
| </div> | |||||
| <ExperimentInstanceComponent | |||||
| create_time={item.create_time} | |||||
| finish_time={item.finish_time} | |||||
| status={item.status as ExperimentStatus} | |||||
| argo_ins_name={item.argo_ins_name} | |||||
| argo_ins_ns={item.argo_ins_ns} | |||||
| experimentInsId={item.id} | |||||
| ></ExperimentInstanceComponent> | |||||
| <div className={styles.operation}> | <div className={styles.operation}> | ||||
| <Button | <Button | ||||
| type="link" | type="link" | ||||
| @@ -244,4 +229,4 @@ function ExperimentInstanceComponent({ | |||||
| ); | ); | ||||
| } | } | ||||
| export default ExperimentInstanceComponent; | |||||
| export default ExperimentInstanceList; | |||||
| @@ -0,0 +1,68 @@ | |||||
| import RunDuration from '@/components/RunDuration'; | |||||
| import { ExperimentStatus } from '@/enums'; | |||||
| import { useSSE, type MessageHandler } from '@/hooks/useSSE'; | |||||
| import { experimentStatusInfo } from '@/pages/Experiment/status'; | |||||
| import { ExperimentCompleted } from '@/utils/constant'; | |||||
| import { formatDate } from '@/utils/date'; | |||||
| import { Typography } from 'antd'; | |||||
| import React, { useCallback } from 'react'; | |||||
| import styles from './index.less'; | |||||
| type ExperimentInstanceProps = { | |||||
| create_time?: string; | |||||
| finish_time?: string; | |||||
| status: ExperimentStatus; | |||||
| argo_ins_name: string; | |||||
| argo_ins_ns: string; | |||||
| experimentInsId: number; | |||||
| }; | |||||
| function ExperimentInstance({ | |||||
| create_time, | |||||
| finish_time, | |||||
| status, | |||||
| argo_ins_name, | |||||
| argo_ins_ns, | |||||
| experimentInsId, | |||||
| }: ExperimentInstanceProps) { | |||||
| const handleSSEMessage: MessageHandler = useCallback( | |||||
| (experimentInsId: number, status: string, finish_time: string) => { | |||||
| window.postMessage({ | |||||
| type: ExperimentCompleted, | |||||
| payload: { | |||||
| id: experimentInsId, | |||||
| status, | |||||
| finish_time, | |||||
| }, | |||||
| }); | |||||
| }, | |||||
| [], | |||||
| ); | |||||
| useSSE(experimentInsId, status, argo_ins_name, argo_ins_ns, handleSSEMessage); | |||||
| return ( | |||||
| <React.Fragment> | |||||
| <div className={styles.description}> | |||||
| <RunDuration createTime={create_time} finishTime={finish_time} /> | |||||
| </div> | |||||
| <div className={styles.startTime}> | |||||
| <Typography.Text ellipsis={{ tooltip: formatDate(create_time) }}> | |||||
| {formatDate(create_time)} | |||||
| </Typography.Text> | |||||
| </div> | |||||
| <div className={styles.statusBox}> | |||||
| <img | |||||
| style={{ width: '17px', marginRight: '7px' }} | |||||
| src={experimentStatusInfo[status]?.icon} | |||||
| draggable={false} | |||||
| alt="" | |||||
| /> | |||||
| <span style={{ color: experimentStatusInfo[status]?.color }} className={styles.statusIcon}> | |||||
| {experimentStatusInfo[status]?.label} | |||||
| </span> | |||||
| </div> | |||||
| </React.Fragment> | |||||
| ); | |||||
| } | |||||
| export default ExperimentInstance; | |||||
| @@ -8,10 +8,12 @@ import KFIcon from '@/components/KFIcon'; | |||||
| import PageTitle from '@/components/PageTitle'; | import PageTitle from '@/components/PageTitle'; | ||||
| import { ExperimentStatus, autoMLTypeOptions } from '@/enums'; | import { ExperimentStatus, autoMLTypeOptions } from '@/enums'; | ||||
| import { useCacheState } from '@/hooks/useCacheState'; | import { useCacheState } from '@/hooks/useCacheState'; | ||||
| import { useServerTime } from '@/hooks/useServerTime'; | |||||
| import { AutoMLData } from '@/pages/AutoML/types'; | import { AutoMLData } from '@/pages/AutoML/types'; | ||||
| import { experimentStatusInfo } from '@/pages/Experiment/status'; | import { experimentStatusInfo } from '@/pages/Experiment/status'; | ||||
| import themes from '@/styles/theme.less'; | import themes from '@/styles/theme.less'; | ||||
| import { type ExperimentInstance as ExperimentInstanceData } from '@/types'; | import { type ExperimentInstance as ExperimentInstanceData } from '@/types'; | ||||
| import { ExperimentCompleted } from '@/utils/constant'; | |||||
| import { to } from '@/utils/promise'; | import { to } from '@/utils/promise'; | ||||
| import tableCellRender, { TableCellValueType } from '@/utils/table'; | import tableCellRender, { TableCellValueType } from '@/utils/table'; | ||||
| import { modalConfirm } from '@/utils/ui'; | import { modalConfirm } from '@/utils/ui'; | ||||
| @@ -28,8 +30,8 @@ import { | |||||
| } from 'antd'; | } from 'antd'; | ||||
| import { type SearchProps } from 'antd/es/input'; | import { type SearchProps } from 'antd/es/input'; | ||||
| import classNames from 'classnames'; | import classNames from 'classnames'; | ||||
| import { useCallback, useEffect, useState } from 'react'; | |||||
| import ExperimentInstance from '../ExperimentInstance'; | |||||
| import { useCallback, useEffect, useRef, useState } from 'react'; | |||||
| import ExperimentInstanceList from '../ExperimentInstanceList'; | |||||
| import { ExperimentListType, experimentListConfig } from './config'; | import { ExperimentListType, experimentListConfig } from './config'; | ||||
| import styles from './index.less'; | import styles from './index.less'; | ||||
| @@ -50,6 +52,7 @@ function ExperimentList({ type }: ExperimentListProps) { | |||||
| const [experimentInsList, setExperimentInsList] = useState<ExperimentInstanceData[]>([]); | const [experimentInsList, setExperimentInsList] = useState<ExperimentInstanceData[]>([]); | ||||
| const [expandedRowKeys, setExpandedRowKeys] = useState<number[]>([]); | const [expandedRowKeys, setExpandedRowKeys] = useState<number[]>([]); | ||||
| const [experimentInsTotal, setExperimentInsTotal] = useState(0); | const [experimentInsTotal, setExperimentInsTotal] = useState(0); | ||||
| const [now] = useServerTime(); | |||||
| const [pagination, setPagination] = useState<TablePaginationConfig>( | const [pagination, setPagination] = useState<TablePaginationConfig>( | ||||
| cacheState?.pagination ?? { | cacheState?.pagination ?? { | ||||
| current: 1, | current: 1, | ||||
| @@ -57,6 +60,7 @@ function ExperimentList({ type }: ExperimentListProps) { | |||||
| }, | }, | ||||
| ); | ); | ||||
| const config = experimentListConfig[type]; | const config = experimentListConfig[type]; | ||||
| const timerRef = useRef<ReturnType<typeof window.setTimeout> | undefined>(); | |||||
| // 获取自主机器学习或超参数自动优化列表 | // 获取自主机器学习或超参数自动优化列表 | ||||
| const getAutoMLList = useCallback(async () => { | const getAutoMLList = useCallback(async () => { | ||||
| @@ -78,6 +82,89 @@ function ExperimentList({ type }: ExperimentListProps) { | |||||
| getAutoMLList(); | getAutoMLList(); | ||||
| }, [getAutoMLList]); | }, [getAutoMLList]); | ||||
| // 获取实验实例列表 | |||||
| const getExperimentInsList = useCallback( | |||||
| async (recordId: number, page: number, size: number) => { | |||||
| const params = { | |||||
| [config.idProperty]: recordId, | |||||
| page: page, | |||||
| size: size, | |||||
| }; | |||||
| const request = config.getInsListReq; | |||||
| const [res] = await to(request(params)); | |||||
| if (res && res.data) { | |||||
| const { content = [], totalElements = 0 } = res.data; | |||||
| try { | |||||
| if (page === 0) { | |||||
| setExperimentInsList(content); | |||||
| } else { | |||||
| setExperimentInsList((prev) => [...prev, ...content]); | |||||
| } | |||||
| setExperimentInsTotal(totalElements); | |||||
| } catch (error) { | |||||
| console.error('JSON parse error: ', error); | |||||
| } | |||||
| } | |||||
| }, | |||||
| [config.getInsListReq, config.idProperty], | |||||
| ); | |||||
| // 刷新实验列表状态, | |||||
| // TODO: 目前是直接刷新实验列表,后续需要优化,只刷新状态 | |||||
| const refreshExperimentList = useCallback(() => { | |||||
| getAutoMLList(); | |||||
| }, [getAutoMLList]); | |||||
| // 刷新实验实例列表 | |||||
| const refreshExperimentIns = useCallback( | |||||
| (experimentId: number) => { | |||||
| const length = experimentInsList.length; | |||||
| getExperimentInsList(experimentId, 0, length); | |||||
| }, | |||||
| [getExperimentInsList, experimentInsList], | |||||
| ); | |||||
| // 新增,删除版本时,重置分页,然后刷新版本列表 | |||||
| useEffect(() => { | |||||
| const handleMessage = (e: MessageEvent) => { | |||||
| 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, | |||||
| ), | |||||
| ); | |||||
| if (timerRef.current) { | |||||
| clearTimeout(timerRef.current); | |||||
| timerRef.current = undefined; | |||||
| } | |||||
| timerRef.current = setTimeout(() => { | |||||
| refreshExperimentList(); | |||||
| }, 10000); | |||||
| } | |||||
| }; | |||||
| window.addEventListener('message', handleMessage); | |||||
| return () => { | |||||
| window.removeEventListener('message', handleMessage); | |||||
| if (timerRef.current) { | |||||
| clearTimeout(timerRef.current); | |||||
| timerRef.current = undefined; | |||||
| } | |||||
| }; | |||||
| }, [refreshExperimentList]); | |||||
| // 搜索 | // 搜索 | ||||
| const onSearch: SearchProps['onSearch'] = (value) => { | const onSearch: SearchProps['onSearch'] = (value) => { | ||||
| setSearchText(value); | setSearchText(value); | ||||
| @@ -151,40 +238,17 @@ function ExperimentList({ type }: ExperimentListProps) { | |||||
| message.success('运行成功'); | message.success('运行成功'); | ||||
| setExpandedRowKeys([record.id]); | setExpandedRowKeys([record.id]); | ||||
| refreshExperimentList(); | refreshExperimentList(); | ||||
| refreshExperimentIns(record.id); | |||||
| getExperimentInsList(record.id, 0, 5); | |||||
| } | } | ||||
| }; | }; | ||||
| // --------------------------- 实验实例 --------------------------- | // --------------------------- 实验实例 --------------------------- | ||||
| // 获取实验实例列表 | |||||
| const getExperimentInsList = async (recordId: number, page: number) => { | |||||
| const params = { | |||||
| [config.idProperty]: recordId, | |||||
| page: page, | |||||
| size: 5, | |||||
| }; | |||||
| const request = config.getInsListReq; | |||||
| const [res] = await to(request(params)); | |||||
| if (res && res.data) { | |||||
| const { content = [], totalElements = 0 } = res.data; | |||||
| try { | |||||
| if (page === 0) { | |||||
| setExperimentInsList(content); | |||||
| } else { | |||||
| setExperimentInsList((prev) => [...prev, ...content]); | |||||
| } | |||||
| setExperimentInsTotal(totalElements); | |||||
| } catch (error) { | |||||
| console.error('JSON parse error: ', error); | |||||
| } | |||||
| } | |||||
| }; | |||||
| // 展开实例 | // 展开实例 | ||||
| const handleExpandChange = (expanded: boolean, record: AutoMLData) => { | const handleExpandChange = (expanded: boolean, record: AutoMLData) => { | ||||
| setExperimentInsList([]); | setExperimentInsList([]); | ||||
| if (expanded) { | if (expanded) { | ||||
| setExpandedRowKeys([record.id]); | setExpandedRowKeys([record.id]); | ||||
| getExperimentInsList(record.id, 0); | |||||
| getExperimentInsList(record.id, 0, 5); | |||||
| refreshExperimentList(); | refreshExperimentList(); | ||||
| } else { | } else { | ||||
| setExpandedRowKeys([]); | setExpandedRowKeys([]); | ||||
| @@ -196,16 +260,11 @@ function ExperimentList({ type }: ExperimentListProps) { | |||||
| navigate(`instance/${autoML.id}/${record.id}`); | navigate(`instance/${autoML.id}/${record.id}`); | ||||
| }; | }; | ||||
| // 刷新实验实例列表 | |||||
| const refreshExperimentIns = (experimentId: number) => { | |||||
| getExperimentInsList(experimentId, 0); | |||||
| }; | |||||
| // 加载更多实验实例 | // 加载更多实验实例 | ||||
| const loadMoreExperimentIns = () => { | const loadMoreExperimentIns = () => { | ||||
| const page = Math.round(experimentInsList.length / 5); | const page = Math.round(experimentInsList.length / 5); | ||||
| const recordId = expandedRowKeys[0]; | const recordId = expandedRowKeys[0]; | ||||
| getExperimentInsList(recordId, page); | |||||
| getExperimentInsList(recordId, page, 5); | |||||
| }; | }; | ||||
| // 实验实例终止 | // 实验实例终止 | ||||
| @@ -218,19 +277,13 @@ function ExperimentList({ type }: ExperimentListProps) { | |||||
| return { | return { | ||||
| ...item, | ...item, | ||||
| status: ExperimentStatus.Terminated, | status: ExperimentStatus.Terminated, | ||||
| finish_time: now().toISOString(), | |||||
| }; | }; | ||||
| } | } | ||||
| return item; | return item; | ||||
| }); | }); | ||||
| }); | }); | ||||
| }; | }; | ||||
| // 刷新实验列表状态, | |||||
| // 目前是直接刷新实验列表,后续需要优化,只刷新状态 | |||||
| const refreshExperimentList = () => { | |||||
| getAutoMLList(); | |||||
| }; | |||||
| // --------------------------- Table --------------------------- | // --------------------------- Table --------------------------- | ||||
| // 分页切换 | // 分页切换 | ||||
| const handleTableChange: TableProps<AutoMLData>['onChange'] = ( | const handleTableChange: TableProps<AutoMLData>['onChange'] = ( | ||||
| @@ -249,7 +302,7 @@ function ExperimentList({ type }: ExperimentListProps) { | |||||
| title: '类型', | title: '类型', | ||||
| dataIndex: 'type', | dataIndex: 'type', | ||||
| key: 'type', | key: 'type', | ||||
| width: '10%', | |||||
| width: '15%', | |||||
| render: tableCellRender(false, TableCellValueType.Enum, { | render: tableCellRender(false, TableCellValueType.Enum, { | ||||
| options: autoMLTypeOptions, | options: autoMLTypeOptions, | ||||
| }), | }), | ||||
| @@ -263,7 +316,7 @@ function ExperimentList({ type }: ExperimentListProps) { | |||||
| title: '实验名称', | title: '实验名称', | ||||
| dataIndex: config.nameProperty, | dataIndex: config.nameProperty, | ||||
| key: 'name', | key: 'name', | ||||
| width: '20%', | |||||
| width: '30%', | |||||
| render: tableCellRender(false, TableCellValueType.Link, { | render: tableCellRender(false, TableCellValueType.Link, { | ||||
| onClick: gotoDetail, | onClick: gotoDetail, | ||||
| }), | }), | ||||
| @@ -273,14 +326,15 @@ function ExperimentList({ type }: ExperimentListProps) { | |||||
| dataIndex: config.descProperty, | dataIndex: config.descProperty, | ||||
| key: 'description', | key: 'description', | ||||
| render: tableCellRender(true), | render: tableCellRender(true), | ||||
| width: type === ExperimentListType.AutoML ? '35%' : '50%', | |||||
| }, | }, | ||||
| ...diffColumns, | ...diffColumns, | ||||
| { | { | ||||
| title: '创建时间', | title: '创建时间', | ||||
| dataIndex: 'update_time', | dataIndex: 'update_time', | ||||
| key: 'update_time', | key: 'update_time', | ||||
| width: 200, | |||||
| render: tableCellRender(false, TableCellValueType.Date), | |||||
| width: '20%', | |||||
| render: tableCellRender(true, TableCellValueType.Date), | |||||
| }, | }, | ||||
| { | { | ||||
| title: '最近五次运行状态', | title: '最近五次运行状态', | ||||
| @@ -409,7 +463,7 @@ function ExperimentList({ type }: ExperimentListProps) { | |||||
| onChange={handleTableChange} | onChange={handleTableChange} | ||||
| expandable={{ | expandable={{ | ||||
| expandedRowRender: (record) => ( | expandedRowRender: (record) => ( | ||||
| <ExperimentInstance | |||||
| <ExperimentInstanceList | |||||
| type={type} | type={type} | ||||
| experimentInsList={experimentInsList} | experimentInsList={experimentInsList} | ||||
| experimentInsTotal={experimentInsTotal} | experimentInsTotal={experimentInsTotal} | ||||
| @@ -420,7 +474,7 @@ function ExperimentList({ type }: ExperimentListProps) { | |||||
| }} | }} | ||||
| onTerminate={handleInstanceTerminate} | onTerminate={handleInstanceTerminate} | ||||
| onLoadMore={() => loadMoreExperimentIns()} | onLoadMore={() => loadMoreExperimentIns()} | ||||
| ></ExperimentInstance> | |||||
| ></ExperimentInstanceList> | |||||
| ), | ), | ||||
| onExpand: handleExpandChange, | onExpand: handleExpandChange, | ||||
| expandedRowKeys: expandedRowKeys, | expandedRowKeys: expandedRowKeys, | ||||
| @@ -0,0 +1,70 @@ | |||||
| import ConfigInfo from '@/components/ConfigInfo'; | |||||
| import RunDuration from '@/components/RunDuration'; | |||||
| import { ExperimentStatus } from '@/enums'; | |||||
| import { experimentStatusInfo } from '@/pages/Experiment/status'; | |||||
| import { type NodeStatus } from '@/types'; | |||||
| import { formatDate } from '@/utils/format'; | |||||
| import { Flex } from 'antd'; | |||||
| import { useMemo } from 'react'; | |||||
| type ExperimentRunBasicProps = { | |||||
| create_time?: string; | |||||
| runStatus?: NodeStatus; | |||||
| instanceStatus?: ExperimentStatus; | |||||
| }; | |||||
| function ExperimentRunBasic({ create_time, runStatus, instanceStatus }: ExperimentRunBasicProps) { | |||||
| const instanceDatas = useMemo(() => { | |||||
| if (!runStatus) { | |||||
| return []; | |||||
| } | |||||
| const status = | |||||
| instanceStatus === ExperimentStatus.Terminated ? instanceStatus : runStatus.phase; | |||||
| const statusInfo = experimentStatusInfo[status]; | |||||
| return [ | |||||
| { | |||||
| label: '启动时间', | |||||
| value: formatDate(create_time), | |||||
| }, | |||||
| { | |||||
| label: '执行时长', | |||||
| value: <RunDuration createTime={create_time} finishTime={runStatus.finishedAt} />, | |||||
| }, | |||||
| { | |||||
| label: '状态', | |||||
| value: ( | |||||
| <Flex align="center"> | |||||
| <img | |||||
| style={{ width: '17px', marginRight: '7px' }} | |||||
| src={statusInfo?.icon} | |||||
| draggable={false} | |||||
| alt="" | |||||
| /> | |||||
| <div | |||||
| style={{ | |||||
| color: statusInfo?.color, | |||||
| fontSize: '15px', | |||||
| lineHeight: 1.6, | |||||
| }} | |||||
| > | |||||
| {statusInfo?.label} | |||||
| </div> | |||||
| </Flex> | |||||
| ), | |||||
| }, | |||||
| ]; | |||||
| }, [runStatus, create_time, instanceStatus]); | |||||
| return ( | |||||
| <ConfigInfo | |||||
| title="运行信息" | |||||
| datas={instanceDatas} | |||||
| labelWidth={70} | |||||
| style={{ marginBottom: '20px' }} | |||||
| /> | |||||
| ); | |||||
| } | |||||
| export default ExperimentRunBasic; | |||||
| @@ -22,7 +22,7 @@ import styles from './index.less'; | |||||
| export type CodeConfigData = { | export type CodeConfigData = { | ||||
| id: number; | id: number; | ||||
| code_repo_name: string; | code_repo_name: string; | ||||
| code_repo_vis: number; | |||||
| is_public: boolean; | |||||
| git_url: string; | git_url: string; | ||||
| git_branch: string; | git_branch: string; | ||||
| git_user_name: string; | git_user_name: string; | ||||
| @@ -1,5 +1,4 @@ | |||||
| import KFModal from '@/components/KFModal'; | import KFModal from '@/components/KFModal'; | ||||
| import { AvailableRange } from '@/enums'; | |||||
| import { type CodeConfigData } from '@/pages/CodeConfig/List'; | import { type CodeConfigData } from '@/pages/CodeConfig/List'; | ||||
| import { addCodeConfigReq, updateCodeConfigReq } from '@/services/codeConfig'; | import { addCodeConfigReq, updateCodeConfigReq } from '@/services/codeConfig'; | ||||
| import { to } from '@/utils/promise'; | import { to } from '@/utils/promise'; | ||||
| @@ -27,7 +26,7 @@ interface AddCodeConfigModalProps extends Omit<ModalProps, 'onOk'> { | |||||
| function AddCodeConfigModal({ opType, codeConfigData, onOk, ...rest }: AddCodeConfigModalProps) { | function AddCodeConfigModal({ opType, codeConfigData, onOk, ...rest }: AddCodeConfigModalProps) { | ||||
| const [form] = Form.useForm(); | const [form] = Form.useForm(); | ||||
| const isPublic = Form.useWatch('code_repo_vis', form) === AvailableRange.Public; | |||||
| const isPublic = Form.useWatch('is_public', form) as boolean; | |||||
| const urlExample = useMemo( | const urlExample = useMemo( | ||||
| () => | () => | ||||
| @@ -57,7 +56,7 @@ function AddCodeConfigModal({ opType, codeConfigData, onOk, ...rest }: AddCodeCo | |||||
| ...formData, | ...formData, | ||||
| }; | }; | ||||
| // 清除多余的信息 | // 清除多余的信息 | ||||
| if (formData.code_repo_vis === AvailableRange.Public) { | |||||
| if (formData.is_public) { | |||||
| omit(params, ['verify_mode', 'git_user_name', 'git_password', 'ssh_key']); | omit(params, ['verify_mode', 'git_user_name', 'git_password', 'ssh_key']); | ||||
| } | } | ||||
| if (formData.verify_mode === VerifyMode.Password) { | if (formData.verify_mode === VerifyMode.Password) { | ||||
| @@ -83,7 +82,7 @@ function AddCodeConfigModal({ opType, codeConfigData, onOk, ...rest }: AddCodeCo | |||||
| // 设置初始值 | // 设置初始值 | ||||
| const initialValues: FormData = codeConfigData ?? { | const initialValues: FormData = codeConfigData ?? { | ||||
| code_repo_vis: AvailableRange.Public, | |||||
| is_public: true, | |||||
| verify_mode: VerifyMode.Password, | verify_mode: VerifyMode.Password, | ||||
| }; | }; | ||||
| if (initialValues.verify_mode === undefined || initialValues.verify_mode === null) { | if (initialValues.verify_mode === undefined || initialValues.verify_mode === null) { | ||||
| @@ -125,7 +124,7 @@ function AddCodeConfigModal({ opType, codeConfigData, onOk, ...rest }: AddCodeCo | |||||
| </Form.Item> | </Form.Item> | ||||
| <Form.Item | <Form.Item | ||||
| label="代码仓库可见性" | label="代码仓库可见性" | ||||
| name="code_repo_vis" | |||||
| name="is_public" | |||||
| rules={[ | rules={[ | ||||
| { | { | ||||
| required: true, | required: true, | ||||
| @@ -134,8 +133,8 @@ function AddCodeConfigModal({ opType, codeConfigData, onOk, ...rest }: AddCodeCo | |||||
| ]} | ]} | ||||
| > | > | ||||
| <Radio.Group> | <Radio.Group> | ||||
| <Radio value={AvailableRange.Public}>公开</Radio> | |||||
| <Radio value={AvailableRange.Private}>私有</Radio> | |||||
| <Radio value={true}>公开</Radio> | |||||
| <Radio value={false}>私有</Radio> | |||||
| </Radio.Group> | </Radio.Group> | ||||
| </Form.Item> | </Form.Item> | ||||
| <Form.Item | <Form.Item | ||||
| @@ -171,11 +170,11 @@ function AddCodeConfigModal({ opType, codeConfigData, onOk, ...rest }: AddCodeCo | |||||
| <Form.Item | <Form.Item | ||||
| noStyle | noStyle | ||||
| shouldUpdate={(prevValues, currentValues) => | shouldUpdate={(prevValues, currentValues) => | ||||
| prevValues?.code_repo_vis !== currentValues?.code_repo_vis | |||||
| prevValues?.is_public !== currentValues?.is_public | |||||
| } | } | ||||
| > | > | ||||
| {({ getFieldValue }) => { | {({ getFieldValue }) => { | ||||
| return getFieldValue('code_repo_vis') === AvailableRange.Private ? ( | |||||
| return getFieldValue('is_public') === false ? ( | |||||
| <> | <> | ||||
| <Form.Item | <Form.Item | ||||
| label="验证方式" | label="验证方式" | ||||
| @@ -1,7 +1,6 @@ | |||||
| import clock from '@/assets/img/clock.png'; | import clock from '@/assets/img/clock.png'; | ||||
| import creatByImg from '@/assets/img/creatBy.png'; | import creatByImg from '@/assets/img/creatBy.png'; | ||||
| import KFIcon from '@/components/KFIcon'; | import KFIcon from '@/components/KFIcon'; | ||||
| import { AvailableRange } from '@/enums'; | |||||
| import { type CodeConfigData } from '@/pages/CodeConfig/List'; | import { type CodeConfigData } from '@/pages/CodeConfig/List'; | ||||
| import { formatDate } from '@/utils/date'; | import { formatDate } from '@/utils/date'; | ||||
| import { Button, Flex, Typography } from 'antd'; | import { Button, Flex, Typography } from 'antd'; | ||||
| @@ -33,12 +32,12 @@ function CodeConfigItem({ item, onClick, onEdit, onRemove }: CodeConfigItemProps | |||||
| <div | <div | ||||
| className={classNames( | className={classNames( | ||||
| styles['code-config-item__tag'], | styles['code-config-item__tag'], | ||||
| item.code_repo_vis === AvailableRange.Public | |||||
| item.is_public | |||||
| ? styles['code-config-item__tag--public'] | ? styles['code-config-item__tag--public'] | ||||
| : styles['code-config-item__tag--private'], | : styles['code-config-item__tag--private'], | ||||
| )} | )} | ||||
| > | > | ||||
| {item.code_repo_vis === AvailableRange.Public ? '公开' : '私有'} | |||||
| {item.is_public ? '公开' : '私有'} | |||||
| </div> | </div> | ||||
| <Button | <Button | ||||
| type="text" | type="text" | ||||
| @@ -28,9 +28,33 @@ | |||||
| border-radius: 4px; | border-radius: 4px; | ||||
| } | } | ||||
| &__praise { | |||||
| display: flex; | |||||
| align-items: center; | |||||
| justify-content: center; | |||||
| width: 70px; | |||||
| height: 28px; | |||||
| margin-left: auto; | |||||
| color: @text-color-tertiary; | |||||
| font-size: 13px; | |||||
| background: #ffffff; | |||||
| border: 1px solid rgba(22, 100, 255, 0.11); | |||||
| border-radius: 4px; | |||||
| cursor: pointer; | |||||
| &:hover { | |||||
| border-color: .addAlpha(@primary-color, 0.5) []; | |||||
| } | |||||
| &--praised { | |||||
| color: @primary-color; | |||||
| } | |||||
| } | |||||
| :global { | :global { | ||||
| .ant-btn-dangerous { | .ant-btn-dangerous { | ||||
| background-color: transparent !important; | |||||
| background-color: .addAlpha(@error-color, 0.06) [] !important; | |||||
| border-color: .addAlpha(@error-color, 0.11) [] !important; | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -13,12 +13,14 @@ import { | |||||
| } from '@/pages/Dataset/config'; | } from '@/pages/Dataset/config'; | ||||
| import GraphLegend from '@/pages/Model/components/GraphLegend'; | import GraphLegend from '@/pages/Model/components/GraphLegend'; | ||||
| import ModelEvolution from '@/pages/Model/components/ModelEvolution'; | import ModelEvolution from '@/pages/Model/components/ModelEvolution'; | ||||
| import { praiseResourceReq, unpraiseResourceReq } from '@/services/dataset'; | |||||
| import { VersionChangedMessage } from '@/utils/constant'; | import { VersionChangedMessage } from '@/utils/constant'; | ||||
| import { openAntdModal } from '@/utils/modal'; | import { openAntdModal } from '@/utils/modal'; | ||||
| import { to } from '@/utils/promise'; | import { to } from '@/utils/promise'; | ||||
| import { modalConfirm } from '@/utils/ui'; | import { modalConfirm } from '@/utils/ui'; | ||||
| import { useParams, useSearchParams } from '@umijs/max'; | import { useParams, useSearchParams } from '@umijs/max'; | ||||
| import { App, Button, Flex, Select, Tabs } from 'antd'; | import { App, Button, Flex, Select, Tabs } from 'antd'; | ||||
| import classNames from 'classnames'; | |||||
| import { useCallback, useEffect, useState } from 'react'; | import { useCallback, useEffect, useState } from 'react'; | ||||
| import AddVersionModal from '../AddVersionModal'; | import AddVersionModal from '../AddVersionModal'; | ||||
| import ResourceIntro from '../ResourceIntro'; | import ResourceIntro from '../ResourceIntro'; | ||||
| @@ -189,6 +191,20 @@ const ResourceInfo = ({ resourceType }: ResourceInfoProps) => { | |||||
| }); | }); | ||||
| }; | }; | ||||
| // 处理点赞 | |||||
| const handlePraise = async () => { | |||||
| const request = info.praised === true ? unpraiseResourceReq : praiseResourceReq; | |||||
| const [res] = await to(request(info.id)); | |||||
| if (res) { | |||||
| message.success('操作成功'); | |||||
| setInfo({ | |||||
| ...info, | |||||
| praised: !info.praised, | |||||
| praises_count: info.praised ? info.praises_count - 1 : info.praises_count + 1, | |||||
| }); | |||||
| } | |||||
| }; | |||||
| const items = [ | const items = [ | ||||
| { | { | ||||
| key: ResourceInfoTabKeys.Introduction, | key: ResourceInfoTabKeys.Introduction, | ||||
| @@ -248,6 +264,19 @@ const ResourceInfo = ({ resourceType }: ResourceInfoProps) => { | |||||
| {(info[tagPropertyName] as string) || '--'} | {(info[tagPropertyName] as string) || '--'} | ||||
| </div> | </div> | ||||
| )} | )} | ||||
| <div | |||||
| className={classNames(styles['resource-info__top__praise'], { | |||||
| [styles['resource-info__top__praise--praised']]: info.praised, | |||||
| })} | |||||
| onClick={handlePraise} | |||||
| > | |||||
| <KFIcon | |||||
| type={info.praised ? 'icon-dianzanhou' : 'icon-dianzan'} | |||||
| font={16} | |||||
| style={{ marginRight: '3px' }} | |||||
| /> | |||||
| <span>{info.praises_count}</span> | |||||
| </div> | |||||
| </Flex> | </Flex> | ||||
| <Flex align="center"> | <Flex align="center"> | ||||
| <span style={{ marginRight: '10px' }}>版本号:</span> | <span style={{ marginRight: '10px' }}>版本号:</span> | ||||
| @@ -262,12 +291,17 @@ const ResourceInfo = ({ resourceType }: ResourceInfoProps) => { | |||||
| <Button type="default" onClick={showModal} icon={<KFIcon type="icon-xinjian2" />}> | <Button type="default" onClick={showModal} icon={<KFIcon type="icon-xinjian2" />}> | ||||
| 创建新版本 | 创建新版本 | ||||
| </Button> | </Button> | ||||
| <Button type="default" style={{ marginLeft: '20px' }} onClick={showVersionSelector}> | |||||
| <Button | |||||
| type="default" | |||||
| style={{ marginLeft: '20px' }} | |||||
| icon={<KFIcon type="icon-banbenduibi" />} | |||||
| onClick={showVersionSelector} | |||||
| > | |||||
| 版本对比 | 版本对比 | ||||
| </Button> | </Button> | ||||
| <Button | <Button | ||||
| type="default" | type="default" | ||||
| style={{ marginLeft: 'auto', marginRight: 0 }} | |||||
| style={{ marginLeft: '20px' }} | |||||
| onClick={handleDelete} | onClick={handleDelete} | ||||
| icon={<KFIcon type="icon-shanchu" />} | icon={<KFIcon type="icon-shanchu" />} | ||||
| disabled={!version} | disabled={!version} | ||||
| @@ -7,12 +7,18 @@ | |||||
| border-radius: 4px; | border-radius: 4px; | ||||
| cursor: pointer; | cursor: pointer; | ||||
| @media screen and (max-width: 1860px) { | |||||
| @media screen and (max-width: 1860px) and (min-width: 1601px) { | |||||
| & { | & { | ||||
| width: calc(33.33% - 13.33px); | width: calc(33.33% - 13.33px); | ||||
| } | } | ||||
| } | } | ||||
| @media screen and (max-width: 1600px) { | |||||
| & { | |||||
| width: calc(50% - 10px); | |||||
| } | |||||
| } | |||||
| &:hover { | &:hover { | ||||
| border-color: @primary-color; | border-color: @primary-color; | ||||
| box-shadow: 0px 0px 6px 1px rgba(0, 0, 0, 0.1); | box-shadow: 0px 0px 6px 1px rgba(0, 0, 0, 0.1); | ||||
| @@ -55,10 +61,20 @@ | |||||
| } | } | ||||
| &__time { | &__time { | ||||
| display: flex; | display: flex; | ||||
| flex: 0 1 content; | |||||
| align-items: center; | align-items: center; | ||||
| width: 100%; | |||||
| color: #808080; | |||||
| min-width: 0; | |||||
| color: @text-color-tertiary; | |||||
| font-size: 13px; | font-size: 13px; | ||||
| &__separator { | |||||
| width: 1px; | |||||
| height: 9px; | |||||
| margin: 0 8px; | |||||
| background-color: rgba(205, 206, 209, 0.5); | |||||
| } | |||||
| &__praise { | |||||
| margin-left: 4px; | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| @@ -14,6 +14,10 @@ type ResourceItemProps = { | |||||
| }; | }; | ||||
| function ResourceItem({ item, isPublic, onClick, onRemove }: ResourceItemProps) { | function ResourceItem({ item, isPublic, onClick, onRemove }: ResourceItemProps) { | ||||
| const timeAgo = `更新于${ | |||||
| item.update_time ? formatDate(item.update_time, 'YYYY-MM-DD') : item.time_ago ?? '' | |||||
| }`; | |||||
| const create_by = item.create_by ?? ''; | |||||
| return ( | return ( | ||||
| <div className={styles['resource-item']} onClick={() => onClick(item)}> | <div className={styles['resource-item']} onClick={() => onClick(item)}> | ||||
| <Flex justify="space-between" align="center" style={{ marginBottom: '20px', height: '32px' }}> | <Flex justify="space-between" align="center" style={{ marginBottom: '20px', height: '32px' }}> | ||||
| @@ -37,7 +41,7 @@ function ResourceItem({ item, isPublic, onClick, onRemove }: ResourceItemProps) | |||||
| )} | )} | ||||
| </Flex> | </Flex> | ||||
| <div className={styles['resource-item__description']}>{item.description}</div> | <div className={styles['resource-item__description']}>{item.description}</div> | ||||
| <Flex justify="space-between"> | |||||
| <Flex justify="space-between" gap={'0 8px'}> | |||||
| <div className={styles['resource-item__time']}> | <div className={styles['resource-item__time']}> | ||||
| <img | <img | ||||
| style={{ width: '17px', marginRight: '6px' }} | style={{ width: '17px', marginRight: '6px' }} | ||||
| @@ -45,14 +49,14 @@ function ResourceItem({ item, isPublic, onClick, onRemove }: ResourceItemProps) | |||||
| draggable={false} | draggable={false} | ||||
| alt="" | alt="" | ||||
| /> | /> | ||||
| <span>{item.create_by ?? ''}</span> | |||||
| <Typography.Text ellipsis={{ tooltip: create_by }}>{create_by}</Typography.Text> | |||||
| </div> | </div> | ||||
| <div className={styles['resource-item__time']}> | <div className={styles['resource-item__time']}> | ||||
| <img style={{ width: '12px', marginRight: '5px' }} src={clock} draggable={false} alt="" /> | <img style={{ width: '12px', marginRight: '5px' }} src={clock} draggable={false} alt="" /> | ||||
| <span> | |||||
| {'最近更新: '} | |||||
| {item.update_time ? formatDate(item.update_time, 'YYYY-MM-DD') : item.time_ago ?? ''} | |||||
| </span> | |||||
| <Typography.Text ellipsis={{ tooltip: timeAgo }}>{timeAgo}</Typography.Text> | |||||
| <div className={styles['resource-item__time__separator']}></div> | |||||
| <KFIcon type="icon-dianzan" font={16} /> | |||||
| <div className={styles['resource-item__time__praise']}>{item.praises_count}</div> | |||||
| </div> | </div> | ||||
| </Flex> | </Flex> | ||||
| </div> | </div> | ||||
| @@ -162,6 +162,8 @@ export interface ResourceData { | |||||
| usage?: string; | usage?: string; | ||||
| relative_paths?: string; | relative_paths?: string; | ||||
| train_task?: TrainTask; // 训练任务 | train_task?: TrainTask; // 训练任务 | ||||
| praises_count: number; // 点赞数 | |||||
| praised: boolean; // 是否点赞 | |||||
| } | } | ||||
| // 数据集数据 | // 数据集数据 | ||||
| @@ -185,7 +185,7 @@ function EditorList() { | |||||
| title: '编辑器名称', | title: '编辑器名称', | ||||
| dataIndex: 'name', | dataIndex: 'name', | ||||
| key: 'name', | key: 'name', | ||||
| width: '20%', | |||||
| width: '16%', | |||||
| render: (text, record, index) => | render: (text, record, index) => | ||||
| record.url && record.status === DevEditorStatus.Running | record.url && record.status === DevEditorStatus.Running | ||||
| ? tableCellRender<EditorData>(true, TableCellValueType.Link, { | ? tableCellRender<EditorData>(true, TableCellValueType.Link, { | ||||
| @@ -197,14 +197,14 @@ function EditorList() { | |||||
| title: '计算资源', | title: '计算资源', | ||||
| dataIndex: 'computing_resource', | dataIndex: 'computing_resource', | ||||
| key: 'computing_resource', | key: 'computing_resource', | ||||
| width: 100, | |||||
| width: '12%', | |||||
| render: tableCellRender(), | render: tableCellRender(), | ||||
| }, | }, | ||||
| { | { | ||||
| title: '资源规格', | title: '资源规格', | ||||
| dataIndex: 'computing_resource_id', | dataIndex: 'computing_resource_id', | ||||
| key: 'computing_resource_id', | key: 'computing_resource_id', | ||||
| width: '20%', | |||||
| width: '12%', | |||||
| render: tableCellRender(true, TableCellValueType.Custom, { | render: tableCellRender(true, TableCellValueType.Custom, { | ||||
| format: getResourceDescription, | format: getResourceDescription, | ||||
| }), | }), | ||||
| @@ -213,36 +213,36 @@ function EditorList() { | |||||
| title: '数据集', | title: '数据集', | ||||
| dataIndex: ['dataset', 'showValue'], | dataIndex: ['dataset', 'showValue'], | ||||
| key: 'dataset', | key: 'dataset', | ||||
| width: '15%', | |||||
| width: '12%', | |||||
| render: tableCellRender(true), | render: tableCellRender(true), | ||||
| }, | }, | ||||
| { | { | ||||
| title: '模型', | title: '模型', | ||||
| dataIndex: ['model', 'showValue'], | dataIndex: ['model', 'showValue'], | ||||
| key: 'model', | key: 'model', | ||||
| width: '15%', | |||||
| width: '12%', | |||||
| render: tableCellRender(true), | render: tableCellRender(true), | ||||
| }, | }, | ||||
| { | { | ||||
| title: '镜像', | title: '镜像', | ||||
| dataIndex: ['image', 'showValue'], | dataIndex: ['image', 'showValue'], | ||||
| key: 'image', | key: 'image', | ||||
| width: '15%', | |||||
| width: '12%', | |||||
| render: tableCellRender(true), | render: tableCellRender(true), | ||||
| }, | }, | ||||
| { | { | ||||
| title: '创建者', | title: '创建者', | ||||
| dataIndex: 'update_by', | dataIndex: 'update_by', | ||||
| key: 'update_by', | key: 'update_by', | ||||
| width: '15%', | |||||
| width: '12%', | |||||
| render: tableCellRender(true), | render: tableCellRender(true), | ||||
| }, | }, | ||||
| { | { | ||||
| title: '创建时间', | title: '创建时间', | ||||
| dataIndex: 'create_time', | dataIndex: 'create_time', | ||||
| key: 'create_time', | key: 'create_time', | ||||
| width: 180, | |||||
| render: tableCellRender(false, TableCellValueType.Date), | |||||
| width: '12%', | |||||
| render: tableCellRender(true, TableCellValueType.Date), | |||||
| }, | }, | ||||
| { | { | ||||
| title: '状态', | title: '状态', | ||||
| @@ -254,7 +254,7 @@ function EditorList() { | |||||
| { | { | ||||
| title: '操作', | title: '操作', | ||||
| dataIndex: 'operation', | dataIndex: 'operation', | ||||
| width: 300, | |||||
| width: 270, | |||||
| key: 'operation', | key: 'operation', | ||||
| render: (_: any, record: EditorData) => ( | render: (_: any, record: EditorData) => ( | ||||
| <div> | <div> | ||||
| @@ -280,7 +280,7 @@ function EditorList() { | |||||
| 启动 | 启动 | ||||
| </Button> | </Button> | ||||
| )} | )} | ||||
| {record.status === DevEditorStatus.Running ? ( | |||||
| {record.status !== DevEditorStatus.Running ? ( | |||||
| <Button | <Button | ||||
| type="link" | type="link" | ||||
| size="small" | size="small" | ||||
| @@ -1,3 +1,4 @@ | |||||
| import RunDuration from '@/components/RunDuration'; | |||||
| import { ExperimentStatus } from '@/enums'; | import { ExperimentStatus } from '@/enums'; | ||||
| import { useStateRef } from '@/hooks/useStateRef'; | import { useStateRef } from '@/hooks/useStateRef'; | ||||
| import { useVisible } from '@/hooks/useVisible'; | import { useVisible } from '@/hooks/useVisible'; | ||||
| @@ -5,7 +6,7 @@ import { getExperimentIns } from '@/services/experiment/index.js'; | |||||
| import { getWorkflowById } from '@/services/pipeline/index.js'; | import { getWorkflowById } from '@/services/pipeline/index.js'; | ||||
| import themes from '@/styles/theme.less'; | import themes from '@/styles/theme.less'; | ||||
| import { fittingString, parseJsonText } from '@/utils'; | import { fittingString, parseJsonText } from '@/utils'; | ||||
| import { elapsedTime, formatDate } from '@/utils/date'; | |||||
| import { formatDate } from '@/utils/date'; | |||||
| import { to } from '@/utils/promise'; | import { to } from '@/utils/promise'; | ||||
| import G6, { Util } from '@antv/g6'; | import G6, { Util } from '@antv/g6'; | ||||
| import { Button } from 'antd'; | import { Button } from 'antd'; | ||||
| @@ -27,7 +28,6 @@ function ExperimentText() { | |||||
| const [paramsModalOpen, openParamsModal, closeParamsModal] = useVisible(false); | const [paramsModalOpen, openParamsModal, closeParamsModal] = useVisible(false); | ||||
| const [propsDrawerOpen, openPropsDrawer, closePropsDrawer, propsDrawerOpenRef] = | const [propsDrawerOpen, openPropsDrawer, closePropsDrawer, propsDrawerOpenRef] = | ||||
| useVisible(false); | useVisible(false); | ||||
| const [currentDate, setCurrentDate] = useState(); | |||||
| const navigate = useNavigate(); | const navigate = useNavigate(); | ||||
| const evtSourceRef = useRef(); | const evtSourceRef = useRef(); | ||||
| const width = 110; | const width = 110; | ||||
| @@ -60,19 +60,6 @@ function ExperimentText() { | |||||
| }; | }; | ||||
| }, []); | }, []); | ||||
| // 定时刷新耗时 | |||||
| useEffect(() => { | |||||
| if (experimentIns && !experimentIns.finish_time) { | |||||
| const timer = setInterval(() => { | |||||
| setCurrentDate(new Date()); | |||||
| console.log('定时刷新'); | |||||
| }, 1000); | |||||
| return () => { | |||||
| clearInterval(timer); | |||||
| }; | |||||
| } | |||||
| }, [experimentIns]); | |||||
| // 获取流水线模版 | // 获取流水线模版 | ||||
| const getWorkflow = async () => { | const getWorkflow = async () => { | ||||
| const [res] = await to(getWorkflowById(locationParams.workflowId)); | const [res] = await to(getWorkflowById(locationParams.workflowId)); | ||||
| @@ -100,7 +87,6 @@ function ExperimentText() { | |||||
| if (res && res.data && workflowRef.current) { | if (res && res.data && workflowRef.current) { | ||||
| setExperimentIns(res.data); | setExperimentIns(res.data); | ||||
| const { status, nodes_status, argo_ins_ns, argo_ins_name, finish_time } = res.data; | const { status, nodes_status, argo_ins_ns, argo_ins_name, finish_time } = res.data; | ||||
| setCurrentDate(new Date(finish_time)); | |||||
| const workflowData = workflowRef.current; | const workflowData = workflowRef.current; | ||||
| const experimentStatusObjs = parseJsonText(nodes_status); | const experimentStatusObjs = parseJsonText(nodes_status); | ||||
| workflowData.nodes.forEach((item) => { | workflowData.nodes.forEach((item) => { | ||||
| @@ -489,7 +475,10 @@ function ExperimentText() { | |||||
| </div> | </div> | ||||
| <div className={styles['pipeline-container__top__info']}> | <div className={styles['pipeline-container__top__info']}> | ||||
| 执行时长: | 执行时长: | ||||
| {elapsedTime(experimentIns?.create_time, experimentIns?.finish_time)} | |||||
| <RunDuration | |||||
| createTime={experimentIns?.create_time} | |||||
| finishTime={experimentIns?.finish_time} | |||||
| /> | |||||
| </div> | </div> | ||||
| <div className={styles['pipeline-container__top__info']}> | <div className={styles['pipeline-container__top__info']}> | ||||
| 状态: | 状态: | ||||
| @@ -1,10 +1,11 @@ | |||||
| import RunDuration from '@/components/RunDuration'; | |||||
| import { ExperimentStatus } from '@/enums'; | import { ExperimentStatus } from '@/enums'; | ||||
| import { experimentStatusInfo } from '@/pages/Experiment/status'; | import { experimentStatusInfo } from '@/pages/Experiment/status'; | ||||
| import { PipelineNodeModelSerialize } from '@/types'; | import { PipelineNodeModelSerialize } from '@/types'; | ||||
| import { elapsedTime, formatDate } from '@/utils/date'; | |||||
| import { formatDate } from '@/utils/date'; | |||||
| import { CloseOutlined, DatabaseOutlined, ProfileOutlined } from '@ant-design/icons'; | import { CloseOutlined, DatabaseOutlined, ProfileOutlined } from '@ant-design/icons'; | ||||
| import { Drawer, Tabs, Typography } from 'antd'; | import { Drawer, Tabs, Typography } from 'antd'; | ||||
| import { useEffect, useMemo, useState } from 'react'; | |||||
| import { useMemo } from 'react'; | |||||
| import ExperimentParameter from '../ExperimentParameter'; | import ExperimentParameter from '../ExperimentParameter'; | ||||
| import ExperimentResult from '../ExperimentResult'; | import ExperimentResult from '../ExperimentResult'; | ||||
| import LogList from '../LogList'; | import LogList from '../LogList'; | ||||
| @@ -41,22 +42,6 @@ const ExperimentDrawer = ({ | |||||
| instanceNodeStartTime, | instanceNodeStartTime, | ||||
| instanceNodeEndTime, | instanceNodeEndTime, | ||||
| }: ExperimentDrawerProps) => { | }: ExperimentDrawerProps) => { | ||||
| const [currentDate, setCurrentDate] = useState( | |||||
| instanceNodeEndTime ? new Date(instanceNodeEndTime) : new Date(), | |||||
| ); | |||||
| // 定时刷新耗时 | |||||
| useEffect(() => { | |||||
| if (!instanceNodeEndTime) { | |||||
| const timer = setInterval(() => { | |||||
| setCurrentDate(new Date()); | |||||
| }, 1000); | |||||
| return () => { | |||||
| clearInterval(timer); | |||||
| }; | |||||
| } | |||||
| }, [instanceNodeEndTime]); | |||||
| // 如果性能有问题,可以进一步拆解 | // 如果性能有问题,可以进一步拆解 | ||||
| const items = useMemo( | const items = useMemo( | ||||
| () => [ | () => [ | ||||
| @@ -158,7 +143,7 @@ const ExperimentDrawer = ({ | |||||
| </div> | </div> | ||||
| <div className={styles['experiment-drawer__info']}> | <div className={styles['experiment-drawer__info']}> | ||||
| 耗时: | 耗时: | ||||
| {elapsedTime(instanceNodeStartTime, currentDate)} | |||||
| <RunDuration createTime={instanceNodeStartTime} finishTime={instanceNodeEndTime} /> | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <Tabs | <Tabs | ||||
| @@ -1,7 +1,6 @@ | |||||
| import KFIcon from '@/components/KFIcon'; | import KFIcon from '@/components/KFIcon'; | ||||
| import { ExperimentStatus } from '@/enums'; | import { ExperimentStatus } from '@/enums'; | ||||
| import { useCheck } from '@/hooks/useCheck'; | import { useCheck } from '@/hooks/useCheck'; | ||||
| import { experimentStatusInfo } from '@/pages/Experiment/status'; | |||||
| import { | import { | ||||
| deleteManyExperimentIns, | deleteManyExperimentIns, | ||||
| deleteQueryByExperimentInsId, | deleteQueryByExperimentInsId, | ||||
| @@ -9,17 +8,17 @@ import { | |||||
| } from '@/services/experiment/index.js'; | } from '@/services/experiment/index.js'; | ||||
| import themes from '@/styles/theme.less'; | import themes from '@/styles/theme.less'; | ||||
| import { type ExperimentInstance } from '@/types'; | import { type ExperimentInstance } from '@/types'; | ||||
| import { elapsedTime, formatDate } from '@/utils/date'; | |||||
| import { to } from '@/utils/promise'; | import { to } from '@/utils/promise'; | ||||
| import { modalConfirm } from '@/utils/ui'; | import { modalConfirm } from '@/utils/ui'; | ||||
| import { DoubleRightOutlined } from '@ant-design/icons'; | import { DoubleRightOutlined } from '@ant-design/icons'; | ||||
| import { App, Button, Checkbox, ConfigProvider, Typography } from 'antd'; | |||||
| import { App, Button, Checkbox, ConfigProvider } from 'antd'; | |||||
| import classNames from 'classnames'; | import classNames from 'classnames'; | ||||
| import { useEffect, useMemo } from 'react'; | import { useEffect, useMemo } from 'react'; | ||||
| import TensorBoardStatusCell from '../TensorBoardStatus'; | import TensorBoardStatusCell from '../TensorBoardStatus'; | ||||
| import styles from './index.less'; | import styles from './index.less'; | ||||
| import ExperimentInstanceComponent from './instance'; | |||||
| type ExperimentInstanceProps = { | |||||
| type ExperimentInstanceListProps = { | |||||
| experimentInsList?: ExperimentInstance[]; | experimentInsList?: ExperimentInstance[]; | ||||
| experimentInsTotal: number; | experimentInsTotal: number; | ||||
| onClickInstance?: (instance: ExperimentInstance) => void; | onClickInstance?: (instance: ExperimentInstance) => void; | ||||
| @@ -29,7 +28,7 @@ type ExperimentInstanceProps = { | |||||
| onLoadMore?: () => void; | onLoadMore?: () => void; | ||||
| }; | }; | ||||
| function ExperimentInstanceComponent({ | |||||
| function ExperimentInstanceList({ | |||||
| experimentInsList, | experimentInsList, | ||||
| experimentInsTotal, | experimentInsTotal, | ||||
| onClickInstance, | onClickInstance, | ||||
| @@ -37,7 +36,7 @@ function ExperimentInstanceComponent({ | |||||
| onRemove, | onRemove, | ||||
| onTerminate, | onTerminate, | ||||
| onLoadMore, | onLoadMore, | ||||
| }: ExperimentInstanceProps) { | |||||
| }: ExperimentInstanceListProps) { | |||||
| const { message } = App.useApp(); | const { message } = App.useApp(); | ||||
| const allIntanceIds = useMemo(() => { | const allIntanceIds = useMemo(() => { | ||||
| return experimentInsList?.map((item) => item.id) || []; | return experimentInsList?.map((item) => item.id) || []; | ||||
| @@ -185,28 +184,16 @@ function ExperimentInstanceComponent({ | |||||
| '--' | '--' | ||||
| )} | )} | ||||
| </div> | </div> | ||||
| <div className={styles.description}> | |||||
| <div style={{ width: '50%' }}>{elapsedTime(item.create_time, item.finish_time)}</div> | |||||
| <div style={{ width: '50%' }} className={styles.startTime}> | |||||
| <Typography.Text ellipsis={{ tooltip: formatDate(item.create_time) }}> | |||||
| {formatDate(item.create_time)} | |||||
| </Typography.Text> | |||||
| </div> | |||||
| </div> | |||||
| <div className={styles.statusBox}> | |||||
| <img | |||||
| style={{ width: '17px', marginRight: '7px' }} | |||||
| src={experimentStatusInfo[item.status as ExperimentStatus]?.icon} | |||||
| draggable={false} | |||||
| alt="" | |||||
| /> | |||||
| <span | |||||
| style={{ color: experimentStatusInfo[item.status as ExperimentStatus]?.color }} | |||||
| className={styles.statusIcon} | |||||
| > | |||||
| {experimentStatusInfo[item.status as ExperimentStatus]?.label} | |||||
| </span> | |||||
| </div> | |||||
| <ExperimentInstanceComponent | |||||
| create_time={item.create_time} | |||||
| finish_time={item.finish_time} | |||||
| status={item.status as ExperimentStatus} | |||||
| argo_ins_name={item.argo_ins_name} | |||||
| argo_ins_ns={item.argo_ins_ns} | |||||
| experimentInsId={item.id} | |||||
| ></ExperimentInstanceComponent> | |||||
| <div className={styles.operation}> | <div className={styles.operation}> | ||||
| <Button | <Button | ||||
| type="link" | type="link" | ||||
| @@ -258,4 +245,4 @@ function ExperimentInstanceComponent({ | |||||
| ); | ); | ||||
| } | } | ||||
| export default ExperimentInstanceComponent; | |||||
| export default ExperimentInstanceList; | |||||
| @@ -0,0 +1,70 @@ | |||||
| import RunDuration from '@/components/RunDuration'; | |||||
| import { ExperimentStatus } from '@/enums'; | |||||
| import { useSSE, type MessageHandler } from '@/hooks/useSSE'; | |||||
| import { experimentStatusInfo } from '@/pages/Experiment/status'; | |||||
| import { ExperimentCompleted } from '@/utils/constant'; | |||||
| import { formatDate } from '@/utils/date'; | |||||
| import { Typography } from 'antd'; | |||||
| import React, { useCallback } from 'react'; | |||||
| import styles from './index.less'; | |||||
| type ExperimentInstanceProps = { | |||||
| create_time?: string; | |||||
| finish_time?: string; | |||||
| status: ExperimentStatus; | |||||
| argo_ins_name: string; | |||||
| argo_ins_ns: string; | |||||
| experimentInsId: number; | |||||
| }; | |||||
| function ExperimentInstance({ | |||||
| create_time, | |||||
| finish_time, | |||||
| status, | |||||
| argo_ins_name, | |||||
| argo_ins_ns, | |||||
| experimentInsId, | |||||
| }: ExperimentInstanceProps) { | |||||
| const handleSSEMessage: MessageHandler = useCallback( | |||||
| (experimentInsId: number, status: string, finish_time: string) => { | |||||
| window.postMessage({ | |||||
| type: ExperimentCompleted, | |||||
| payload: { | |||||
| id: experimentInsId, | |||||
| status, | |||||
| finish_time, | |||||
| }, | |||||
| }); | |||||
| }, | |||||
| [], | |||||
| ); | |||||
| useSSE(experimentInsId, status, argo_ins_name, argo_ins_ns, handleSSEMessage); | |||||
| return ( | |||||
| <React.Fragment> | |||||
| <div className={styles.description}> | |||||
| <div style={{ width: '50%' }}> | |||||
| <RunDuration createTime={create_time} finishTime={finish_time} /> | |||||
| </div> | |||||
| <div style={{ width: '50%' }} className={styles.startTime}> | |||||
| <Typography.Text ellipsis={{ tooltip: formatDate(create_time) }}> | |||||
| {formatDate(create_time)} | |||||
| </Typography.Text> | |||||
| </div> | |||||
| </div> | |||||
| <div className={styles.statusBox}> | |||||
| <img | |||||
| style={{ width: '17px', marginRight: '7px' }} | |||||
| src={experimentStatusInfo[status]?.icon} | |||||
| draggable={false} | |||||
| alt="" | |||||
| /> | |||||
| <span style={{ color: experimentStatusInfo[status]?.color }} className={styles.statusIcon}> | |||||
| {experimentStatusInfo[status]?.label} | |||||
| </span> | |||||
| </div> | |||||
| </React.Fragment> | |||||
| ); | |||||
| } | |||||
| export default ExperimentInstance; | |||||
| @@ -15,18 +15,20 @@ import { | |||||
| } from '@/services/experiment/index.js'; | } from '@/services/experiment/index.js'; | ||||
| import { getWorkflow } from '@/services/pipeline/index.js'; | import { getWorkflow } from '@/services/pipeline/index.js'; | ||||
| import themes from '@/styles/theme.less'; | import themes from '@/styles/theme.less'; | ||||
| import { ExperimentCompleted } from '@/utils/constant'; | |||||
| import { to } from '@/utils/promise'; | import { to } from '@/utils/promise'; | ||||
| import tableCellRender, { TableCellValueType } from '@/utils/table'; | import tableCellRender, { TableCellValueType } from '@/utils/table'; | ||||
| import { modalConfirm } from '@/utils/ui'; | import { modalConfirm } from '@/utils/ui'; | ||||
| import { App, Button, ConfigProvider, Dropdown, Input, Space, Table, Tooltip } from 'antd'; | import { App, Button, ConfigProvider, Dropdown, Input, Space, Table, Tooltip } from 'antd'; | ||||
| import classNames from 'classnames'; | import classNames from 'classnames'; | ||||
| import { useCallback, useEffect, useState } from 'react'; | |||||
| import { useCallback, useEffect, useRef, useState } from 'react'; | |||||
| import { useNavigate } from 'react-router-dom'; | import { useNavigate } from 'react-router-dom'; | ||||
| import { ComparisonType } from './Comparison/config'; | import { ComparisonType } from './Comparison/config'; | ||||
| import AddExperimentModal from './components/AddExperimentModal'; | import AddExperimentModal from './components/AddExperimentModal'; | ||||
| import ExperimentInstance from './components/ExperimentInstance'; | |||||
| import ExperimentInstanceList from './components/ExperimentInstanceList'; | |||||
| import styles from './index.less'; | import styles from './index.less'; | ||||
| import { experimentStatusInfo } from './status'; | import { experimentStatusInfo } from './status'; | ||||
| import { useServerTime } from '@/hooks/useServerTime'; | |||||
| // 定时器 | // 定时器 | ||||
| const timerIds = new Map(); | const timerIds = new Map(); | ||||
| @@ -36,7 +38,7 @@ function Experiment() { | |||||
| const [experimentList, setExperimentList] = useState([]); | const [experimentList, setExperimentList] = useState([]); | ||||
| const [workflowList, setWorkflowList] = useState([]); | const [workflowList, setWorkflowList] = useState([]); | ||||
| const [experimentId, setExperimentId] = useState(null); | const [experimentId, setExperimentId] = useState(null); | ||||
| const [experimentInList, setExperimentInList] = useState([]); | |||||
| const [experimentInsList, setExperimentInsList] = useState([]); | |||||
| const [expandedRowKeys, setExpandedRowKeys] = useState(null); | const [expandedRowKeys, setExpandedRowKeys] = useState(null); | ||||
| const [total, setTotal] = useState(0); | const [total, setTotal] = useState(0); | ||||
| const [isAdd, setIsAdd] = useState(true); | const [isAdd, setIsAdd] = useState(true); | ||||
| @@ -46,6 +48,7 @@ function Experiment() { | |||||
| const [cacheState, setCacheState] = useCacheState(); | const [cacheState, setCacheState] = useCacheState(); | ||||
| const [searchText, setSearchText] = useState(cacheState?.searchText); | const [searchText, setSearchText] = useState(cacheState?.searchText); | ||||
| const [inputText, setInputText] = useState(cacheState?.searchText); | const [inputText, setInputText] = useState(cacheState?.searchText); | ||||
| const [now] = useServerTime(); | |||||
| const [pagination, setPagination] = useState( | const [pagination, setPagination] = useState( | ||||
| cacheState?.pagination ?? { | cacheState?.pagination ?? { | ||||
| current: 1, | current: 1, | ||||
| @@ -53,7 +56,34 @@ function Experiment() { | |||||
| }, | }, | ||||
| ); | ); | ||||
| const { message } = App.useApp(); | const { message } = App.useApp(); | ||||
| const timerRef = useRef(); | |||||
| // 获取实验列表 | |||||
| const getExperimentList = useCallback(async () => { | |||||
| const params = { | |||||
| page: pagination.current - 1, | |||||
| size: pagination.pageSize, | |||||
| name: searchText || undefined, | |||||
| }; | |||||
| 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); | |||||
| } | |||||
| }, [pagination, searchText]); | |||||
| // 刷新实验列表状态, | |||||
| // 目前是直接刷新实验列表,后续需要优化,只刷新状态 | |||||
| const refreshExperimentList = useCallback(() => { | |||||
| getExperimentList(); | |||||
| }, [getExperimentList]); | |||||
| // 获取流水线列表 | |||||
| useEffect(() => { | useEffect(() => { | ||||
| // 获取流水线列表 | // 获取流水线列表 | ||||
| const getWorkflowList = async () => { | const getWorkflowList = async () => { | ||||
| @@ -76,28 +106,51 @@ function Experiment() { | |||||
| }, []); | }, []); | ||||
| // 获取实验列表 | // 获取实验列表 | ||||
| const getExperimentList = useCallback(async () => { | |||||
| const params = { | |||||
| page: pagination.current - 1, | |||||
| size: pagination.pageSize, | |||||
| name: searchText || undefined, | |||||
| }; | |||||
| 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); | |||||
| } | |||||
| }, [pagination, searchText]); | |||||
| useEffect(() => { | useEffect(() => { | ||||
| getExperimentList(); | getExperimentList(); | ||||
| }, [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, | |||||
| ), | |||||
| ); | |||||
| if (timerRef.current) { | |||||
| clearTimeout(timerRef.current); | |||||
| timerRef.current = undefined; | |||||
| } | |||||
| timerRef.current = setTimeout(() => { | |||||
| refreshExperimentList(); | |||||
| }, 10000); | |||||
| } | |||||
| }; | |||||
| window.addEventListener('message', handleMessage); | |||||
| return () => { | |||||
| window.removeEventListener('message', handleMessage); | |||||
| if (timerRef.current) { | |||||
| clearTimeout(timerRef.current); | |||||
| timerRef.current = undefined; | |||||
| } | |||||
| }; | |||||
| }, [refreshExperimentList]); | |||||
| // 搜索 | // 搜索 | ||||
| const onSearch = (value) => { | const onSearch = (value) => { | ||||
| setSearchText(value); | setSearchText(value); | ||||
| @@ -108,11 +161,11 @@ function Experiment() { | |||||
| }; | }; | ||||
| // 获取实验实例列表 | // 获取实验实例列表 | ||||
| const getQueryByExperiment = async (experimentId, page) => { | |||||
| const getQueryByExperiment = async (experimentId, page, size = 5) => { | |||||
| const params = { | const params = { | ||||
| experimentId: experimentId, | experimentId: experimentId, | ||||
| page: page, | page: page, | ||||
| size: 5, | |||||
| size: size, | |||||
| }; | }; | ||||
| const [res, error] = await to(getQueryByExperimentId(params)); | const [res, error] = await to(getQueryByExperimentId(params)); | ||||
| if (res && res.data) { | if (res && res.data) { | ||||
| @@ -127,10 +180,10 @@ function Experiment() { | |||||
| }; | }; | ||||
| }); | }); | ||||
| if (page === 0) { | if (page === 0) { | ||||
| setExperimentInList(list); | |||||
| setExperimentInsList(list); | |||||
| clearExperimentInTimers(); | clearExperimentInTimers(); | ||||
| } else { | } else { | ||||
| setExperimentInList((prev) => [...prev, ...list]); | |||||
| setExperimentInsList((prev) => [...prev, ...list]); | |||||
| } | } | ||||
| setExperimentInsTotal(totalElements); | setExperimentInsTotal(totalElements); | ||||
| // 获取 TensorBoard 状态 | // 获取 TensorBoard 状态 | ||||
| @@ -173,7 +226,7 @@ function Experiment() { | |||||
| }; | }; | ||||
| const [res] = await to(getTensorBoardStatusReq(params)); | const [res] = await to(getTensorBoardStatusReq(params)); | ||||
| if (res && res.data) { | if (res && res.data) { | ||||
| setExperimentInList((prevList) => { | |||||
| setExperimentInsList((prevList) => { | |||||
| return prevList.map((item) => { | return prevList.map((item) => { | ||||
| if (item.id === experimentIn.id) { | if (item.id === experimentIn.id) { | ||||
| return { | return { | ||||
| @@ -201,11 +254,11 @@ function Experiment() { | |||||
| // 展开实例 | // 展开实例 | ||||
| const expandChange = (e, record) => { | const expandChange = (e, record) => { | ||||
| clearExperimentInTimers(); | clearExperimentInTimers(); | ||||
| setExperimentInList([]); | |||||
| setExperimentInsList([]); | |||||
| if (record.id === expandedRowKeys) { | if (record.id === expandedRowKeys) { | ||||
| setExpandedRowKeys(null); | setExpandedRowKeys(null); | ||||
| } else { | } else { | ||||
| getQueryByExperiment(record.id, 0); | |||||
| getQueryByExperiment(record.id, 0, 5); | |||||
| refreshExperimentList(); | refreshExperimentList(); | ||||
| } | } | ||||
| }; | }; | ||||
| @@ -285,7 +338,7 @@ function Experiment() { | |||||
| if (res) { | if (res) { | ||||
| message.success('运行成功'); | message.success('运行成功'); | ||||
| refreshExperimentList(); | refreshExperimentList(); | ||||
| refreshExperimentIns(id); | |||||
| getQueryByExperiment(id, 0, 5); | |||||
| } | } | ||||
| }; | }; | ||||
| @@ -323,22 +376,17 @@ function Experiment() { | |||||
| } | } | ||||
| }; | }; | ||||
| // 刷新实验列表状态, | |||||
| // 目前是直接刷新实验列表,后续需要优化,只刷新状态 | |||||
| const refreshExperimentList = () => { | |||||
| getExperimentList(); | |||||
| }; | |||||
| // 实验实例终止 | // 实验实例终止 | ||||
| const handleInstanceTerminate = async (experimentIn) => { | const handleInstanceTerminate = async (experimentIn) => { | ||||
| // 刷新实验列表 | // 刷新实验列表 | ||||
| refreshExperimentList(); | refreshExperimentList(); | ||||
| setExperimentInList((prevList) => { | |||||
| setExperimentInsList((prevList) => { | |||||
| return prevList.map((item) => { | return prevList.map((item) => { | ||||
| if (item.id === experimentIn.id) { | if (item.id === experimentIn.id) { | ||||
| return { | return { | ||||
| ...item, | ...item, | ||||
| status: ExperimentStatus.Terminated, | status: ExperimentStatus.Terminated, | ||||
| finish_time: now().toISOString(), | |||||
| }; | }; | ||||
| } | } | ||||
| return item; | return item; | ||||
| @@ -367,13 +415,14 @@ function Experiment() { | |||||
| // 刷新实验实例列表 | // 刷新实验实例列表 | ||||
| const refreshExperimentIns = (experimentId) => { | const refreshExperimentIns = (experimentId) => { | ||||
| getQueryByExperiment(experimentId, 0); | |||||
| const length = experimentInsList.length; | |||||
| getQueryByExperiment(experimentId, 0, length); | |||||
| }; | }; | ||||
| // 加载更多实验实例 | // 加载更多实验实例 | ||||
| const loadMoreExperimentIns = () => { | const loadMoreExperimentIns = () => { | ||||
| const page = Math.round(experimentInList.length / 5); | |||||
| getQueryByExperiment(expandedRowKeys, page); | |||||
| const page = Math.round(experimentInsList.length / 5); | |||||
| getQueryByExperiment(expandedRowKeys, page, 5); | |||||
| }; | }; | ||||
| // 处理删除 | // 处理删除 | ||||
| @@ -544,8 +593,8 @@ function Experiment() { | |||||
| scroll={{ y: 'calc(100% - 55px)' }} | scroll={{ y: 'calc(100% - 55px)' }} | ||||
| expandable={{ | expandable={{ | ||||
| expandedRowRender: (record) => ( | expandedRowRender: (record) => ( | ||||
| <ExperimentInstance | |||||
| experimentInsList={experimentInList} | |||||
| <ExperimentInstanceList | |||||
| experimentInsList={experimentInsList} | |||||
| experimentInsTotal={experimentInsTotal} | experimentInsTotal={experimentInsTotal} | ||||
| onClickInstance={(item) => gotoInstanceInfo(item, record)} | onClickInstance={(item) => gotoInstanceInfo(item, record)} | ||||
| onClickTensorBoard={handleTensorboard} | onClickTensorBoard={handleTensorboard} | ||||
| @@ -555,7 +604,7 @@ function Experiment() { | |||||
| }} | }} | ||||
| onTerminate={handleInstanceTerminate} | onTerminate={handleInstanceTerminate} | ||||
| onLoadMore={() => loadMoreExperimentIns()} | onLoadMore={() => loadMoreExperimentIns()} | ||||
| ></ExperimentInstance> | |||||
| ></ExperimentInstanceList> | |||||
| ), | ), | ||||
| onExpand: expandChange, | onExpand: expandChange, | ||||
| expandedRowKeys: [expandedRowKeys], | expandedRowKeys: [expandedRowKeys], | ||||
| @@ -53,7 +53,7 @@ function HyperParameterInstance() { | |||||
| const info = res.data as HyperParameterInstanceData; | const info = res.data as HyperParameterInstanceData; | ||||
| const { param, node_status, argo_ins_name, argo_ins_ns, status, create_time } = info; | const { param, node_status, argo_ins_name, argo_ins_ns, status, create_time } = info; | ||||
| // 解析配置参数 | // 解析配置参数 | ||||
| const paramJson = parseJsonText(param); | |||||
| const paramJson = parseJsonText(param).data; | |||||
| if (paramJson) { | if (paramJson) { | ||||
| // 实例详情返回的参数是字符串,需要转换 | // 实例详情返回的参数是字符串,需要转换 | ||||
| if (typeof paramJson.parameters === 'string') { | if (typeof paramJson.parameters === 'string') { | ||||
| @@ -121,13 +121,13 @@ function HyperParameterInstance() { | |||||
| if (dataJson) { | if (dataJson) { | ||||
| const nodes = dataJson?.result?.object?.status?.nodes; | const nodes = dataJson?.result?.object?.status?.nodes; | ||||
| if (nodes) { | if (nodes) { | ||||
| // 节点 | |||||
| setNodes(nodes); | |||||
| const workflowStatus = Object.values(nodes).find((node: any) => | const workflowStatus = Object.values(nodes).find((node: any) => | ||||
| node.displayName.startsWith(NodePrefix), | node.displayName.startsWith(NodePrefix), | ||||
| ) as NodeStatus; | ) as NodeStatus; | ||||
| // 节点 | |||||
| setNodes(nodes); | |||||
| // 设置工作流状态 | // 设置工作流状态 | ||||
| if (workflowStatus) { | if (workflowStatus) { | ||||
| setWorkflowStatus(workflowStatus); | setWorkflowStatus(workflowStatus); | ||||
| @@ -168,6 +168,7 @@ function HyperParameterInstance() { | |||||
| className={styles['hyper-parameter-instance__basic']} | className={styles['hyper-parameter-instance__basic']} | ||||
| info={experimentInfo} | info={experimentInfo} | ||||
| runStatus={workflowStatus} | runStatus={workflowStatus} | ||||
| instanceStatus={instanceInfo?.status} | |||||
| isInstance | isInstance | ||||
| /> | /> | ||||
| ), | ), | ||||
| @@ -1,6 +1,8 @@ | |||||
| import ConfigInfo, { type BasicInfoData } from '@/components/ConfigInfo'; | import ConfigInfo, { type BasicInfoData } from '@/components/ConfigInfo'; | ||||
| import { hyperParameterOptimizedMode } from '@/enums'; | |||||
| import RunDuration from '@/components/RunDuration'; | |||||
| import { ExperimentStatus, hyperParameterOptimizedMode } from '@/enums'; | |||||
| import { useComputingResource } from '@/hooks/useComputingResource'; | import { useComputingResource } from '@/hooks/useComputingResource'; | ||||
| import ExperimentRunBasic from '@/pages/AutoML/components/ExperimentRunBasic'; | |||||
| import { experimentStatusInfo } from '@/pages/Experiment/status'; | import { experimentStatusInfo } from '@/pages/Experiment/status'; | ||||
| import { | import { | ||||
| schedulerAlgorithms, | schedulerAlgorithms, | ||||
| @@ -8,7 +10,6 @@ import { | |||||
| } from '@/pages/HyperParameter/components/CreateForm/utils'; | } from '@/pages/HyperParameter/components/CreateForm/utils'; | ||||
| import { HyperParameterData } from '@/pages/HyperParameter/types'; | import { HyperParameterData } from '@/pages/HyperParameter/types'; | ||||
| import { type NodeStatus } from '@/types'; | import { type NodeStatus } from '@/types'; | ||||
| import { elapsedTime } from '@/utils/date'; | |||||
| import { | import { | ||||
| formatCodeConfig, | formatCodeConfig, | ||||
| formatDataset, | formatDataset, | ||||
| @@ -33,12 +34,14 @@ type HyperParameterBasicProps = { | |||||
| className?: string; | className?: string; | ||||
| isInstance?: boolean; | isInstance?: boolean; | ||||
| runStatus?: NodeStatus; | runStatus?: NodeStatus; | ||||
| instanceStatus?: ExperimentStatus; | |||||
| }; | }; | ||||
| function HyperParameterBasic({ | function HyperParameterBasic({ | ||||
| info, | info, | ||||
| className, | className, | ||||
| runStatus, | runStatus, | ||||
| instanceStatus, | |||||
| isInstance = false, | isInstance = false, | ||||
| }: HyperParameterBasicProps) { | }: HyperParameterBasicProps) { | ||||
| const getResourceDescription = useComputingResource()[1]; | const getResourceDescription = useComputingResource()[1]; | ||||
| @@ -155,7 +158,7 @@ function HyperParameterBasic({ | |||||
| }, | }, | ||||
| { | { | ||||
| label: '执行时长', | label: '执行时长', | ||||
| value: elapsedTime(info.create_time, runStatus.finishedAt), | |||||
| value: <RunDuration createTime={info.create_time} finishTime={runStatus.finishedAt} />, | |||||
| ellipsis: true, | ellipsis: true, | ||||
| }, | }, | ||||
| { | { | ||||
| @@ -187,11 +190,10 @@ function HyperParameterBasic({ | |||||
| return ( | return ( | ||||
| <div className={classNames(styles['hyper-parameter-basic'], className)}> | <div className={classNames(styles['hyper-parameter-basic'], className)}> | ||||
| {isInstance && runStatus && ( | {isInstance && runStatus && ( | ||||
| <ConfigInfo | |||||
| title="运行信息" | |||||
| datas={instanceDatas} | |||||
| labelWidth={70} | |||||
| style={{ marginBottom: '20px' }} | |||||
| <ExperimentRunBasic | |||||
| create_time={info?.create_time} | |||||
| runStatus={runStatus} | |||||
| instanceStatus={instanceStatus} | |||||
| /> | /> | ||||
| )} | )} | ||||
| {!isInstance && ( | {!isInstance && ( | ||||
| @@ -164,20 +164,21 @@ function MirrorInfo() { | |||||
| title: '镜像版本', | title: '镜像版本', | ||||
| dataIndex: 'tag_name', | dataIndex: 'tag_name', | ||||
| key: 'tag_name', | key: 'tag_name', | ||||
| width: '25%', | |||||
| width: '30%', | |||||
| render: tableCellRender(), | render: tableCellRender(), | ||||
| }, | }, | ||||
| { | { | ||||
| title: '镜像地址', | title: '镜像地址', | ||||
| dataIndex: 'url', | dataIndex: 'url', | ||||
| key: 'url', | key: 'url', | ||||
| width: '25%', | |||||
| width: '40%', | |||||
| render: tableCellRender('auto', TableCellValueType.Text, { copyable: true }), | render: tableCellRender('auto', TableCellValueType.Text, { copyable: true }), | ||||
| }, | }, | ||||
| { | { | ||||
| title: '版本描述', | title: '版本描述', | ||||
| dataIndex: 'description', | dataIndex: 'description', | ||||
| key: 'description', | key: 'description', | ||||
| width: '30%', | |||||
| render: tableCellRender(true), | render: tableCellRender(true), | ||||
| }, | }, | ||||
| { | { | ||||
| @@ -36,6 +36,13 @@ | |||||
| .ant-btn-variant-text { | .ant-btn-variant-text { | ||||
| color: #565658; | color: #565658; | ||||
| } | } | ||||
| .anticon-question-circle { | |||||
| margin-top: -12px; | |||||
| margin-left: 1px !important; | |||||
| color: @text-color-tertiary !important; | |||||
| font-size: 12px !important; | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -3,7 +3,6 @@ | |||||
| * @Date: 2024-04-16 13:58:08 | * @Date: 2024-04-16 13:58:08 | ||||
| * @Description: 创建服务版本 | * @Description: 创建服务版本 | ||||
| */ | */ | ||||
| import CodeSelect from '@/components/CodeSelect'; | |||||
| import PageTitle from '@/components/PageTitle'; | import PageTitle from '@/components/PageTitle'; | ||||
| import ParameterSelect from '@/components/ParameterSelect'; | import ParameterSelect from '@/components/ParameterSelect'; | ||||
| import ResourceSelect, { | import ResourceSelect, { | ||||
| @@ -36,7 +35,7 @@ export type FormData = { | |||||
| description: string; // 描述 | description: string; // 描述 | ||||
| model: ParameterInputObject; // 模型 | model: ParameterInputObject; // 模型 | ||||
| image: ParameterInputObject; // 镜像 | image: ParameterInputObject; // 镜像 | ||||
| code_config: ParameterInputObject; // 代码 | |||||
| // code_config: ParameterInputObject; // 代码 | |||||
| resource: string; // 资源规格 | resource: string; // 资源规格 | ||||
| replicas: string; // 副本数量 | replicas: string; // 副本数量 | ||||
| mount_path: string; // 模型路径 | mount_path: string; // 模型路径 | ||||
| @@ -75,28 +74,23 @@ function CreateServiceVersion() { | |||||
| setOperationType(res.operationType); | setOperationType(res.operationType); | ||||
| setLastPage(res.lastPage); | setLastPage(res.lastPage); | ||||
| setVersionInfo(res); | setVersionInfo(res); | ||||
| let model, codeConfig, envVariables; | |||||
| let model, envVariables; | |||||
| // 模型 | |||||
| if (res.model && typeof res.model === 'object') { | if (res.model && typeof res.model === 'object') { | ||||
| model = changePropertyName(res.model, { show_value: 'showValue' }); | model = changePropertyName(res.model, { show_value: 'showValue' }); | ||||
| // 接口返回是数据没有 value 值,但是 form 需要 value | // 接口返回是数据没有 value 值,但是 form 需要 value | ||||
| model.value = model.showValue; | model.value = model.showValue; | ||||
| } | } | ||||
| if (res.code_config && typeof res.code_config === 'object') { | |||||
| codeConfig = changePropertyName(res.code_config, { show_value: 'showValue' }); | |||||
| // 接口返回是数据没有 value 值,但是 form 需要 value | |||||
| codeConfig.value = codeConfig.showValue; | |||||
| } | |||||
| // 环境变量 | |||||
| if (res.env_variables && typeof res.env_variables === 'object') { | if (res.env_variables && typeof res.env_variables === 'object') { | ||||
| envVariables = Object.entries(res.env_variables).map(([key, value]) => ({ | envVariables = Object.entries(res.env_variables).map(([key, value]) => ({ | ||||
| key, | key, | ||||
| value, | value, | ||||
| })); | })); | ||||
| } | } | ||||
| const formData = { | const formData = { | ||||
| ...omit(res, 'model', 'code_config', 'env_variables'), | |||||
| ...omit(res, 'model', 'env_variables'), | |||||
| model: model, | model: model, | ||||
| code_config: codeConfig, | |||||
| env_variables: envVariables, | env_variables: envVariables, | ||||
| }; | }; | ||||
| form.setFieldsValue(formData); | form.setFieldsValue(formData); | ||||
| @@ -124,7 +118,6 @@ function CreateServiceVersion() { | |||||
| const createServiceVersion = async (formData: FormData) => { | const createServiceVersion = async (formData: FormData) => { | ||||
| const envList = formData['env_variables']; | const envList = formData['env_variables']; | ||||
| const model = formData['model']; | const model = formData['model']; | ||||
| const codeConfig = formData['code_config']; | |||||
| const envVariables = envList?.reduce((acc, cur) => { | const envVariables = envList?.reduce((acc, cur) => { | ||||
| acc[cur.key] = cur.value; | acc[cur.key] = cur.value; | ||||
| return acc; | return acc; | ||||
| @@ -132,18 +125,13 @@ function CreateServiceVersion() { | |||||
| // 根据后台要求,修改表单数据 | // 根据后台要求,修改表单数据 | ||||
| const object = { | const object = { | ||||
| ...omit(formData, ['replicas', 'env_variables', 'model', 'code_config']), | |||||
| ...omit(formData, ['replicas', 'env_variables', 'model']), | |||||
| replicas: Number(formData.replicas), | replicas: Number(formData.replicas), | ||||
| env_variables: envVariables, | env_variables: envVariables, | ||||
| model: changePropertyName( | model: changePropertyName( | ||||
| pick(model, ['id', 'name', 'version', 'path', 'identifier', 'owner', 'showValue']), | pick(model, ['id', 'name', 'version', 'path', 'identifier', 'owner', 'showValue']), | ||||
| { showValue: 'show_value' }, | { showValue: 'show_value' }, | ||||
| ), | ), | ||||
| code_config: codeConfig | |||||
| ? changePropertyName(pick(codeConfig, ['code_path', 'branch', 'showValue']), { | |||||
| showValue: 'show_value', | |||||
| }) | |||||
| : undefined, | |||||
| service_id: serviceId, | service_id: serviceId, | ||||
| }; | }; | ||||
| @@ -335,9 +323,13 @@ function CreateServiceVersion() { | |||||
| </Form.Item> | </Form.Item> | ||||
| </Col> | </Col> | ||||
| </Row> | </Row> | ||||
| <Row gutter={8}> | |||||
| {/* <Row gutter={8}> | |||||
| <Col span={10}> | <Col span={10}> | ||||
| <Form.Item label="代码配置" name="code_config"> | |||||
| <Form.Item | |||||
| label="代码配置" | |||||
| name="code_config" | |||||
| tooltip="此处代码配置为可引用的代码配置,具体代码编写指南见用户手册中服务部署模块" | |||||
| > | |||||
| <CodeSelect | <CodeSelect | ||||
| placeholder="请选择代码配置" | placeholder="请选择代码配置" | ||||
| canInput={false} | canInput={false} | ||||
| @@ -346,7 +338,7 @@ function CreateServiceVersion() { | |||||
| /> | /> | ||||
| </Form.Item> | </Form.Item> | ||||
| </Col> | </Col> | ||||
| </Row> | |||||
| </Row> */} | |||||
| <Row gutter={8}> | <Row gutter={8}> | ||||
| <Col span={10}> | <Col span={10}> | ||||
| <Form.Item | <Form.Item | ||||
| @@ -284,14 +284,7 @@ function ServiceInfo() { | |||||
| render: tableCellRender(true), | render: tableCellRender(true), | ||||
| }, | }, | ||||
| { | { | ||||
| title: '状态', | |||||
| dataIndex: 'run_state', | |||||
| key: 'run_state', | |||||
| width: '20%', | |||||
| render: ServiceRunStatusCell, | |||||
| }, | |||||
| { | |||||
| title: '版本镜像', | |||||
| title: '镜像版本', | |||||
| dataIndex: ['image', 'showValue'], | dataIndex: ['image', 'showValue'], | ||||
| key: 'image', | key: 'image', | ||||
| width: '20%', | width: '20%', | ||||
| @@ -313,6 +306,13 @@ function ServiceInfo() { | |||||
| format: getResourceDescription, | format: getResourceDescription, | ||||
| }), | }), | ||||
| }, | }, | ||||
| { | |||||
| title: '状态', | |||||
| dataIndex: 'run_state', | |||||
| key: 'run_state', | |||||
| width: '20%', | |||||
| render: ServiceRunStatusCell, | |||||
| }, | |||||
| { | { | ||||
| title: '操作', | title: '操作', | ||||
| dataIndex: 'operation', | dataIndex: 'operation', | ||||
| @@ -3,7 +3,7 @@ import { ServiceRunStatus } from '@/enums'; | |||||
| import { useComputingResource } from '@/hooks/useComputingResource'; | import { useComputingResource } from '@/hooks/useComputingResource'; | ||||
| import { ServiceVersionData } from '@/pages/ModelDeployment/types'; | import { ServiceVersionData } from '@/pages/ModelDeployment/types'; | ||||
| import { formatDate } from '@/utils/date'; | import { formatDate } from '@/utils/date'; | ||||
| import { formatCodeConfig, formatMirror, formatModel } from '@/utils/format'; | |||||
| import { formatMirror, formatModel } from '@/utils/format'; | |||||
| import { Flex } from 'antd'; | import { Flex } from 'antd'; | ||||
| import ModelDeployStatusCell from '../ModelDeployStatusCell'; | import ModelDeployStatusCell from '../ModelDeployStatusCell'; | ||||
| @@ -47,11 +47,11 @@ function VersionBasicInfo({ info }: BasicInfoProps) { | |||||
| label: '版本名称', | label: '版本名称', | ||||
| value: info?.version, | value: info?.version, | ||||
| }, | }, | ||||
| { | |||||
| label: '代码配置', | |||||
| value: info?.code_config, | |||||
| format: formatCodeConfig, | |||||
| }, | |||||
| // { | |||||
| // label: '代码配置', | |||||
| // value: info?.code_config, | |||||
| // format: formatCodeConfig, | |||||
| // }, | |||||
| { | { | ||||
| label: '镜像', | label: '镜像', | ||||
| value: info?.image, | value: info?.image, | ||||
| @@ -11,6 +11,7 @@ | |||||
| flex: 1; | flex: 1; | ||||
| flex-direction: column; | flex-direction: column; | ||||
| align-items: center; | align-items: center; | ||||
| padding: 0 20px; | |||||
| &--border { | &--border { | ||||
| border-right: 1px solid @border-color; | border-right: 1px solid @border-color; | ||||
| @@ -1,3 +1,5 @@ | |||||
| import { formatNumber } from '@/utils/format'; | |||||
| import { Typography } from 'antd'; | |||||
| import classNames from 'classnames'; | import classNames from 'classnames'; | ||||
| import styles from './index.less'; | import styles from './index.less'; | ||||
| @@ -10,11 +12,11 @@ function Statistics({ remaining, consuming }: StatisticsProps) { | |||||
| const items = [ | const items = [ | ||||
| { | { | ||||
| title: '当前可用算力积分(分)', | title: '当前可用算力积分(分)', | ||||
| value: remaining ?? '-', | |||||
| value: remaining, | |||||
| }, | }, | ||||
| { | { | ||||
| title: '总消耗算力积分(分)', | title: '总消耗算力积分(分)', | ||||
| value: consuming ?? '-', | |||||
| value: consuming, | |||||
| }, | }, | ||||
| ]; | ]; | ||||
| @@ -27,7 +29,12 @@ function Statistics({ remaining, consuming }: StatisticsProps) { | |||||
| [styles['statistics__item--border']]: index === 0, | [styles['statistics__item--border']]: index === 0, | ||||
| })} | })} | ||||
| > | > | ||||
| <span className={styles['statistics__item__value']}>{item.value}</span> | |||||
| <Typography.Paragraph | |||||
| ellipsis={{ tooltip: formatNumber(item.value) }} | |||||
| className={styles['statistics__item__value']} | |||||
| > | |||||
| {formatNumber(item.value)} | |||||
| </Typography.Paragraph> | |||||
| <span className={styles['statistics__item__title']}>{item.title}</span> | <span className={styles['statistics__item__title']}>{item.title}</span> | ||||
| </div> | </div> | ||||
| ))} | ))} | ||||
| @@ -1,5 +1,6 @@ | |||||
| import { PointsStatistics } from '@/pages/Points/index'; | import { PointsStatistics } from '@/pages/Points/index'; | ||||
| import { getPointsStatisticsReq } from '@/services/points'; | import { getPointsStatisticsReq } from '@/services/points'; | ||||
| import { formatNumber } from '@/utils/format'; | |||||
| import { to } from '@/utils/promise'; | import { to } from '@/utils/promise'; | ||||
| import { useNavigate } from '@umijs/max'; | import { useNavigate } from '@umijs/max'; | ||||
| import { Typography } from 'antd'; | import { Typography } from 'antd'; | ||||
| @@ -22,14 +23,16 @@ function UserPoints() { | |||||
| getPointsStatistics(); | getPointsStatistics(); | ||||
| }, []); | }, []); | ||||
| const userCredit = formatNumber(statistics?.userCredit); | |||||
| return ( | return ( | ||||
| <div className={styles['user-points']}> | <div className={styles['user-points']}> | ||||
| <div className={styles['user-points__label']}>当前可用算力积分</div> | <div className={styles['user-points__label']}>当前可用算力积分</div> | ||||
| <Typography.Paragraph | <Typography.Paragraph | ||||
| className={styles['user-points__value']} | className={styles['user-points__value']} | ||||
| ellipsis={{ tooltip: statistics?.userCredit ?? '--' }} | |||||
| ellipsis={{ tooltip: userCredit }} | |||||
| > | > | ||||
| {statistics?.userCredit ?? '--'} | |||||
| {userCredit} | |||||
| </Typography.Paragraph> | </Typography.Paragraph> | ||||
| <div | <div | ||||
| className={styles['user-points__button']} | className={styles['user-points__button']} | ||||
| @@ -187,3 +187,18 @@ export function deleteUploadFileReq(params) { | |||||
| params, | params, | ||||
| }); | }); | ||||
| } | } | ||||
| // 点赞 | |||||
| export function praiseResourceReq(id) { | |||||
| return request(`/api/mmp/newmodel/praise/${id}`, { | |||||
| method: 'POST', | |||||
| }); | |||||
| } | |||||
| // 取消点赞 | |||||
| export function unpraiseResourceReq(id) { | |||||
| return request(`/api/mmp/newmodel/unpraise/${id}`, { | |||||
| method: 'DELETE', | |||||
| }); | |||||
| } | |||||
| @@ -151,3 +151,11 @@ export function getExpMetricsReq(data) { | |||||
| data, | data, | ||||
| }); | }); | ||||
| } | } | ||||
| // 获取服务器的当前时间 | |||||
| export function getSeverTimeReq(data) { | |||||
| return request(`/api/mmp/experimentIns/time`, { | |||||
| method: 'GET', | |||||
| }); | |||||
| } | |||||
| @@ -165,12 +165,12 @@ function CodeConfigItem({ item, onClick }: CodeConfigItemProps) { | |||||
| <div | <div | ||||
| className={classNames( | className={classNames( | ||||
| styles['code-config-item__tag'], | styles['code-config-item__tag'], | ||||
| item.code_repo_vis === AvailableRange.Public | |||||
| item.is_public | |||||
| ? styles['code-config-item__tag--public'] | ? styles['code-config-item__tag--public'] | ||||
| : styles['code-config-item__tag--private'], | : styles['code-config-item__tag--private'], | ||||
| )} | )} | ||||
| > | > | ||||
| {item.code_repo_vis === AvailableRange.Public ? '公开' : '私有'} | |||||
| {item.is_public ? '公开' : '私有'} | |||||
| </div> | </div> | ||||
| </Flex> | </Flex> | ||||
| <Typography.Paragraph | <Typography.Paragraph | ||||
| @@ -450,7 +450,7 @@ export const codeListData = { | |||||
| { | { | ||||
| id: 2, | id: 2, | ||||
| code_repo_name: '介电材料代码', | code_repo_name: '介电材料代码', | ||||
| code_repo_vis: 1, | |||||
| is_public: true, | |||||
| git_url: 'https://gitlink.org.cn/fuli/ML_for_Materials.git', | git_url: 'https://gitlink.org.cn/fuli/ML_for_Materials.git', | ||||
| git_branch: 'master', | git_branch: 'master', | ||||
| verify_mode: null, | verify_mode: null, | ||||
| @@ -466,7 +466,7 @@ export const codeListData = { | |||||
| { | { | ||||
| id: 3, | id: 3, | ||||
| code_repo_name: '生物活性材料代码', | code_repo_name: '生物活性材料代码', | ||||
| code_repo_vis: 1, | |||||
| is_public: true, | |||||
| git_url: 'https://gitlink.org.cn/zhaoyihan/test_mole_pre.git', | git_url: 'https://gitlink.org.cn/zhaoyihan/test_mole_pre.git', | ||||
| git_branch: 'parse_dataset', | git_branch: 'parse_dataset', | ||||
| verify_mode: null, | verify_mode: null, | ||||
| @@ -482,7 +482,7 @@ export const codeListData = { | |||||
| { | { | ||||
| id: 4, | id: 4, | ||||
| code_repo_name: '数据处理', | code_repo_name: '数据处理', | ||||
| code_repo_vis: 1, | |||||
| is_public: true, | |||||
| git_url: 'https://openi.pcl.ac.cn/somunslotus/somun202304241505581.git', | git_url: 'https://openi.pcl.ac.cn/somunslotus/somun202304241505581.git', | ||||
| git_branch: 'train_ci_test', | git_branch: 'train_ci_test', | ||||
| verify_mode: null, | verify_mode: null, | ||||
| @@ -498,7 +498,7 @@ export const codeListData = { | |||||
| { | { | ||||
| id: 5, | id: 5, | ||||
| code_repo_name: '手写体识别部署', | code_repo_name: '手写体识别部署', | ||||
| code_repo_vis: 1, | |||||
| is_public: true, | |||||
| git_url: 'https://gitlink.org.cn/somunslotus/mnist-inference.git', | git_url: 'https://gitlink.org.cn/somunslotus/mnist-inference.git', | ||||
| git_branch: 'master', | git_branch: 'master', | ||||
| verify_mode: null, | verify_mode: null, | ||||
| @@ -514,7 +514,7 @@ export const codeListData = { | |||||
| { | { | ||||
| id: 7, | id: 7, | ||||
| code_repo_name: '手写体识别训练', | code_repo_name: '手写体识别训练', | ||||
| code_repo_vis: 1, | |||||
| is_public: true, | |||||
| git_url: 'https://openi.pcl.ac.cn/somunslotus/somun202304241505581.git', | git_url: 'https://openi.pcl.ac.cn/somunslotus/somun202304241505581.git', | ||||
| git_branch: 'train_ci_test', | git_branch: 'train_ci_test', | ||||
| verify_mode: null, | verify_mode: null, | ||||
| @@ -17,3 +17,6 @@ export const VersionChangedMessage = 'versionChanged'; | |||||
| // 创建服务成功消息,去创建服务版本 | // 创建服务成功消息,去创建服务版本 | ||||
| export const ServiceCreatedMessage = 'serviceCreated'; | export const ServiceCreatedMessage = 'serviceCreated'; | ||||
| // 实验完成 | |||||
| export const ExperimentCompleted = 'ExperimentCompleted'; | |||||
| @@ -1,3 +1,4 @@ | |||||
| import { now } from '@/hooks/useServerTime'; | |||||
| import dayjs from 'dayjs'; | import dayjs from 'dayjs'; | ||||
| /** | /** | ||||
| @@ -13,7 +14,7 @@ export const elapsedTime = (begin?: string | Date | null, end?: string | Date | | |||||
| } | } | ||||
| const beginDate = dayjs(begin); | const beginDate = dayjs(begin); | ||||
| const endDate = end === undefined || end === null ? dayjs() : dayjs(end); | |||||
| const endDate = end === undefined || end === null ? dayjs(now()) : dayjs(end); | |||||
| if (!beginDate.isValid() || !endDate.isValid()) { | if (!beginDate.isValid() || !endDate.isValid()) { | ||||
| return '--'; | return '--'; | ||||
| } | } | ||||
| @@ -193,3 +193,19 @@ export const formatEnum = (options: EnumOptions[]): FormatEnumFunc => { | |||||
| return option && option.label ? option.label : '--'; | return option && option.label ? option.label : '--'; | ||||
| }; | }; | ||||
| }; | }; | ||||
| /** | |||||
| * 格式化数字 | |||||
| * | |||||
| * @param value - 值、 | |||||
| * @param toFixed - 保留几位小数 | |||||
| * @return 格式化的数字,如果不是数字,返回 '--' | |||||
| */ | |||||
| export const formatNumber = (value?: number | null, toFixed?: number) : number | string => { | |||||
| if (typeof value !== "number") { | |||||
| return '--' | |||||
| } | |||||
| return toFixed ? Number(value).toFixed(toFixed) : value | |||||
| } | |||||
| @@ -6,6 +6,7 @@ | |||||
| import { PageEnum } from '@/enums/pagesEnums'; | import { PageEnum } from '@/enums/pagesEnums'; | ||||
| import G6 from '@antv/g6'; | import G6 from '@antv/g6'; | ||||
| import { number } from 'echarts'; | |||||
| /** | /** | ||||
| * 生成 8 位随机数 | * 生成 8 位随机数 | ||||
| @@ -346,3 +347,4 @@ export const trimCharacter = (str: string, ch: string): string => { | |||||
| export const convertEmptyStringToUndefined = (value?: string): string | undefined => { | export const convertEmptyStringToUndefined = (value?: string): string | undefined => { | ||||
| return value === '' ? undefined : value; | return value === '' ? undefined : value; | ||||
| }; | }; | ||||
| @@ -30,7 +30,7 @@ public class Constant { | |||||
| public final static String Source_Hand_Export = "hand_export"; | public final static String Source_Hand_Export = "hand_export"; | ||||
| public final static String Source_Add = "add"; | public final static String Source_Add = "add"; | ||||
| public final static String Building = "building"; | |||||
| public final static String Building = "Building"; | |||||
| public final static String Running = "Running"; | public final static String Running = "Running"; | ||||
| public final static String Failed = "Failed"; | public final static String Failed = "Failed"; | ||||
| @@ -42,7 +42,7 @@ public class Constant { | |||||
| public final static String Error = "Error"; | public final static String Error = "Error"; | ||||
| public final static String Unknown = "Unknown"; | public final static String Unknown = "Unknown"; | ||||
| public final static String Available = "available"; | |||||
| public final static String Available = "Available"; | |||||
| public final static String Type_Train = "train"; | public final static String Type_Train = "train"; | ||||
| public final static String Type_Evaluate = "evaluate"; | public final static String Type_Evaluate = "evaluate"; | ||||
| @@ -62,7 +62,6 @@ public class Constant { | |||||
| public final static String TaskType_ActiveLearn = "active_learn"; | public final static String TaskType_ActiveLearn = "active_learn"; | ||||
| public final static String TaskType_Service = "service"; | public final static String TaskType_Service = "service"; | ||||
| public final static String TaskType_ML = "machine_learn"; | public final static String TaskType_ML = "machine_learn"; | ||||
| public final static String TaskType_TextClassification = "text_classification"; | |||||
| public final static String ML_CSV = "auto_ml"; | public final static String ML_CSV = "auto_ml"; | ||||
| public final static String ML_TextClassification = "text_classification"; | public final static String ML_TextClassification = "text_classification"; | ||||
| @@ -59,10 +59,10 @@ public class ActiveLearnInsController extends BaseController { | |||||
| return genericsSuccess(this.activeLearnInsService.getDetailById(id)); | return genericsSuccess(this.activeLearnInsService.getDetailById(id)); | ||||
| } | } | ||||
| @GetMapping("/getExpMetrics") | |||||
| @PostMapping("/getExpMetrics") | |||||
| @ApiOperation("获取当前实验的指标对比地址") | @ApiOperation("获取当前实验的指标对比地址") | ||||
| @ApiResponse | @ApiResponse | ||||
| public GenericsAjaxResult<String> getExpMetrics(@RequestParam(value = "experiment_ins_id") String experimentInsId) throws Exception { | |||||
| public GenericsAjaxResult<String> getExpMetrics(@RequestBody String experimentInsId) throws Exception { | |||||
| return genericsSuccess(activeLearnInsService.getExpMetrics(experimentInsId)); | return genericsSuccess(activeLearnInsService.getExpMetrics(experimentInsId)); | ||||
| } | } | ||||
| } | } | ||||
| @@ -100,7 +100,7 @@ public class ComponentController { | |||||
| */ | */ | ||||
| @DeleteMapping("{id}") | @DeleteMapping("{id}") | ||||
| @ApiOperation("根据id删除组件") | @ApiOperation("根据id删除组件") | ||||
| public AjaxResult deleteById(@PathVariable("id") Integer id) { | |||||
| public AjaxResult deleteById(@PathVariable("id") Integer id) throws Exception { | |||||
| return AjaxResult.success(this.componentService.removeById(id)); | return AjaxResult.success(this.componentService.removeById(id)); | ||||
| } | } | ||||
| @@ -118,7 +118,7 @@ public class DatasetVersionController extends BaseController { | |||||
| */ | */ | ||||
| @DeleteMapping({"{id}"}) | @DeleteMapping({"{id}"}) | ||||
| @ApiOperation("删除数据集版本") | @ApiOperation("删除数据集版本") | ||||
| public GenericsAjaxResult<String> deleteById(@PathVariable("id") Integer id) { | |||||
| public GenericsAjaxResult<String> deleteById(@PathVariable("id") Integer id) throws Exception { | |||||
| return genericsSuccess(this.datasetVersionService.removeById(id)); | return genericsSuccess(this.datasetVersionService.removeById(id)); | ||||
| } | } | ||||
| @@ -132,7 +132,7 @@ public class DatasetVersionController extends BaseController { | |||||
| @DeleteMapping("/deleteVersion") | @DeleteMapping("/deleteVersion") | ||||
| @ApiOperation(value = "逻辑删除模型版本", notes = "根据数据集ID和版本逻辑删除模型版本记录。") | @ApiOperation(value = "逻辑删除模型版本", notes = "根据数据集ID和版本逻辑删除模型版本记录。") | ||||
| public GenericsAjaxResult<Map<Integer, String>> deleteDatasetVersion(@RequestParam("dataset_id") Integer datasetId, | public GenericsAjaxResult<Map<Integer, String>> deleteDatasetVersion(@RequestParam("dataset_id") Integer datasetId, | ||||
| @RequestParam("version") String version) { | |||||
| @RequestParam("version") String version) throws Exception { | |||||
| return genericsSuccess(this.datasetVersionService.deleteDatasetVersion(datasetId, version)); | return genericsSuccess(this.datasetVersionService.deleteDatasetVersion(datasetId, version)); | ||||
| } | } | ||||
| @@ -14,6 +14,7 @@ import org.springframework.web.bind.annotation.*; | |||||
| import javax.annotation.Resource; | import javax.annotation.Resource; | ||||
| import java.io.IOException; | import java.io.IOException; | ||||
| import java.util.Date; | |||||
| import java.util.List; | import java.util.List; | ||||
| import java.util.Map; | import java.util.Map; | ||||
| @@ -42,8 +43,8 @@ public class ExperimentInsController extends BaseController { | |||||
| @GetMapping | @GetMapping | ||||
| @ApiOperation("分页查询") | @ApiOperation("分页查询") | ||||
| public GenericsAjaxResult<Page<ExperimentIns>> queryByPage(ExperimentIns experimentIns, int page, int size) throws IOException { | public GenericsAjaxResult<Page<ExperimentIns>> queryByPage(ExperimentIns experimentIns, int page, int size) throws IOException { | ||||
| PageRequest pageRequest = PageRequest.of(page,size); | |||||
| return genericsSuccess(this.experimentInsService.queryByPage(experimentIns, pageRequest)); | |||||
| PageRequest pageRequest = PageRequest.of(page, size); | |||||
| return genericsSuccess(this.experimentInsService.queryByPage(experimentIns, pageRequest)); | |||||
| } | } | ||||
| /** | /** | ||||
| @@ -55,7 +56,7 @@ public class ExperimentInsController extends BaseController { | |||||
| @GetMapping("{id}") | @GetMapping("{id}") | ||||
| @ApiOperation("通过id查询实验实例") | @ApiOperation("通过id查询实验实例") | ||||
| public GenericsAjaxResult<ExperimentIns> queryById(@PathVariable("id") Integer id) throws IOException { | public GenericsAjaxResult<ExperimentIns> queryById(@PathVariable("id") Integer id) throws IOException { | ||||
| return genericsSuccess(this.experimentInsService.queryById(id)); | |||||
| return genericsSuccess(this.experimentInsService.queryById(id)); | |||||
| } | } | ||||
| /** | /** | ||||
| @@ -67,7 +68,7 @@ public class ExperimentInsController extends BaseController { | |||||
| @GetMapping("/queryByExperimentId/{Experiment_id}") | @GetMapping("/queryByExperimentId/{Experiment_id}") | ||||
| @ApiOperation("通过实验id查询查询实验实例列表") | @ApiOperation("通过实验id查询查询实验实例列表") | ||||
| public GenericsAjaxResult<List<ExperimentIns>> queryByExperimentId(@PathVariable("Experiment_id") Integer experimentId) throws IOException { | public GenericsAjaxResult<List<ExperimentIns>> queryByExperimentId(@PathVariable("Experiment_id") Integer experimentId) throws IOException { | ||||
| return genericsSuccess(this.experimentInsService.getByExperimentId(experimentId)); | |||||
| return genericsSuccess(this.experimentInsService.getByExperimentId(experimentId)); | |||||
| } | } | ||||
| /** | /** | ||||
| @@ -79,7 +80,7 @@ public class ExperimentInsController extends BaseController { | |||||
| @PostMapping | @PostMapping | ||||
| @ApiOperation("新增实验实例") | @ApiOperation("新增实验实例") | ||||
| public GenericsAjaxResult<ExperimentIns> add(@RequestBody ExperimentIns experimentIns) { | public GenericsAjaxResult<ExperimentIns> add(@RequestBody ExperimentIns experimentIns) { | ||||
| return genericsSuccess(this.experimentInsService.insert(experimentIns)); | |||||
| return genericsSuccess(this.experimentInsService.insert(experimentIns)); | |||||
| } | } | ||||
| /** | /** | ||||
| @@ -91,7 +92,7 @@ public class ExperimentInsController extends BaseController { | |||||
| @PutMapping | @PutMapping | ||||
| @ApiOperation("编辑实验实例") | @ApiOperation("编辑实验实例") | ||||
| public GenericsAjaxResult<ExperimentIns> edit(@RequestBody ExperimentIns experimentIns) throws IOException { | public GenericsAjaxResult<ExperimentIns> edit(@RequestBody ExperimentIns experimentIns) throws IOException { | ||||
| return genericsSuccess(this.experimentInsService.update(experimentIns)); | |||||
| return genericsSuccess(this.experimentInsService.update(experimentIns)); | |||||
| } | } | ||||
| /** | /** | ||||
| @@ -102,14 +103,14 @@ public class ExperimentInsController extends BaseController { | |||||
| */ | */ | ||||
| @DeleteMapping("{id}") | @DeleteMapping("{id}") | ||||
| @ApiOperation("删除实验实例") | @ApiOperation("删除实验实例") | ||||
| public GenericsAjaxResult<String> deleteById( @PathVariable("id") Integer id) { | |||||
| return genericsSuccess(this.experimentInsService.removeById(id)); | |||||
| public GenericsAjaxResult<String> deleteById(@PathVariable("id") Integer id) { | |||||
| return genericsSuccess(this.experimentInsService.removeById(id)); | |||||
| } | } | ||||
| @DeleteMapping("batchDelete") | @DeleteMapping("batchDelete") | ||||
| @ApiOperation("批量删除实验实例") | @ApiOperation("批量删除实验实例") | ||||
| public GenericsAjaxResult<String> batchDelete(@RequestBody List<Integer> ids) throws Exception{ | |||||
| return genericsSuccess(this.experimentInsService.batchDelete(ids)); | |||||
| public GenericsAjaxResult<String> batchDelete(@RequestBody List<Integer> ids) throws Exception { | |||||
| return genericsSuccess(this.experimentInsService.batchDelete(ids)); | |||||
| } | } | ||||
| /** | /** | ||||
| @@ -133,8 +134,8 @@ public class ExperimentInsController extends BaseController { | |||||
| @GetMapping("/log") | @GetMapping("/log") | ||||
| @ApiOperation("查询实例日志") | @ApiOperation("查询实例日志") | ||||
| public GenericsAjaxResult<String> showExperimentInsLog(@RequestParam("id") Integer id, | public GenericsAjaxResult<String> showExperimentInsLog(@RequestParam("id") Integer id, | ||||
| @RequestParam("component_id") String componentId){ | |||||
| return genericsSuccess(this.experimentInsService.showExperimentInsLog(id,componentId)); | |||||
| @RequestParam("component_id") String componentId) { | |||||
| return genericsSuccess(this.experimentInsService.showExperimentInsLog(id, componentId)); | |||||
| } | } | ||||
| /** | /** | ||||
| @@ -146,14 +147,14 @@ public class ExperimentInsController extends BaseController { | |||||
| @GetMapping("/pods/log") | @GetMapping("/pods/log") | ||||
| @ApiOperation("获取pod实时日志请求") | @ApiOperation("获取pod实时日志请求") | ||||
| public GenericsAjaxResult<Map<String, Object>> getRealtimePodLog(@RequestParam("pod_name") String podName, | public GenericsAjaxResult<Map<String, Object>> getRealtimePodLog(@RequestParam("pod_name") String podName, | ||||
| @RequestParam("start_time") String startTime){ | |||||
| return genericsSuccess(this.experimentInsService.getRealtimePodLog(podName,startTime)); | |||||
| @RequestParam("start_time") String startTime) { | |||||
| return genericsSuccess(this.experimentInsService.getRealtimePodLog(podName, startTime)); | |||||
| } | } | ||||
| @PostMapping("/pods/realTimeLog") | @PostMapping("/pods/realTimeLog") | ||||
| @ApiOperation("获取pod实时日志请求") | @ApiOperation("获取pod实时日志请求") | ||||
| public GenericsAjaxResult<String> getRealtimePodLogFromPod(@RequestBody PodLogVo podLogVo){ | |||||
| public GenericsAjaxResult<String> getRealtimePodLogFromPod(@RequestBody PodLogVo podLogVo) { | |||||
| return genericsSuccess(this.experimentInsService.getRealtimePodLogFromPod(podLogVo)); | return genericsSuccess(this.experimentInsService.getRealtimePodLogFromPod(podLogVo)); | ||||
| } | } | ||||
| @@ -166,14 +167,11 @@ public class ExperimentInsController extends BaseController { | |||||
| @PostMapping("/realTimeLog") | @PostMapping("/realTimeLog") | ||||
| @ApiOperation("查询实验实例实时日志") | @ApiOperation("查询实验实例实时日志") | ||||
| public GenericsAjaxResult<Map<String, Object>> getRealtimeWorkflowLog(@RequestBody LogRequestVo logRequest){ | |||||
| public GenericsAjaxResult<Map<String, Object>> getRealtimeWorkflowLog(@RequestBody LogRequestVo logRequest) { | |||||
| return genericsSuccess(this.experimentInsService.getRealtimeWorkflowLog(logRequest)); | return genericsSuccess(this.experimentInsService.getRealtimeWorkflowLog(logRequest)); | ||||
| } | } | ||||
| /** | /** | ||||
| * 查询实验节点结果 | * 查询实验节点结果 | ||||
| * | * | ||||
| @@ -184,8 +182,13 @@ public class ExperimentInsController extends BaseController { | |||||
| public GenericsAjaxResult<List> getNodeResult(@RequestBody Map map) throws Exception { | public GenericsAjaxResult<List> getNodeResult(@RequestBody Map map) throws Exception { | ||||
| Integer id = Integer.parseInt((String) map.get("id")); | Integer id = Integer.parseInt((String) map.get("id")); | ||||
| String nodeId = (String) map.get("node_id"); | String nodeId = (String) map.get("node_id"); | ||||
| return genericsSuccess(this.experimentInsService.getNodeResult(id,nodeId)); | |||||
| return genericsSuccess(this.experimentInsService.getNodeResult(id, nodeId)); | |||||
| } | } | ||||
| @GetMapping("/time") | |||||
| @ApiOperation("获取当前时间") | |||||
| public GenericsAjaxResult<Date> getTime() { | |||||
| return genericsSuccess(new Date()); | |||||
| } | |||||
| } | } | ||||
| @@ -114,7 +114,7 @@ public class AssetIconController extends BaseController { | |||||
| */ | */ | ||||
| @DeleteMapping("{id}") | @DeleteMapping("{id}") | ||||
| @ApiOperation("删除图标") | @ApiOperation("删除图标") | ||||
| public GenericsAjaxResult<String> deleteById(@PathVariable("id") Integer id) { | |||||
| public GenericsAjaxResult<String> deleteById(@PathVariable("id") Integer id) throws Exception { | |||||
| return genericsSuccess(this.assetIconService.removeById(id)); | return genericsSuccess(this.assetIconService.removeById(id)); | ||||
| } | } | ||||
| @@ -0,0 +1,25 @@ | |||||
| package com.ruoyi.platform.controller.knowledgeGraph; | |||||
| import com.ruoyi.common.core.web.controller.BaseController; | |||||
| import com.ruoyi.common.core.web.domain.GenericsAjaxResult; | |||||
| import io.swagger.annotations.Api; | |||||
| import io.swagger.annotations.ApiOperation; | |||||
| import org.springframework.beans.factory.annotation.Value; | |||||
| import org.springframework.web.bind.annotation.GetMapping; | |||||
| import org.springframework.web.bind.annotation.RequestMapping; | |||||
| import org.springframework.web.bind.annotation.RestController; | |||||
| @RestController | |||||
| @RequestMapping("/knowledgeGraph") | |||||
| @Api("knowledgeGraph service") | |||||
| public class KnowledgeGraphController extends BaseController { | |||||
| @Value("${knowledgeGraphUrl}") | |||||
| private String url; | |||||
| @GetMapping(value = "/getURL") | |||||
| @ApiOperation("得到访问地址") | |||||
| public GenericsAjaxResult<String> getURL() { | |||||
| return genericsSuccess(url); | |||||
| } | |||||
| } | |||||
| @@ -1,9 +1,7 @@ | |||||
| package com.ruoyi.platform.controller.labelStudio; | package com.ruoyi.platform.controller.labelStudio; | ||||
| import com.ruoyi.common.core.web.controller.BaseController; | import com.ruoyi.common.core.web.controller.BaseController; | ||||
| import com.ruoyi.common.core.web.domain.AjaxResult; | |||||
| import com.ruoyi.common.core.web.domain.GenericsAjaxResult; | import com.ruoyi.common.core.web.domain.GenericsAjaxResult; | ||||
| import com.ruoyi.platform.service.JupyterService; | |||||
| import io.swagger.annotations.Api; | import io.swagger.annotations.Api; | ||||
| import io.swagger.annotations.ApiOperation; | import io.swagger.annotations.ApiOperation; | ||||
| import org.springframework.beans.factory.annotation.Value; | import org.springframework.beans.factory.annotation.Value; | ||||
| @@ -11,12 +9,6 @@ import org.springframework.web.bind.annotation.GetMapping; | |||||
| import org.springframework.web.bind.annotation.RequestMapping; | import org.springframework.web.bind.annotation.RequestMapping; | ||||
| import org.springframework.web.bind.annotation.RestController; | import org.springframework.web.bind.annotation.RestController; | ||||
| import javax.annotation.Resource; | |||||
| import java.io.File; | |||||
| import java.io.FileInputStream; | |||||
| import java.io.IOException; | |||||
| import java.io.InputStream; | |||||
| @RestController | @RestController | ||||
| @RequestMapping("/labelStudio") | @RequestMapping("/labelStudio") | ||||
| @Api("labelStudio service") | @Api("labelStudio service") | ||||
| @@ -25,7 +17,7 @@ public class labelStudioController extends BaseController { | |||||
| private String url; | private String url; | ||||
| @GetMapping(value = "/getURL") | @GetMapping(value = "/getURL") | ||||
| @ApiOperation("得到访问地址") | @ApiOperation("得到访问地址") | ||||
| public GenericsAjaxResult<String> getURL() throws IOException { | |||||
| public GenericsAjaxResult<String> getURL() { | |||||
| return genericsSuccess(url); | return genericsSuccess(url); | ||||
| } | } | ||||
| } | } | ||||
| @@ -101,7 +101,7 @@ public class ModelDependencyController extends BaseController { | |||||
| */ | */ | ||||
| @DeleteMapping("{id}") | @DeleteMapping("{id}") | ||||
| @ApiOperation("删除模型依赖") | @ApiOperation("删除模型依赖") | ||||
| public GenericsAjaxResult<String> deleteById(@PathVariable("id") Integer id) { | |||||
| public GenericsAjaxResult<String> deleteById(@PathVariable("id") Integer id) throws Exception { | |||||
| return genericsSuccess(this.modelDependencyService.removeById(id)); | return genericsSuccess(this.modelDependencyService.removeById(id)); | ||||
| } | } | ||||
| @@ -130,7 +130,7 @@ public class ModelsVersionController extends BaseController { | |||||
| @DeleteMapping("/deleteVersion") | @DeleteMapping("/deleteVersion") | ||||
| @ApiOperation(value = "逻辑删除模型版本", notes = "根据模型ID和版本逻辑删除模型版本记录。") | @ApiOperation(value = "逻辑删除模型版本", notes = "根据模型ID和版本逻辑删除模型版本记录。") | ||||
| public GenericsAjaxResult<Map<Integer, String>> deleteModelsVersion(@RequestParam("models_id") Integer modelsId, | public GenericsAjaxResult<Map<Integer, String>> deleteModelsVersion(@RequestParam("models_id") Integer modelsId, | ||||
| @RequestParam("version") String version) throws IOException { | |||||
| @RequestParam("version") String version) throws Exception { | |||||
| return genericsSuccess(this.modelsVersionService.deleteModelsVersion(modelsId, version)); | return genericsSuccess(this.modelsVersionService.deleteModelsVersion(modelsId, version)); | ||||
| } | } | ||||
| @@ -94,7 +94,7 @@ public class ComputingResourceController extends BaseController { | |||||
| */ | */ | ||||
| @DeleteMapping("{id}") | @DeleteMapping("{id}") | ||||
| @ApiOperation("删除计算资源") | @ApiOperation("删除计算资源") | ||||
| public GenericsAjaxResult<String> deleteById(@PathVariable("id") Integer id) { | |||||
| public GenericsAjaxResult<String> deleteById(@PathVariable("id") Integer id) throws Exception { | |||||
| return genericsSuccess(this.computingResourceService.removeById(id)); | return genericsSuccess(this.computingResourceService.removeById(id)); | ||||
| } | } | ||||
| @@ -84,7 +84,7 @@ public class WorkflowParamController extends BaseController { | |||||
| */ | */ | ||||
| @DeleteMapping | @DeleteMapping | ||||
| @ApiOperation("删除流水线参数") | @ApiOperation("删除流水线参数") | ||||
| public GenericsAjaxResult<String> deleteById(Integer id) { | |||||
| public GenericsAjaxResult<String> deleteById(Integer id) throws Exception { | |||||
| return genericsSuccess(this.workflowParamService.removeById(id)); | return genericsSuccess(this.workflowParamService.removeById(id)); | ||||
| } | } | ||||
| @@ -21,6 +21,9 @@ public class CodeConfig implements Serializable { | |||||
| @ApiModelProperty(name = "code_repo_vis", value = "代码仓库可见性(1-公开,0-私有)") | @ApiModelProperty(name = "code_repo_vis", value = "代码仓库可见性(1-公开,0-私有)") | ||||
| private Integer codeRepoVis; | private Integer codeRepoVis; | ||||
| @ApiModelProperty(name = "is_public", value = "1-公开,0-私有)") | |||||
| private Boolean isPublic; | |||||
| @ApiModelProperty(name = "git_url", value = "Git地址") | @ApiModelProperty(name = "git_url", value = "Git地址") | ||||
| private String gitUrl; | private String gitUrl; | ||||
| @@ -7,6 +7,8 @@ import io.swagger.annotations.ApiModelProperty; | |||||
| import lombok.Data; | import lombok.Data; | ||||
| import java.util.Date; | import java.util.Date; | ||||
| import java.util.List; | |||||
| import java.util.Map; | |||||
| @Data | @Data | ||||
| @JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) | @JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) | ||||
| @@ -48,4 +50,6 @@ public class MachineLearnIns { | |||||
| private Date updateTime; | private Date updateTime; | ||||
| private Date finishTime; | private Date finishTime; | ||||
| private Map fileMap; | |||||
| } | } | ||||
| @@ -33,7 +33,7 @@ public class ActiveLearnInsStatusTask { | |||||
| private List<Long> activeLearnIds = new ArrayList<>(); | private List<Long> activeLearnIds = new ArrayList<>(); | ||||
| @Scheduled(cron = "0/30 * * * * ?") // 每30S执行一次 | |||||
| @Scheduled(cron = "0/10 * * * * ?") // 每10S执行一次 | |||||
| public void executeActiveLearnInsStatus() { | public void executeActiveLearnInsStatus() { | ||||
| List<ActiveLearnIns> activeLearnInsList = activeLearnInsService.queryActiveLearnInsIsNotTerminated(); | List<ActiveLearnIns> activeLearnInsList = activeLearnInsService.queryActiveLearnInsIsNotTerminated(); | ||||
| @@ -29,7 +29,7 @@ public class AutoMlInsStatusTask { | |||||
| private List<Long> autoMlIds = new ArrayList<>(); | private List<Long> autoMlIds = new ArrayList<>(); | ||||
| @Scheduled(cron = "0/30 * * * * ?") // 每30S执行一次 | |||||
| @Scheduled(cron = "0/10 * * * * ?") // 每10S执行一次 | |||||
| public void executeAutoMlInsStatus() { | public void executeAutoMlInsStatus() { | ||||
| // 首先查到所有非终止态的实验实例 | // 首先查到所有非终止态的实验实例 | ||||
| List<AutoMlIns> autoMlInsList = autoMlInsService.queryByAutoMlInsIsNotTerminated(); | List<AutoMlIns> autoMlInsList = autoMlInsService.queryByAutoMlInsIsNotTerminated(); | ||||
| @@ -36,7 +36,7 @@ public class ExperimentInstanceStatusTask { | |||||
| private List<Integer> experimentIds = new ArrayList<>(); | private List<Integer> experimentIds = new ArrayList<>(); | ||||
| @Scheduled(cron = "0/30 * * * * ?") // 每30S执行一次 | |||||
| @Scheduled(cron = "0/10 * * * * ?") // 每10S执行一次 | |||||
| public void executeExperimentInsStatus() throws Exception { | public void executeExperimentInsStatus() throws Exception { | ||||
| // 首先查到所有非终止态的实验实例 | // 首先查到所有非终止态的实验实例 | ||||
| List<ExperimentIns> experimentInsList = experimentInsService.queryByExperimentIsNotTerminated(); | List<ExperimentIns> experimentInsList = experimentInsService.queryByExperimentIsNotTerminated(); | ||||
| @@ -31,7 +31,7 @@ public class MLStatusTask { | |||||
| private List<Long> machineLearnIds = new ArrayList<>(); | private List<Long> machineLearnIds = new ArrayList<>(); | ||||
| @Scheduled(cron = "0/30 * * * * ?") // 每30S执行一次 | |||||
| @Scheduled(cron = "0/10 * * * * ?") // 每10S执行一次 | |||||
| public void executeMachineLearnInsStatus() { | public void executeMachineLearnInsStatus() { | ||||
| // 首先查到所有非终止态的实验实例 | // 首先查到所有非终止态的实验实例 | ||||
| List<MachineLearnIns> insList = machineLearnInsService.queryNotTerminated(); | List<MachineLearnIns> insList = machineLearnInsService.queryNotTerminated(); | ||||
| @@ -33,7 +33,7 @@ public class RayInsStatusTask { | |||||
| private List<Long> rayIds = new ArrayList<>(); | private List<Long> rayIds = new ArrayList<>(); | ||||
| @Scheduled(cron = "0/30 * * * * ?") // 每30S执行一次 | |||||
| @Scheduled(cron = "0/10 * * * * ?") // 每10S执行一次 | |||||
| public void executeRayInsStatus() { | public void executeRayInsStatus() { | ||||
| List<RayIns> rayInsList = rayInsService.queryByRayInsIsNotTerminated(); | List<RayIns> rayInsList = rayInsService.queryByRayInsIsNotTerminated(); | ||||
| @@ -64,7 +64,7 @@ public interface AssetIconService { | |||||
| */ | */ | ||||
| boolean deleteById(Integer id); | boolean deleteById(Integer id); | ||||
| String removeById(Integer id); | |||||
| String removeById(Integer id) throws Exception; | |||||
| List<AssetIcon> queryByName(String name); | List<AssetIcon> queryByName(String name); | ||||
| @@ -57,7 +57,7 @@ public interface ComponentService { | |||||
| */ | */ | ||||
| boolean deleteById(Integer id); | boolean deleteById(Integer id); | ||||
| String removeById(Integer id); | |||||
| String removeById(Integer id) throws Exception; | |||||
| List<Map> queryAllGroupedByCategory() throws Exception; | List<Map> queryAllGroupedByCategory() throws Exception; | ||||
| @@ -54,6 +54,6 @@ public interface ComputingResourceService { | |||||
| */ | */ | ||||
| boolean deleteById(Integer id); | boolean deleteById(Integer id); | ||||
| String removeById(Integer id); | |||||
| String removeById(Integer id) throws Exception; | |||||
| } | } | ||||
| @@ -58,7 +58,7 @@ public interface DatasetVersionService { | |||||
| */ | */ | ||||
| boolean deleteById(Integer id); | boolean deleteById(Integer id); | ||||
| String removeById(Integer id); | |||||
| String removeById(Integer id) throws Exception; | |||||
| List<DatasetVersion> queryByDatasetId(Integer datasetId); | List<DatasetVersion> queryByDatasetId(Integer datasetId); | ||||
| @@ -66,7 +66,7 @@ public interface DatasetVersionService { | |||||
| Map<String,Object> queryByDatasetIdAndVersion(Integer datasetId, String version); | Map<String,Object> queryByDatasetIdAndVersion(Integer datasetId, String version); | ||||
| Map<Integer,String> deleteDatasetVersion(Integer datasetId, String version); | |||||
| Map<Integer,String> deleteDatasetVersion(Integer datasetId, String version) throws Exception; | |||||
| void checkDeclaredVersion(DatasetVersion insert) throws Exception; | void checkDeclaredVersion(DatasetVersion insert) throws Exception; | ||||
| @@ -57,7 +57,7 @@ public interface ModelDependencyService { | |||||
| */ | */ | ||||
| boolean deleteById(Integer id); | boolean deleteById(Integer id); | ||||
| String removeById(Integer id); | |||||
| String removeById(Integer id) throws Exception; | |||||
| List<ModelDependency> queryByModelDependency(ModelDependency modelDependency) throws IOException; | List<ModelDependency> queryByModelDependency(ModelDependency modelDependency) throws IOException; | ||||
| @@ -69,7 +69,7 @@ public interface ModelsVersionService { | |||||
| Map<String,Object> queryByModelsIdAndVersion(Integer modelsId, String version); | Map<String,Object> queryByModelsIdAndVersion(Integer modelsId, String version); | ||||
| Map<Integer, String> deleteModelsVersion(Integer modelsId, String version) throws IOException; | |||||
| Map<Integer, String> deleteModelsVersion(Integer modelsId, String version) throws Exception; | |||||
| String addModelVersions(List<ModelsVersion> modelsVersions) throws Exception; | String addModelVersions(List<ModelsVersion> modelsVersions) throws Exception; | ||||
| @@ -45,7 +45,7 @@ public interface WorkflowParamService { | |||||
| */ | */ | ||||
| WorkflowParam update(WorkflowParam workflowParam); | WorkflowParam update(WorkflowParam workflowParam); | ||||
| String removeById(Integer id); | |||||
| String removeById(Integer id) throws Exception; | |||||
| /** | /** | ||||
| * 通过主键删除数据 | * 通过主键删除数据 | ||||
| @@ -57,7 +57,7 @@ public class ActiveLearnServiceImpl implements ActiveLearnService { | |||||
| @Override | @Override | ||||
| public ActiveLearn save(ActiveLearnVo activeLearnVo) throws Exception { | public ActiveLearn save(ActiveLearnVo activeLearnVo) throws Exception { | ||||
| if (activeLearnVo.getName().length() >= 64) { | |||||
| if (activeLearnVo.getName().length() > 64) { | |||||
| throw new RuntimeException("实验名称大于最大长度"); | throw new RuntimeException("实验名称大于最大长度"); | ||||
| } | } | ||||
| ActiveLearn activeLearnByName = activeLearnDao.getActiveLearnByName(activeLearnVo.getName()); | ActiveLearn activeLearnByName = activeLearnDao.getActiveLearnByName(activeLearnVo.getName()); | ||||
| @@ -157,6 +157,10 @@ public class ActiveLearnServiceImpl implements ActiveLearnService { | |||||
| throw new RuntimeException("转换流水线失败"); | throw new RuntimeException("转换流水线失败"); | ||||
| } | } | ||||
| Map<String, Object> converMap = JsonUtils.jsonToMap(convertRes); | Map<String, Object> converMap = JsonUtils.jsonToMap(convertRes); | ||||
| if (converMap.get("data") == null) { | |||||
| throw new RuntimeException("转换流水线失败"); | |||||
| } | |||||
| // 组装运行接口json | // 组装运行接口json | ||||
| Map<String, Object> output = (Map<String, Object>) converMap.get("output"); | Map<String, Object> output = (Map<String, Object>) converMap.get("output"); | ||||
| Map<String, Object> runReqMap = new HashMap<>(); | Map<String, Object> runReqMap = new HashMap<>(); | ||||
| @@ -101,10 +101,10 @@ public class AssetIconServiceImpl implements AssetIconService { | |||||
| } | } | ||||
| @Override | @Override | ||||
| public String removeById(Integer id) { | |||||
| public String removeById(Integer id) throws Exception { | |||||
| AssetIcon assetIcon = this.assetIconDao.queryById(id); | AssetIcon assetIcon = this.assetIconDao.queryById(id); | ||||
| if (assetIcon == null){ | if (assetIcon == null){ | ||||
| return "图标不存在"; | |||||
| throw new Exception("图标不存在"); | |||||
| } | } | ||||
| //判断权限,只有admin和创建者本身可以删除 | //判断权限,只有admin和创建者本身可以删除 | ||||
| @@ -114,7 +114,7 @@ public class AssetIconServiceImpl implements AssetIconService { | |||||
| String createdBy = assetIcon.getCreateBy(); | String createdBy = assetIcon.getCreateBy(); | ||||
| if (!(StringUtils.equals(username,"admin") || StringUtils.equals(username,createdBy))){ | if (!(StringUtils.equals(username,"admin") || StringUtils.equals(username,createdBy))){ | ||||
| return "无权限删除该图标"; | |||||
| throw new Exception("无权限删除该图标"); | |||||
| } | } | ||||
| assetIcon.setState(0); | assetIcon.setState(0); | ||||
| @@ -66,7 +66,7 @@ public class AutoMlServiceImpl implements AutoMlService { | |||||
| @Override | @Override | ||||
| public AutoMl save(AutoMlVo autoMlVo) throws Exception { | public AutoMl save(AutoMlVo autoMlVo) throws Exception { | ||||
| if (autoMlVo.getMlName().length() >= 64) { | |||||
| if (autoMlVo.getMlName().length() > 64) { | |||||
| throw new RuntimeException("实验名称大于最大长度"); | throw new RuntimeException("实验名称大于最大长度"); | ||||
| } | } | ||||
| AutoMl autoMlByName = autoMlDao.getAutoMlByName(autoMlVo.getMlName()); | AutoMl autoMlByName = autoMlDao.getAutoMlByName(autoMlVo.getMlName()); | ||||
| @@ -121,7 +121,7 @@ public class CodeConfigServiceImpl implements CodeConfigService { | |||||
| String username = loginUser.getUsername(); | String username = loginUser.getUsername(); | ||||
| String createBy = codeConfig.getCreateBy(); | String createBy = codeConfig.getCreateBy(); | ||||
| if (!(StringUtils.equals(username, "admin") || StringUtils.equals(username, createBy))) { | if (!(StringUtils.equals(username, "admin") || StringUtils.equals(username, createBy))) { | ||||
| return "无权限删除该代码配置"; | |||||
| throw new Exception("无权限删除该代码配置"); | |||||
| } | } | ||||
| codeConfig.setState(Constant.State_invalid); | codeConfig.setState(Constant.State_invalid); | ||||
| return this.codeConfigDao.update(codeConfig) > 0 ? "删除成功" : "删除失败"; | return this.codeConfigDao.update(codeConfig) > 0 ? "删除成功" : "删除失败"; | ||||
| @@ -47,7 +47,7 @@ public class ComponentServiceImpl implements ComponentService { | |||||
| @Override | @Override | ||||
| public Component queryById(Integer id) { | public Component queryById(Integer id) { | ||||
| Component component = this.componentDao.queryById(id); | Component component = this.componentDao.queryById(id); | ||||
| if (component == null){ | |||||
| if (component == null) { | |||||
| throw new RuntimeException("组件不存在"); | throw new RuntimeException("组件不存在"); | ||||
| } | } | ||||
| @@ -58,14 +58,14 @@ public class ComponentServiceImpl implements ComponentService { | |||||
| public List<Map> queryAllGroupedByCategory() throws Exception { | public List<Map> queryAllGroupedByCategory() throws Exception { | ||||
| List<Component> componentList = this.componentDao.queryAll(); | List<Component> componentList = this.componentDao.queryAll(); | ||||
| List<Map> result = new ArrayList<>(); | List<Map> result = new ArrayList<>(); | ||||
| if (componentList.isEmpty()){ | |||||
| if (componentList.isEmpty()) { | |||||
| return result; | return result; | ||||
| } | } | ||||
| List<SysDictData> categoryTypeList = DictUtils.getDictCache("category_type"); | List<SysDictData> categoryTypeList = DictUtils.getDictCache("category_type"); | ||||
| Map<Integer,List<Component>> groupedComponent = componentList.stream().collect(Collectors.groupingBy(Component::getCategoryId)); | |||||
| for (Map.Entry <Integer,List<Component>> entry : groupedComponent.entrySet()) { | |||||
| Map<Integer, List<Component>> groupedComponent = componentList.stream().collect(Collectors.groupingBy(Component::getCategoryId)); | |||||
| for (Map.Entry<Integer, List<Component>> entry : groupedComponent.entrySet()) { | |||||
| List<SysDictData> categorys = categoryTypeList.stream().filter(sysDictData -> StringUtils.equals(sysDictData.getDictValue(), String.valueOf(entry.getKey()))).collect(Collectors.toList()); | List<SysDictData> categorys = categoryTypeList.stream().filter(sysDictData -> StringUtils.equals(sysDictData.getDictValue(), String.valueOf(entry.getKey()))).collect(Collectors.toList()); | ||||
| if (categorys.size() ==0){ | |||||
| if (categorys.size() == 0) { | |||||
| throw new Exception("组件类型不存在"); | throw new Exception("组件类型不存在"); | ||||
| } | } | ||||
| Map map = new HashMap(); | Map map = new HashMap(); | ||||
| @@ -80,8 +80,8 @@ public class ComponentServiceImpl implements ComponentService { | |||||
| /** | /** | ||||
| * 分页查询 | * 分页查询 | ||||
| * | * | ||||
| * @param component 筛选条件 | |||||
| * @param pageRequest 分页对象 | |||||
| * @param component 筛选条件 | |||||
| * @param pageRequest 分页对象 | |||||
| * @return 查询结果 | * @return 查询结果 | ||||
| */ | */ | ||||
| @Override | @Override | ||||
| @@ -104,7 +104,7 @@ public class ComponentServiceImpl implements ComponentService { | |||||
| component.setControlStrategy(controlStrategy); | component.setControlStrategy(controlStrategy); | ||||
| //json转换,存数据库 | //json转换,存数据库 | ||||
| String inParameters= gson.toJson(componentVo.getInParameters(), LinkedHashMap.class); | |||||
| String inParameters = gson.toJson(componentVo.getInParameters(), LinkedHashMap.class); | |||||
| String outParameters = gson.toJson(componentVo.getOutParameters(), LinkedHashMap.class); | String outParameters = gson.toJson(componentVo.getOutParameters(), LinkedHashMap.class); | ||||
| String envVariable = gson.toJson(componentVo.getEnvVirables(), LinkedHashMap.class); | String envVariable = gson.toJson(componentVo.getEnvVirables(), LinkedHashMap.class); | ||||
| component.setEnvVirables(envVariable); | component.setEnvVirables(envVariable); | ||||
| @@ -118,9 +118,9 @@ public class ComponentServiceImpl implements ComponentService { | |||||
| component.setState(1); | component.setState(1); | ||||
| // 检查相同category_id下的component_name是否已存在 | // 检查相同category_id下的component_name是否已存在 | ||||
| Integer existingCount = this.componentDao.countByNameAndCategoryId(component.getComponentName(),component.getCategoryId()); | |||||
| Integer existingCount = this.componentDao.countByNameAndCategoryId(component.getComponentName(), component.getCategoryId()); | |||||
| if(existingCount != null && existingCount > 0) { | |||||
| if (existingCount != null && existingCount > 0) { | |||||
| throw new RuntimeException("该类别下已有同名组件"); | throw new RuntimeException("该类别下已有同名组件"); | ||||
| } | } | ||||
| @@ -139,15 +139,15 @@ public class ComponentServiceImpl implements ComponentService { | |||||
| Component component = this.queryById(componentVo.getId()); | Component component = this.queryById(componentVo.getId()); | ||||
| //只能更新当前存在的组件 | //只能更新当前存在的组件 | ||||
| if (component == null){ | |||||
| if (component == null) { | |||||
| throw new RuntimeException("组件不存在,无法更新"); | throw new RuntimeException("组件不存在,无法更新"); | ||||
| } | } | ||||
| //将object转成string类型 | //将object转成string类型 | ||||
| component = ConvertUtil.entityToVo(componentVo,Component.class); | |||||
| component = ConvertUtil.entityToVo(componentVo, Component.class); | |||||
| Gson gson = new Gson(); | Gson gson = new Gson(); | ||||
| String inParameters= gson.toJson(componentVo.getInParameters(), LinkedHashMap.class); | |||||
| String inParameters = gson.toJson(componentVo.getInParameters(), LinkedHashMap.class); | |||||
| String outParameters = gson.toJson(componentVo.getOutParameters(), LinkedHashMap.class); | String outParameters = gson.toJson(componentVo.getOutParameters(), LinkedHashMap.class); | ||||
| String envVariable = gson.toJson(componentVo.getEnvVirables(), LinkedHashMap.class); | String envVariable = gson.toJson(componentVo.getEnvVirables(), LinkedHashMap.class); | ||||
| @@ -174,24 +174,24 @@ public class ComponentServiceImpl implements ComponentService { | |||||
| } | } | ||||
| @Override | @Override | ||||
| public String removeById(Integer id) { | |||||
| public String removeById(Integer id) throws Exception { | |||||
| Component component = this.componentDao.queryById(id); | Component component = this.componentDao.queryById(id); | ||||
| //先进行判断 组件是否存在 | //先进行判断 组件是否存在 | ||||
| if (component == null ){ | |||||
| return "组件不存在"; | |||||
| if (component == null) { | |||||
| throw new Exception("组件不存在"); | |||||
| } | } | ||||
| //判断权限,只有admin和创建者本身可以删除该组件 | //判断权限,只有admin和创建者本身可以删除该组件 | ||||
| LoginUser loginUser = SecurityUtils.getLoginUser(); | LoginUser loginUser = SecurityUtils.getLoginUser(); | ||||
| String username = loginUser.getUsername(); | String username = loginUser.getUsername(); | ||||
| String createdBy = component.getCreateBy(); | String createdBy = component.getCreateBy(); | ||||
| if (!(StringUtils.equals(username,"admin") || StringUtils.equals(username,createdBy))){ | |||||
| return "无权限删除该组件"; | |||||
| if (!(StringUtils.equals(username, "admin") || StringUtils.equals(username, createdBy))) { | |||||
| throw new Exception("无权限删除该组件"); | |||||
| } | } | ||||
| component.setState(0); | component.setState(0); | ||||
| return this.componentDao.update(component)>0?"删除成功":"删除失败"; | |||||
| return this.componentDao.update(component) > 0 ? "删除成功" : "删除失败"; | |||||
| } | } | ||||
| @@ -92,10 +92,10 @@ public class ComputingResourceServiceImpl implements ComputingResourceService { | |||||
| } | } | ||||
| @Override | @Override | ||||
| public String removeById(Integer id) { | |||||
| public String removeById(Integer id) throws Exception { | |||||
| ComputingResource computingResource = this.computingResourceDao.queryById(id); | ComputingResource computingResource = this.computingResourceDao.queryById(id); | ||||
| if (computingResource == null){ | if (computingResource == null){ | ||||
| return "计算资源不存在"; | |||||
| throw new Exception("计算资源不存在"); | |||||
| } | } | ||||
| //判断权限,只有admin和创建者本身可以删除该数据集 | //判断权限,只有admin和创建者本身可以删除该数据集 | ||||
| @@ -103,7 +103,7 @@ public class ComputingResourceServiceImpl implements ComputingResourceService { | |||||
| String username = loginUser.getUsername(); | String username = loginUser.getUsername(); | ||||
| String createdBy = computingResource.getCreateBy(); | String createdBy = computingResource.getCreateBy(); | ||||
| if (!(StringUtils.equals(username,"admin") || StringUtils.equals(username,createdBy))){ | if (!(StringUtils.equals(username,"admin") || StringUtils.equals(username,createdBy))){ | ||||
| return "无权限删除该计算资源"; | |||||
| throw new Exception("无权限删除该计算资源"); | |||||
| } | } | ||||
| computingResource.setState(0); | computingResource.setState(0); | ||||
| @@ -128,17 +128,17 @@ public class DatasetVersionServiceImpl implements DatasetVersionService { | |||||
| @Override | @Override | ||||
| public String removeById(Integer id) { | |||||
| public String removeById(Integer id) throws Exception { | |||||
| DatasetVersion datasetVersion = this.datasetVersionDao.queryById(id); | DatasetVersion datasetVersion = this.datasetVersionDao.queryById(id); | ||||
| if (datasetVersion == null){ | if (datasetVersion == null){ | ||||
| return "数据集版本信息不存在"; | |||||
| throw new Exception("数据集版本信息不存在"); | |||||
| } | } | ||||
| //判断权限,只有admin和创建者本身可以删除该数据集版本信息 | //判断权限,只有admin和创建者本身可以删除该数据集版本信息 | ||||
| LoginUser loginUser = SecurityUtils.getLoginUser(); | LoginUser loginUser = SecurityUtils.getLoginUser(); | ||||
| String username = loginUser.getUsername(); | String username = loginUser.getUsername(); | ||||
| String createdBy = datasetVersion.getCreateBy(); | String createdBy = datasetVersion.getCreateBy(); | ||||
| if (!(StringUtils.equals(username,"admin") || StringUtils.equals(username,createdBy))){ | if (!(StringUtils.equals(username,"admin") || StringUtils.equals(username,createdBy))){ | ||||
| return "无权限删除该数据集版本"; | |||||
| throw new Exception("无权限删除该数据集版本"); | |||||
| } | } | ||||
| datasetVersion.setState(0); | datasetVersion.setState(0); | ||||
| @@ -175,7 +175,7 @@ public class DatasetVersionServiceImpl implements DatasetVersionService { | |||||
| } | } | ||||
| @Override | @Override | ||||
| public Map<Integer, String> deleteDatasetVersion(Integer datasetId, String version) { | |||||
| public Map<Integer, String> deleteDatasetVersion(Integer datasetId, String version) throws Exception { | |||||
| Map<Integer, String> results = new HashMap<Integer,String>(); | Map<Integer, String> results = new HashMap<Integer,String>(); | ||||
| // 根据模型ID和版本查询所有模型版本 | // 根据模型ID和版本查询所有模型版本 | ||||
| List<DatasetVersion> versions = this.datasetVersionDao.queryAllByDatasetVersion(datasetId, version); | List<DatasetVersion> versions = this.datasetVersionDao.queryAllByDatasetVersion(datasetId, version); | ||||
| @@ -109,8 +109,12 @@ public class DevEnvironmentServiceImpl implements DevEnvironmentService { | |||||
| devEnvironment.setStandard(devEnvironmentVo.getStandard()); | devEnvironment.setStandard(devEnvironmentVo.getStandard()); | ||||
| devEnvironment.setEnvVariable(devEnvironmentVo.getEnvVariable()); | devEnvironment.setEnvVariable(devEnvironmentVo.getEnvVariable()); | ||||
| devEnvironment.setImage(JacksonUtil.toJSONString(devEnvironmentVo.getImage())); | devEnvironment.setImage(JacksonUtil.toJSONString(devEnvironmentVo.getImage())); | ||||
| devEnvironment.setDataset(JacksonUtil.toJSONString(devEnvironmentVo.getDataset())); | |||||
| devEnvironment.setModel(JacksonUtil.toJSONString(devEnvironmentVo.getModel())); | |||||
| if (devEnvironmentVo.getDataset() != null) { | |||||
| devEnvironment.setDataset(JacksonUtil.toJSONString(devEnvironmentVo.getDataset())); | |||||
| } | |||||
| if (devEnvironmentVo.getModel() != null) { | |||||
| devEnvironment.setModel(JacksonUtil.toJSONString(devEnvironmentVo.getModel())); | |||||
| } | |||||
| devEnvironment.setCreateBy(loginUser.getUsername()); | devEnvironment.setCreateBy(loginUser.getUsername()); | ||||
| devEnvironment.setUpdateBy(loginUser.getUsername()); | devEnvironment.setUpdateBy(loginUser.getUsername()); | ||||
| devEnvironment.setUpdateTime(new Date()); | devEnvironment.setUpdateTime(new Date()); | ||||
| @@ -154,7 +158,7 @@ public class DevEnvironmentServiceImpl implements DevEnvironmentService { | |||||
| public String removeById(Integer id) throws Exception { | public String removeById(Integer id) throws Exception { | ||||
| DevEnvironment devEnvironment = this.devEnvironmentDao.queryById(id); | DevEnvironment devEnvironment = this.devEnvironmentDao.queryById(id); | ||||
| if (devEnvironment == null) { | if (devEnvironment == null) { | ||||
| return "开发环境信息不存在"; | |||||
| throw new RuntimeException("开发环境信息不存在"); | |||||
| } | } | ||||
| //判断权限,只有admin和创建者本身可以删除该数据集 | //判断权限,只有admin和创建者本身可以删除该数据集 | ||||
| @@ -162,7 +166,7 @@ public class DevEnvironmentServiceImpl implements DevEnvironmentService { | |||||
| String username = loginUser.getUsername(); | String username = loginUser.getUsername(); | ||||
| String createdBy = devEnvironment.getCreateBy(); | String createdBy = devEnvironment.getCreateBy(); | ||||
| if (!(StringUtils.equals(username, "admin") || StringUtils.equals(username, createdBy))) { | if (!(StringUtils.equals(username, "admin") || StringUtils.equals(username, createdBy))) { | ||||
| return "无权限删除该开发环境"; | |||||
| throw new RuntimeException("无权限删除该开发环境"); | |||||
| } | } | ||||
| jupyterService.stopJupyterService(id); | jupyterService.stopJupyterService(id); | ||||
| @@ -18,6 +18,8 @@ import com.ruoyi.platform.vo.PodLogVo; | |||||
| import com.ruoyi.system.api.constant.Constant; | import com.ruoyi.system.api.constant.Constant; | ||||
| import com.ruoyi.system.api.model.LoginUser; | import com.ruoyi.system.api.model.LoginUser; | ||||
| import org.apache.commons.lang3.StringUtils; | import org.apache.commons.lang3.StringUtils; | ||||
| import org.slf4j.Logger; | |||||
| import org.slf4j.LoggerFactory; | |||||
| import org.springframework.beans.factory.annotation.Value; | import org.springframework.beans.factory.annotation.Value; | ||||
| import org.springframework.data.domain.Page; | import org.springframework.data.domain.Page; | ||||
| import org.springframework.data.domain.PageImpl; | import org.springframework.data.domain.PageImpl; | ||||
| @@ -37,6 +39,8 @@ import java.util.*; | |||||
| */ | */ | ||||
| @Service("experimentInsService") | @Service("experimentInsService") | ||||
| public class ExperimentInsServiceImpl implements ExperimentInsService { | public class ExperimentInsServiceImpl implements ExperimentInsService { | ||||
| private static final Logger logger = LoggerFactory.getLogger(ExperimentInsServiceImpl.class); | |||||
| @Resource | @Resource | ||||
| private ExperimentInsDao experimentInsDao; | private ExperimentInsDao experimentInsDao; | ||||
| @Resource | @Resource | ||||
| @@ -209,7 +213,7 @@ public class ExperimentInsServiceImpl implements ExperimentInsService { | |||||
| public String removeById(Integer id) { | public String removeById(Integer id) { | ||||
| ExperimentIns experimentIns = experimentInsDao.queryById(id); | ExperimentIns experimentIns = experimentInsDao.queryById(id); | ||||
| if (experimentIns == null) { | if (experimentIns == null) { | ||||
| return "实验实例不存在"; | |||||
| throw new RuntimeException("实验实例不存在"); | |||||
| } | } | ||||
| //判断权限,只有admin和创建者本身可以删除该实验实例 | //判断权限,只有admin和创建者本身可以删除该实验实例 | ||||
| @@ -217,14 +221,14 @@ public class ExperimentInsServiceImpl implements ExperimentInsService { | |||||
| String username = loginUser.getUsername(); | String username = loginUser.getUsername(); | ||||
| String createdBy = experimentIns.getCreateBy(); | String createdBy = experimentIns.getCreateBy(); | ||||
| if (!(StringUtils.equals(username, "admin") || StringUtils.equals(username, createdBy))) { | if (!(StringUtils.equals(username, "admin") || StringUtils.equals(username, createdBy))) { | ||||
| return "无权限删除该流水线"; | |||||
| throw new RuntimeException("无权限删除该流水线"); | |||||
| } | } | ||||
| if (StringUtils.isEmpty(experimentIns.getStatus())) { | if (StringUtils.isEmpty(experimentIns.getStatus())) { | ||||
| experimentIns = queryStatusFromArgo(experimentIns); | experimentIns = queryStatusFromArgo(experimentIns); | ||||
| } | } | ||||
| if (StringUtils.equals(experimentIns.getStatus(), "Running")) { | |||||
| return "实验实例正在运行,不可删除"; | |||||
| if (StringUtils.equals(experimentIns.getStatus(), Constant.Running)) { | |||||
| throw new RuntimeException("实验实例正在运行,不可删除"); | |||||
| } | } | ||||
| experimentIns.setState(0); | experimentIns.setState(0); | ||||
| int update = this.experimentInsDao.update(experimentIns); | int update = this.experimentInsDao.update(experimentIns); | ||||
| @@ -362,7 +366,7 @@ public class ExperimentInsServiceImpl implements ExperimentInsService { | |||||
| } | } | ||||
| // 只有状态是"Running"时才能终止实例 | // 只有状态是"Running"时才能终止实例 | ||||
| if (!currentStatus.equalsIgnoreCase("Running")) { | |||||
| if (!currentStatus.equalsIgnoreCase(Constant.Running)) { | |||||
| throw new Exception("终止错误,只有运行状态的实例才能终止"); // 如果不是"Running"状态,则不执行终止操作 | throw new Exception("终止错误,只有运行状态的实例才能终止"); // 如果不是"Running"状态,则不执行终止操作 | ||||
| } | } | ||||
| @@ -430,40 +434,44 @@ public class ExperimentInsServiceImpl implements ExperimentInsService { | |||||
| } | } | ||||
| void deleteExportVersion(ExperimentIns experimentIns) throws Exception { | |||||
| LoginUser loginUser = SecurityUtils.getLoginUser(); | |||||
| String ci4sUsername = loginUser.getUsername(); | |||||
| String nodesResult = experimentIns.getNodesResult(); | |||||
| if (StringUtils.isNotEmpty(nodesResult)) { | |||||
| Map<String, Object> nodesResultMap = JsonUtils.jsonToMap(nodesResult); | |||||
| Map<String, Object> paramOutput = (Map<String, Object>) nodesResultMap.get("param_output"); | |||||
| for (String key : paramOutput.keySet()) { | |||||
| //删除导出模型版本 | |||||
| if (key.contains("model-export")) { | |||||
| HashMap queryMap = new HashMap<String, Integer>(); | |||||
| queryMap.put("insId", experimentIns.getId()); | |||||
| ModelDependency1 modelDependency1 = modelDependency1Dao.queryByInsId(JSON.toJSONString(queryMap)); | |||||
| if (modelDependency1 != null) { | |||||
| if (StringUtils.isNotEmpty(modelDependency1.getVersion())) { | |||||
| String relativePath = ci4sUsername + "/model/" + modelDependency1.getRepoId() + "/" + modelDependency1.getIdentifier() + "/" + modelDependency1.getVersion() + "/model"; | |||||
| modelsService.deleteVersion(modelDependency1.getRepoId(), modelDependency1.getIdentifier(), modelDependency1.getOwner(), modelDependency1.getVersion(), relativePath); | |||||
| } else { | |||||
| modelDependency1Dao.deleteModelById(modelDependency1.getId()); | |||||
| void deleteExportVersion(ExperimentIns experimentIns) { | |||||
| try { | |||||
| LoginUser loginUser = SecurityUtils.getLoginUser(); | |||||
| String ci4sUsername = loginUser.getUsername(); | |||||
| String nodesResult = experimentIns.getNodesResult(); | |||||
| if (StringUtils.isNotEmpty(nodesResult)) { | |||||
| Map<String, Object> nodesResultMap = JsonUtils.jsonToMap(nodesResult); | |||||
| Map<String, Object> paramOutput = (Map<String, Object>) nodesResultMap.get("param_output"); | |||||
| for (String key : paramOutput.keySet()) { | |||||
| //删除导出模型版本 | |||||
| if (key.contains("model-export")) { | |||||
| HashMap queryMap = new HashMap<String, Integer>(); | |||||
| queryMap.put("insId", experimentIns.getId()); | |||||
| ModelDependency1 modelDependency1 = modelDependency1Dao.queryByInsId(JSON.toJSONString(queryMap)); | |||||
| if (modelDependency1 != null) { | |||||
| if (StringUtils.isNotEmpty(modelDependency1.getVersion())) { | |||||
| String relativePath = ci4sUsername + "/model/" + modelDependency1.getRepoId() + "/" + modelDependency1.getIdentifier() + "/" + modelDependency1.getVersion() + "/model"; | |||||
| modelsService.deleteVersion(modelDependency1.getRepoId(), modelDependency1.getIdentifier(), modelDependency1.getOwner(), modelDependency1.getVersion(), relativePath); | |||||
| } else { | |||||
| modelDependency1Dao.deleteModelById(modelDependency1.getId()); | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| } | |||||
| //删除导出数据集版本 | |||||
| if (key.contains("dataset-export")) { | |||||
| HashMap queryMap = new HashMap<String, Integer>(); | |||||
| queryMap.put("ins_id", experimentIns.getId()); | |||||
| DatasetTempStorage datasetTempStorage = datasetTempStorageDao.queryByInsId(JSON.toJSONString(queryMap)); | |||||
| String relativePath = ci4sUsername + "/datasets/" + datasetTempStorage.getRepoId() + "/" + datasetTempStorage.getName() + "/" + datasetTempStorage.getVersion() + "/dataset"; | |||||
| newDatasetService.deleteDatasetVersionNew(datasetTempStorage.getRepoId(), datasetTempStorage.getName(), datasetTempStorage.getCreateBy(), datasetTempStorage.getVersion(), relativePath); | |||||
| //删除导出数据集版本 | |||||
| if (key.contains("dataset-export")) { | |||||
| HashMap queryMap = new HashMap<String, Integer>(); | |||||
| queryMap.put("ins_id", experimentIns.getId()); | |||||
| DatasetTempStorage datasetTempStorage = datasetTempStorageDao.queryByInsId(JSON.toJSONString(queryMap)); | |||||
| String relativePath = ci4sUsername + "/datasets/" + datasetTempStorage.getRepoId() + "/" + datasetTempStorage.getName() + "/" + datasetTempStorage.getVersion() + "/dataset"; | |||||
| newDatasetService.deleteDatasetVersionNew(datasetTempStorage.getRepoId(), datasetTempStorage.getName(), datasetTempStorage.getCreateBy(), datasetTempStorage.getVersion(), relativePath); | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| } catch (Exception e) { | |||||
| logger.error(e.getMessage()); | |||||
| } | } | ||||
| } | } | ||||
| @@ -237,6 +237,9 @@ public class ExperimentServiceImpl implements ExperimentService { | |||||
| throw new Exception("转换流水线失败"); | throw new Exception("转换流水线失败"); | ||||
| } | } | ||||
| Map<String, Object> converMap = JsonUtils.jsonToMap(convertRes); | Map<String, Object> converMap = JsonUtils.jsonToMap(convertRes); | ||||
| if (converMap.get("data") == null) { | |||||
| throw new RuntimeException("转换流水线失败"); | |||||
| } | |||||
| // 判断积分和资源是否足够 | // 判断积分和资源是否足够 | ||||
| Map<String, Map<String, Object>> resourceInfo = (Map<String, Map<String, Object>>) converMap.get("resource_info"); | Map<String, Map<String, Object>> resourceInfo = (Map<String, Map<String, Object>>) converMap.get("resource_info"); | ||||
| @@ -466,7 +466,7 @@ public class ImageServiceImpl implements ImageService { | |||||
| imageVo.setValue(resultMap.get("imageName")); | imageVo.setValue(resultMap.get("imageName")); | ||||
| imageVo.setVersion(String.valueOf(imageVersion.getId())); | imageVo.setVersion(String.valueOf(imageVersion.getId())); | ||||
| resultMap.put("id", String.valueOf(oldImage.getId())); | |||||
| resultMap.put("id", String.valueOf(image.getId())); | |||||
| resultMap.put("version", String.valueOf(imageVersion.getId())); | resultMap.put("version", String.valueOf(imageVersion.getId())); | ||||
| resultMap.put("value",resultMap.get("imageName")); | resultMap.put("value",resultMap.get("imageName")); | ||||
| @@ -9,8 +9,11 @@ import com.ruoyi.platform.service.ResourceOccupyService; | |||||
| import com.ruoyi.platform.utils.DateUtils; | import com.ruoyi.platform.utils.DateUtils; | ||||
| import com.ruoyi.platform.utils.HttpUtils; | import com.ruoyi.platform.utils.HttpUtils; | ||||
| import com.ruoyi.platform.utils.JsonUtils; | import com.ruoyi.platform.utils.JsonUtils; | ||||
| import com.ruoyi.platform.utils.MinioUtil; | |||||
| import com.ruoyi.system.api.constant.Constant; | import com.ruoyi.system.api.constant.Constant; | ||||
| import org.apache.commons.lang3.StringUtils; | import org.apache.commons.lang3.StringUtils; | ||||
| import org.slf4j.Logger; | |||||
| import org.slf4j.LoggerFactory; | |||||
| import org.springframework.beans.factory.annotation.Value; | import org.springframework.beans.factory.annotation.Value; | ||||
| import org.springframework.stereotype.Service; | import org.springframework.stereotype.Service; | ||||
| import org.springframework.data.domain.Page; | import org.springframework.data.domain.Page; | ||||
| @@ -23,6 +26,8 @@ import java.util.*; | |||||
| @Service | @Service | ||||
| public class MachineLearnInsServiceImpl implements MachineLearnInsService { | public class MachineLearnInsServiceImpl implements MachineLearnInsService { | ||||
| private static final Logger logger = LoggerFactory.getLogger(MachineLearnInsServiceImpl.class); | |||||
| @Value("${argo.url}") | @Value("${argo.url}") | ||||
| private String argoUrl; | private String argoUrl; | ||||
| @Value("${argo.workflowStatus}") | @Value("${argo.workflowStatus}") | ||||
| @@ -36,6 +41,8 @@ public class MachineLearnInsServiceImpl implements MachineLearnInsService { | |||||
| private MachineLearnDao machineLearnDao; | private MachineLearnDao machineLearnDao; | ||||
| @Resource | @Resource | ||||
| private ResourceOccupyService resourceOccupyService; | private ResourceOccupyService resourceOccupyService; | ||||
| @Resource | |||||
| private MinioUtil minioUtil; | |||||
| @Override | @Override | ||||
| public Page<MachineLearnIns> queryByPage(Long machineLearnId, PageRequest pageRequest) { | public Page<MachineLearnIns> queryByPage(Long machineLearnId, PageRequest pageRequest) { | ||||
| @@ -237,6 +244,9 @@ public class MachineLearnInsServiceImpl implements MachineLearnInsService { | |||||
| if (Constant.Running.equals(machineLearnIns.getStatus()) || Constant.Pending.equals(machineLearnIns.getStatus())) { | if (Constant.Running.equals(machineLearnIns.getStatus()) || Constant.Pending.equals(machineLearnIns.getStatus())) { | ||||
| machineLearnIns = queryStatusFromArgo(machineLearnIns); | machineLearnIns = queryStatusFromArgo(machineLearnIns); | ||||
| } | } | ||||
| if (Constant.ML_VideoClassification.equals(machineLearnIns.getType())) { | |||||
| getFileList(machineLearnIns); | |||||
| } | |||||
| return machineLearnIns; | return machineLearnIns; | ||||
| } | } | ||||
| @@ -256,4 +266,24 @@ public class MachineLearnInsServiceImpl implements MachineLearnInsService { | |||||
| machineLearnDao.edit(machineLearn); | machineLearnDao.edit(machineLearn); | ||||
| } | } | ||||
| } | } | ||||
| public void getFileList(MachineLearnIns ins) { | |||||
| String directoryPath = ins.getResultPath(); | |||||
| try { | |||||
| String bucketName = directoryPath.substring(0, directoryPath.indexOf("/")); | |||||
| String prefix = directoryPath.substring(directoryPath.indexOf("/") + 1, directoryPath.length()) + "/"; | |||||
| List<Map> fileMaps = minioUtil.listRayFilesInDirectory(bucketName, prefix); | |||||
| Map<Object, Object> fileMap = new HashMap<>(); | |||||
| fileMap.put("children", fileMaps); | |||||
| fileMap.put("url", directoryPath); | |||||
| fileMap.put("isDirectory", true); | |||||
| fileMap.put("size", "0 B"); | |||||
| fileMap.put("name", "result file"); | |||||
| ins.setFileMap(fileMap); | |||||
| } catch (Exception e) { | |||||
| logger.error("未找到结果文件"); | |||||
| } | |||||
| } | |||||
| } | } | ||||
| @@ -63,7 +63,7 @@ public class MachineLearnServiceImpl implements MachineLearnService { | |||||
| @Override | @Override | ||||
| public MachineLearn add(MachineLearn machineLearn) { | public MachineLearn add(MachineLearn machineLearn) { | ||||
| if (machineLearn.getName().length() >= 64) { | |||||
| if (machineLearn.getName().length() > 64) { | |||||
| throw new RuntimeException("实验名称大于最大长度"); | throw new RuntimeException("实验名称大于最大长度"); | ||||
| } | } | ||||
| MachineLearn machineLearnByName = machineLearnDao.getMachineLearnByName(machineLearn.getName()); | MachineLearn machineLearnByName = machineLearnDao.getMachineLearnByName(machineLearn.getName()); | ||||
| @@ -105,6 +105,12 @@ public class MachineLearnServiceImpl implements MachineLearnService { | |||||
| if (!(StringUtils.equals(username, "admin") || StringUtils.equals(username, createBy))) { | if (!(StringUtils.equals(username, "admin") || StringUtils.equals(username, createBy))) { | ||||
| throw new RuntimeException("无权限删除该实验"); | throw new RuntimeException("无权限删除该实验"); | ||||
| } | } | ||||
| List<MachineLearnIns> insList = machineLearnInsDao.getByMachineLearnId(machineLearn.getId()); | |||||
| if (!insList.isEmpty()) { | |||||
| throw new RuntimeException("该实验存在实例,无法删除"); | |||||
| } | |||||
| machineLearn.setUpdateBy(SecurityUtils.getLoginUser().getUsername()); | machineLearn.setUpdateBy(SecurityUtils.getLoginUser().getUsername()); | ||||
| machineLearn.setState(Constant.State_invalid); | machineLearn.setState(Constant.State_invalid); | ||||
| resourceOccupyService.deleteTaskState(Constant.TaskType_ML, id, null); | resourceOccupyService.deleteTaskState(Constant.TaskType_ML, id, null); | ||||
| @@ -159,6 +165,12 @@ public class MachineLearnServiceImpl implements MachineLearnService { | |||||
| if (convertRes == null || StringUtils.isEmpty(convertRes)) { | if (convertRes == null || StringUtils.isEmpty(convertRes)) { | ||||
| throw new RuntimeException("转换流水线失败"); | throw new RuntimeException("转换流水线失败"); | ||||
| } | } | ||||
| Map<String, Object> convertResMap = JsonUtils.jsonToMap(convertRes); | |||||
| if (convertResMap.get("data")== null) { | |||||
| throw new RuntimeException("转换流水线失败"); | |||||
| } | |||||
| Map<String, Object> converMap = JsonUtils.jsonToMap(convertRes); | Map<String, Object> converMap = JsonUtils.jsonToMap(convertRes); | ||||
| // 组装运行接口json | // 组装运行接口json | ||||
| Map<String, Object> output = (Map<String, Object>) converMap.get("output"); | Map<String, Object> output = (Map<String, Object>) converMap.get("output"); | ||||
| @@ -191,7 +203,8 @@ public class MachineLearnServiceImpl implements MachineLearnService { | |||||
| Map<String, Object> param_output = (Map<String, Object>) output.get("param_output"); | Map<String, Object> param_output = (Map<String, Object>) output.get("param_output"); | ||||
| List output1 = (ArrayList) param_output.values().toArray()[0]; | List output1 = (ArrayList) param_output.values().toArray()[0]; | ||||
| Map<String, String> output2 = (Map<String, String>) output1.get(0); | Map<String, String> output2 = (Map<String, String>) output1.get(0); | ||||
| String outputPath = minioEndpoint + "/" + output2.get("path").replace("{{workflow.name}}", (String) metadata.get("name")) + "/"; | |||||
| String outputStr = output2.get("path").replace("{{workflow.name}}", (String) metadata.get("name")); | |||||
| String outputPath = minioEndpoint + "/" + outputStr + "/"; | |||||
| switch (machineLearn.getType()) { | switch (machineLearn.getType()) { | ||||
| case Constant.ML_CSV: { | case Constant.ML_CSV: { | ||||
| @@ -208,13 +221,13 @@ public class MachineLearnServiceImpl implements MachineLearnService { | |||||
| } | } | ||||
| case Constant.ML_TextClassification: { | case Constant.ML_TextClassification: { | ||||
| machineLearnIns.setModelPath(outputPath + "saved_dict/" + modelType + ".ckpt"); | machineLearnIns.setModelPath(outputPath + "saved_dict/" + modelType + ".ckpt"); | ||||
| machineLearnIns.setRunHistoryPath(output2.get("path").replace("{{workflow.name}}", (String) metadata.get("name")).substring("data/".length()) + "/log/" + modelType); | |||||
| machineLearnIns.setRunHistoryPath(outputStr.substring("data/".length()) + "/log/" + modelType); | |||||
| machineLearnIns.setResultPath(outputPath + "log/" + modelType + "/result.txt"); | machineLearnIns.setResultPath(outputPath + "log/" + modelType + "/result.txt"); | ||||
| break; | break; | ||||
| } | } | ||||
| case Constant.ML_VideoClassification: { | case Constant.ML_VideoClassification: { | ||||
| machineLearnIns.setResultPath(outputPath); | |||||
| machineLearnIns.setRunHistoryPath(output2.get("path").replace("{{workflow.name}}", (String) metadata.get("name")).substring("data/".length()) + "/log"); | |||||
| machineLearnIns.setResultPath(outputStr); | |||||
| machineLearnIns.setRunHistoryPath(outputStr.substring("data/".length()) + "/log"); | |||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| @@ -245,7 +245,7 @@ public class ModelDependencyServiceImpl implements ModelDependencyService { | |||||
| } | } | ||||
| @Override | @Override | ||||
| public String removeById(Integer id) { | |||||
| public String removeById(Integer id) throws Exception { | |||||
| ModelDependency modelDependency = this.modelDependencyDao.queryById(id); | ModelDependency modelDependency = this.modelDependencyDao.queryById(id); | ||||
| if (modelDependency == null){ | if (modelDependency == null){ | ||||
| return "模型依赖信息不存在"; | return "模型依赖信息不存在"; | ||||
| @@ -256,7 +256,7 @@ public class ModelDependencyServiceImpl implements ModelDependencyService { | |||||
| String username = loginUser.getUsername(); | String username = loginUser.getUsername(); | ||||
| String createdBy = modelDependency.getCreateBy(); | String createdBy = modelDependency.getCreateBy(); | ||||
| if (!(StringUtils.equals(username,"admin") || StringUtils.equals(username,createdBy))){ | if (!(StringUtils.equals(username,"admin") || StringUtils.equals(username,createdBy))){ | ||||
| return "无权限删除"; | |||||
| throw new Exception("无权限删除"); | |||||
| } | } | ||||
| modelDependency.setState(0); | modelDependency.setState(0); | ||||
| @@ -194,7 +194,7 @@ public class ModelsVersionServiceImpl implements ModelsVersionService { | |||||
| } | } | ||||
| @Override | @Override | ||||
| public Map<Integer, String> deleteModelsVersion(Integer modelsId, String version) throws IOException { | |||||
| public Map<Integer, String> deleteModelsVersion(Integer modelsId, String version) throws Exception { | |||||
| Map<Integer, String> results = new HashMap<Integer,String>(); | Map<Integer, String> results = new HashMap<Integer,String>(); | ||||
| // 根据模型ID和版本查询所有模型版本 | // 根据模型ID和版本查询所有模型版本 | ||||
| List<ModelsVersion> versions = this.modelsVersionDao.queryAllByModelsVersion(modelsId, version); | List<ModelsVersion> versions = this.modelsVersionDao.queryAllByModelsVersion(modelsId, version); | ||||
| @@ -63,7 +63,7 @@ public class RayServiceImpl implements RayService { | |||||
| @Override | @Override | ||||
| public Ray save(RayVo rayVo) throws Exception { | public Ray save(RayVo rayVo) throws Exception { | ||||
| if (rayVo.getName().length() >= 64) { | |||||
| if (rayVo.getName().length() > 64) { | |||||
| throw new RuntimeException("实验名称大于最大长度"); | throw new RuntimeException("实验名称大于最大长度"); | ||||
| } | } | ||||
| Ray rayByName = rayDao.getRayByName(rayVo.getName()); | Ray rayByName = rayDao.getRayByName(rayVo.getName()); | ||||
| @@ -175,7 +175,12 @@ public class RayServiceImpl implements RayService { | |||||
| if (convertRes == null || StringUtils.isEmpty(convertRes)) { | if (convertRes == null || StringUtils.isEmpty(convertRes)) { | ||||
| throw new RuntimeException("转换流水线失败"); | throw new RuntimeException("转换流水线失败"); | ||||
| } | } | ||||
| Map<String, Object> converMap = JsonUtils.jsonToMap(convertRes); | Map<String, Object> converMap = JsonUtils.jsonToMap(convertRes); | ||||
| if (converMap.get("data") == null) { | |||||
| throw new RuntimeException("转换流水线失败"); | |||||
| } | |||||
| // 组装运行接口json | // 组装运行接口json | ||||
| Map<String, Object> output = (Map<String, Object>) converMap.get("output"); | Map<String, Object> output = (Map<String, Object>) converMap.get("output"); | ||||
| Map<String, Object> runReqMap = new HashMap<>(); | Map<String, Object> runReqMap = new HashMap<>(); | ||||
| @@ -15,6 +15,7 @@ import org.springframework.stereotype.Service; | |||||
| import org.springframework.transaction.annotation.Transactional; | import org.springframework.transaction.annotation.Transactional; | ||||
| import javax.annotation.Resource; | import javax.annotation.Resource; | ||||
| import java.math.BigDecimal; | |||||
| import java.util.Date; | import java.util.Date; | ||||
| import java.util.HashMap; | import java.util.HashMap; | ||||
| import java.util.List; | import java.util.List; | ||||
| @@ -113,9 +114,11 @@ public class ResourceOccupyServiceImpl implements ResourceOccupyService { | |||||
| } | } | ||||
| Double hours = (double) timeDifferenceMillis / (1000 * 60 * 60); | Double hours = (double) timeDifferenceMillis / (1000 * 60 * 60); | ||||
| Double deduceCredit = resourceOccupy.getCreditPerHour() * hours; | Double deduceCredit = resourceOccupy.getCreditPerHour() * hours; | ||||
| resourceOccupyDao.deduceCredit(deduceCredit, resourceOccupy.getUserId()); | |||||
| double deduceCreditTrun = new BigDecimal(deduceCredit).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue(); | |||||
| deduceCreditTrun = deduceCreditTrun > 0 ? deduceCreditTrun : 0.01; | |||||
| resourceOccupyDao.deduceCredit(deduceCreditTrun, resourceOccupy.getUserId()); | |||||
| resourceOccupy.setDeduceCredit(resourceOccupy.getDeduceCredit() + deduceCredit); | |||||
| resourceOccupy.setDeduceCredit(resourceOccupy.getDeduceCredit() + deduceCreditTrun); | |||||
| resourceOccupy.setDeduceLastTime(now); | resourceOccupy.setDeduceLastTime(now); | ||||
| resourceOccupy.setState(Constant.State_valid); | resourceOccupy.setState(Constant.State_valid); | ||||
| resourceOccupyDao.edit(resourceOccupy); | resourceOccupyDao.edit(resourceOccupy); | ||||
| @@ -171,6 +174,6 @@ public class ResourceOccupyServiceImpl implements ResourceOccupyService { | |||||
| @Override | @Override | ||||
| public void deleteTaskState(String taskType, Long taskId, Long taskInsId) { | public void deleteTaskState(String taskType, Long taskId, Long taskInsId) { | ||||
| resourceOccupyDao.deleteTaskState(taskType,taskId,taskInsId); | |||||
| resourceOccupyDao.deleteTaskState(taskType, taskId, taskInsId); | |||||
| } | } | ||||
| } | } | ||||
| @@ -80,10 +80,10 @@ public class WorkflowParamServiceImpl implements WorkflowParamService { | |||||
| } | } | ||||
| @Override | @Override | ||||
| public String removeById(Integer id) { | |||||
| public String removeById(Integer id) throws Exception { | |||||
| WorkflowParam workflowParam = this.workflowParamDao.queryById(id); | WorkflowParam workflowParam = this.workflowParamDao.queryById(id); | ||||
| if (workflowParam == null){ | if (workflowParam == null){ | ||||
| return "流水线参数不存在"; | |||||
| throw new Exception("流水线参数不存在"); | |||||
| } | } | ||||
| //判断权限,只有admin和创建者本身可以删除 | //判断权限,只有admin和创建者本身可以删除 | ||||
| @@ -93,7 +93,7 @@ public class WorkflowParamServiceImpl implements WorkflowParamService { | |||||
| String createdBy = workflowParam.getCreateBy(); | String createdBy = workflowParam.getCreateBy(); | ||||
| if (!(StringUtils.equals(username,"admin") || StringUtils.equals(username,createdBy))){ | if (!(StringUtils.equals(username,"admin") || StringUtils.equals(username,createdBy))){ | ||||
| return "无权限删除该流水线参数"; | |||||
| throw new Exception("无权限删除该流水线参数"); | |||||
| } | } | ||||
| workflowParam.setState(0); | workflowParam.setState(0); | ||||
| @@ -180,12 +180,13 @@ public class WorkflowServiceImpl implements WorkflowService { | |||||
| Workflow workflow = this.queryById(id); | Workflow workflow = this.queryById(id); | ||||
| if (workflow != null) { | if (workflow != null) { | ||||
| try { | try { | ||||
| if (workflow.getName().length() >= 64) { | |||||
| Workflow duplicateWorkflow = new Workflow(); | |||||
| duplicateWorkflow.setName(workflow.getName() + "-copy-" + UUID.randomUUID().toString().substring(0, 6)); | |||||
| if (duplicateWorkflow.getName().length() > 64) { | |||||
| throw new RuntimeException("流水线名称大于最大长度"); | throw new RuntimeException("流水线名称大于最大长度"); | ||||
| } | } | ||||
| Workflow duplicateWorkflow = new Workflow(); | |||||
| duplicateWorkflow.setName(workflow.getName() + "-copy-" + UUID.randomUUID().toString().substring(0, 6)); | |||||
| String oldDag = workflow.getDag(); | String oldDag = workflow.getDag(); | ||||
| // 创建请求数据的Json(map) | // 创建请求数据的Json(map) | ||||
| Map<String, Object> requestData = new HashMap<>(); | Map<String, Object> requestData = new HashMap<>(); | ||||
| @@ -350,7 +350,7 @@ public class MinioUtil { | |||||
| map.put("name", fileName); | map.put("name", fileName); | ||||
| map.put("size", formattedSize); | map.put("size", formattedSize); | ||||
| if ((fileName.startsWith("run") || fileName.startsWith("checkpoint")) && fileSize == 0) { | |||||
| if ((fileName.startsWith("run") || fileName.startsWith("checkpoint") || fileName.equals("log")) && fileSize == 0) { | |||||
| map.put("isDirectory", true); | map.put("isDirectory", true); | ||||
| map.put("children", listRayFilesInDirectory(bucketName, fullPath)); | map.put("children", listRayFilesInDirectory(bucketName, fullPath)); | ||||
| } else { | } else { | ||||
| @@ -2,10 +2,10 @@ | |||||
| <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> | <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> | ||||
| <mapper namespace="com.ruoyi.platform.mapper.CodeConfigDao"> | <mapper namespace="com.ruoyi.platform.mapper.CodeConfigDao"> | ||||
| <insert id="insert"> | |||||
| insert into code_config(code_repo_name, code_repo_vis, git_url, git_branch, verify_mode, git_user_name, | |||||
| <insert id="insert" keyProperty="id" useGeneratedKeys="true"> | |||||
| insert into code_config(code_repo_name, is_public, git_url, git_branch, verify_mode, git_user_name, | |||||
| git_password, ssh_key, create_by, create_time, update_by, update_time) | git_password, ssh_key, create_by, create_time, update_by, update_time) | ||||
| values (#{codeConfig.codeRepoName}, #{codeConfig.codeRepoVis}, #{codeConfig.gitUrl}, #{codeConfig.gitBranch}, | |||||
| values (#{codeConfig.codeRepoName}, #{codeConfig.isPublic}, #{codeConfig.gitUrl}, #{codeConfig.gitBranch}, | |||||
| #{codeConfig.verifyMode}, #{codeConfig.gitUserName}, #{codeConfig.gitPassword}, | #{codeConfig.verifyMode}, #{codeConfig.gitUserName}, #{codeConfig.gitPassword}, | ||||
| #{codeConfig.sshKey}, #{codeConfig.createBy}, #{codeConfig.createTime}, #{codeConfig.updateBy}, | #{codeConfig.sshKey}, #{codeConfig.createBy}, #{codeConfig.createTime}, #{codeConfig.updateBy}, | ||||
| #{codeConfig.updateTime}) | #{codeConfig.updateTime}) | ||||
| @@ -17,8 +17,8 @@ | |||||
| <if test="codeConfig.codeRepoName != null and codeConfig.codeRepoName != ''"> | <if test="codeConfig.codeRepoName != null and codeConfig.codeRepoName != ''"> | ||||
| code_repo_name = #{codeConfig.codeRepoName}, | code_repo_name = #{codeConfig.codeRepoName}, | ||||
| </if> | </if> | ||||
| <if test="codeConfig.codeRepoVis != null"> | |||||
| code_repo_vis = #{codeConfig.codeRepoVis}, | |||||
| <if test="codeConfig.isPublic != null"> | |||||
| is_public = #{codeConfig.isPublic}, | |||||
| </if> | </if> | ||||
| <if test="codeConfig.gitUrl != null"> | <if test="codeConfig.gitUrl != null"> | ||||
| git_url = #{codeConfig.gitUrl}, | git_url = #{codeConfig.gitUrl}, | ||||
| @@ -78,6 +78,7 @@ | |||||
| <sql id="common_condition"> | <sql id="common_condition"> | ||||
| <where> | <where> | ||||
| state = 1 | state = 1 | ||||
| and code_repo_vis = 1 | |||||
| <if test="codeConfig.id != null"> | <if test="codeConfig.id != null"> | ||||
| and id = #{codeConfig.id} | and id = #{codeConfig.id} | ||||
| </if> | </if> | ||||
| @@ -85,7 +86,7 @@ | |||||
| and code_repo_name LIKE CONCAT('%', #{codeConfig.codeRepoName}, '%') | and code_repo_name LIKE CONCAT('%', #{codeConfig.codeRepoName}, '%') | ||||
| </if> | </if> | ||||
| <if test="codeConfig.codeRepoVis != null"> | <if test="codeConfig.codeRepoVis != null"> | ||||
| and code_repo_vis = #{codeConfig.codeRepoVis} | |||||
| and is_public = #{codeConfig.isPublic} | |||||
| </if> | </if> | ||||
| <if test="codeConfig.gitUrl != null"> | <if test="codeConfig.gitUrl != null"> | ||||
| and git_url = #{codeConfig.gitUrl} | and git_url = #{codeConfig.gitUrl} | ||||
| @@ -104,7 +104,7 @@ | |||||
| user_id, | user_id, | ||||
| description, | description, | ||||
| credit_per_hour, | credit_per_hour, | ||||
| TRUNCATE(deduce_credit, 1) as deduce_credit, | |||||
| deduce_credit, | |||||
| start_time, | start_time, | ||||
| task_type, | task_type, | ||||
| task_id, | task_id, | ||||
| @@ -119,13 +119,15 @@ | |||||
| </select> | </select> | ||||
| <select id="getUserCredit" resultType="java.lang.Double"> | <select id="getUserCredit" resultType="java.lang.Double"> | ||||
| select TRUNCATE(credit, 1) as credit | |||||
| select | |||||
| credit | |||||
| from sys_user | from sys_user | ||||
| where user_id = #{userId} | where user_id = #{userId} | ||||
| </select> | </select> | ||||
| <select id="getDeduceCredit" resultType="java.lang.Double"> | <select id="getDeduceCredit" resultType="java.lang.Double"> | ||||
| select TRUNCATE(sum(deduce_credit), 1) as deduce_credit | |||||
| select | |||||
| sum(deduce_credit) as deduce_credit | |||||
| from resource_occupy | from resource_occupy | ||||
| where user_id = #{userId} | where user_id = #{userId} | ||||
| </select> | </select> | ||||