Browse Source

Merge pull request '合并dev' (#221) from dev into dev-check

pull/225/head
cp3hnu 9 months ago
parent
commit
039ad81fc7
100 changed files with 1105 additions and 582 deletions
  1. +2
    -0
      react-ui/src/app.tsx
  2. +2
    -3
      react-ui/src/components/CodeConfigItem/index.tsx
  3. +35
    -0
      react-ui/src/components/RunDuration/index.tsx
  4. +3
    -3
      react-ui/src/enums/index.ts
  5. +17
    -23
      react-ui/src/hooks/useSSE.ts
  6. +53
    -0
      react-ui/src/hooks/useServerTime.ts
  7. +1
    -1
      react-ui/src/iconfont/iconfont-menu.js
  8. +15
    -1
      react-ui/src/iconfont/iconfont-menu.json
  9. +1
    -1
      react-ui/src/iconfont/iconfont.js
  10. +4
    -3
      react-ui/src/pages/ActiveLearn/Instance/index.tsx
  11. +15
    -53
      react-ui/src/pages/ActiveLearn/components/ActiveLearnBasic/index.tsx
  12. +3
    -3
      react-ui/src/pages/ActiveLearn/components/CreateForm/ExecuteConfig.tsx
  13. +4
    -3
      react-ui/src/pages/AutoML/Instance/index.tsx
  14. +14
    -48
      react-ui/src/pages/AutoML/components/AutoMLBasic/index.tsx
  15. +6
    -4
      react-ui/src/pages/AutoML/components/ExperimentInstanceList/index.less
  16. +14
    -29
      react-ui/src/pages/AutoML/components/ExperimentInstanceList/index.tsx
  17. +68
    -0
      react-ui/src/pages/AutoML/components/ExperimentInstanceList/instance.tsx
  18. +100
    -46
      react-ui/src/pages/AutoML/components/ExperimentList/index.tsx
  19. +70
    -0
      react-ui/src/pages/AutoML/components/ExperimentRunBasic/index.tsx
  20. +1
    -1
      react-ui/src/pages/CodeConfig/List/index.tsx
  21. +8
    -9
      react-ui/src/pages/CodeConfig/components/AddCodeConfigModal/index.tsx
  22. +2
    -3
      react-ui/src/pages/CodeConfig/components/CodeConfigItem/index.tsx
  23. +25
    -1
      react-ui/src/pages/Dataset/components/ResourceInfo/index.less
  24. +36
    -2
      react-ui/src/pages/Dataset/components/ResourceInfo/index.tsx
  25. +20
    -4
      react-ui/src/pages/Dataset/components/ResourceItem/index.less
  26. +10
    -6
      react-ui/src/pages/Dataset/components/ResourceItem/index.tsx
  27. +2
    -0
      react-ui/src/pages/Dataset/config.tsx
  28. +11
    -11
      react-ui/src/pages/DevelopmentEnvironment/List/index.tsx
  29. +6
    -17
      react-ui/src/pages/Experiment/Info/index.jsx
  30. +4
    -19
      react-ui/src/pages/Experiment/components/ExperimentDrawer/index.tsx
  31. +0
    -0
      react-ui/src/pages/Experiment/components/ExperimentInstanceList/index.less
  32. +16
    -29
      react-ui/src/pages/Experiment/components/ExperimentInstanceList/index.tsx
  33. +70
    -0
      react-ui/src/pages/Experiment/components/ExperimentInstanceList/instance.tsx
  34. +91
    -42
      react-ui/src/pages/Experiment/index.jsx
  35. +5
    -4
      react-ui/src/pages/HyperParameter/Instance/index.tsx
  36. +10
    -8
      react-ui/src/pages/HyperParameter/components/HyperParameterBasic/index.tsx
  37. +3
    -2
      react-ui/src/pages/Mirror/Info/index.tsx
  38. +7
    -0
      react-ui/src/pages/ModelDeployment/CreateVersion/index.less
  39. +13
    -21
      react-ui/src/pages/ModelDeployment/CreateVersion/index.tsx
  40. +8
    -8
      react-ui/src/pages/ModelDeployment/ServiceInfo/index.tsx
  41. +6
    -6
      react-ui/src/pages/ModelDeployment/components/VersionBasicInfo/index.tsx
  42. +1
    -0
      react-ui/src/pages/Points/components/Statistics/index.less
  43. +10
    -3
      react-ui/src/pages/Points/components/Statistics/index.tsx
  44. +5
    -2
      react-ui/src/pages/Workspace/components/UserPoints/index.tsx
  45. +15
    -0
      react-ui/src/services/dataset/index.js
  46. +8
    -0
      react-ui/src/services/experiment/index.js
  47. +2
    -2
      react-ui/src/stories/docs/Less.mdx
  48. +5
    -5
      react-ui/src/stories/mockData.ts
  49. +3
    -0
      react-ui/src/utils/constant.ts
  50. +2
    -1
      react-ui/src/utils/date.ts
  51. +16
    -0
      react-ui/src/utils/format.ts
  52. +2
    -0
      react-ui/src/utils/index.ts
  53. +2
    -3
      ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/constant/Constant.java
  54. +2
    -2
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/activeLearn/ActiveLearnInsController.java
  55. +1
    -1
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/component/ComponentController.java
  56. +2
    -2
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/dataset/DatasetVersionController.java
  57. +23
    -20
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/experiment/ExperimentInsController.java
  58. +1
    -1
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/icon/AssetIconController.java
  59. +25
    -0
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/knowledgeGraph/KnowledgeGraphController.java
  60. +1
    -9
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/labelStudio/labelStudioController.java
  61. +1
    -1
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/model/ModelDependencyController.java
  62. +1
    -1
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/model/ModelsVersionController.java
  63. +1
    -1
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/resources/ComputingResourceController.java
  64. +1
    -1
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/workflow/WorkflowParamController.java
  65. +3
    -0
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/domain/CodeConfig.java
  66. +4
    -0
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/domain/MachineLearnIns.java
  67. +1
    -1
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/scheduling/ActiveLearnInsStatusTask.java
  68. +1
    -1
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/scheduling/AutoMlInsStatusTask.java
  69. +1
    -1
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/scheduling/ExperimentInstanceStatusTask.java
  70. +1
    -1
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/scheduling/MLStatusTask.java
  71. +1
    -1
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/scheduling/RayInsStatusTask.java
  72. +1
    -1
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/AssetIconService.java
  73. +1
    -1
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/ComponentService.java
  74. +1
    -1
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/ComputingResourceService.java
  75. +2
    -2
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/DatasetVersionService.java
  76. +1
    -1
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/ModelDependencyService.java
  77. +1
    -1
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/ModelsVersionService.java
  78. +1
    -1
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/WorkflowParamService.java
  79. +5
    -1
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/ActiveLearnServiceImpl.java
  80. +3
    -3
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/AssetIconServiceImpl.java
  81. +1
    -1
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/AutoMlServiceImpl.java
  82. +1
    -1
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/CodeConfigServiceImpl.java
  83. +19
    -19
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/ComponentServiceImpl.java
  84. +3
    -3
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/ComputingResourceServiceImpl.java
  85. +4
    -4
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/DatasetVersionServiceImpl.java
  86. +8
    -4
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/DevEnvironmentServiceImpl.java
  87. +43
    -35
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/ExperimentInsServiceImpl.java
  88. +3
    -0
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/ExperimentServiceImpl.java
  89. +1
    -1
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/ImageServiceImpl.java
  90. +30
    -0
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/MachineLearnInsServiceImpl.java
  91. +18
    -5
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/MachineLearnServiceImpl.java
  92. +2
    -2
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/ModelDependencyServiceImpl.java
  93. +1
    -1
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/ModelsVersionServiceImpl.java
  94. +6
    -1
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/RayServiceImpl.java
  95. +6
    -3
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/ResourceOccupyServiceImpl.java
  96. +3
    -3
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/WorkflowParamServiceImpl.java
  97. +4
    -3
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/WorkflowServiceImpl.java
  98. +1
    -1
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/utils/MinioUtil.java
  99. +7
    -6
      ruoyi-modules/management-platform/src/main/resources/mapper/managementPlatform/CodeConfigDaoMapper.xml
  100. +5
    -3
      ruoyi-modules/management-platform/src/main/resources/mapper/managementPlatform/ResourceOccupy.xml

+ 2
- 0
react-ui/src/app.tsx View File

@@ -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,


+ 2
- 3
react-ui/src/components/CodeConfigItem/index.tsx View File

@@ -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


+ 35
- 0
react-ui/src/components/RunDuration/index.tsx View File

@@ -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;

+ 3
- 3
react-ui/src/enums/index.ts View File

@@ -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', // 失败
} }


// 服务运行状态 // 服务运行状态


+ 17
- 23
react-ui/src/hooks/useSSE.ts View File

@@ -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]);
}; };

+ 53
- 0
react-ui/src/hooks/useServerTime.ts View File

@@ -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
- 1
react-ui/src/iconfont/iconfont-menu.js
File diff suppressed because it is too large
View File


+ 15
- 1
react-ui/src/iconfont/iconfont-menu.json View File

@@ -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": "模型开发",


+ 1
- 1
react-ui/src/iconfont/iconfont.js
File diff suppressed because it is too large
View File


+ 4
- 3
react-ui/src/pages/ActiveLearn/Instance/index.tsx View File

@@ -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
/> />
), ),


+ 15
- 53
react-ui/src/pages/ActiveLearn/components/ActiveLearnBasic/index.tsx View File

@@ -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 && (


+ 3
- 3
react-ui/src/pages/ActiveLearn/components/CreateForm/ExecuteConfig.tsx View File

@@ -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>


+ 4
- 3
react-ui/src/pages/AutoML/Instance/index.tsx View File

@@ -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
/> />
), ),


+ 14
- 48
react-ui/src/pages/AutoML/components/AutoMLBasic/index.tsx View File

@@ -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 && (


react-ui/src/pages/AutoML/components/ExperimentInstance/index.less → react-ui/src/pages/AutoML/components/ExperimentInstanceList/index.less View File

@@ -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 {

react-ui/src/pages/AutoML/components/ExperimentInstance/index.tsx → react-ui/src/pages/AutoML/components/ExperimentInstanceList/index.tsx View File

@@ -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;

+ 68
- 0
react-ui/src/pages/AutoML/components/ExperimentInstanceList/instance.tsx View File

@@ -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;

+ 100
- 46
react-ui/src/pages/AutoML/components/ExperimentList/index.tsx View File

@@ -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,


+ 70
- 0
react-ui/src/pages/AutoML/components/ExperimentRunBasic/index.tsx View File

@@ -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;

+ 1
- 1
react-ui/src/pages/CodeConfig/List/index.tsx View File

@@ -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;


+ 8
- 9
react-ui/src/pages/CodeConfig/components/AddCodeConfigModal/index.tsx View File

@@ -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="验证方式"


+ 2
- 3
react-ui/src/pages/CodeConfig/components/CodeConfigItem/index.tsx View File

@@ -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"


+ 25
- 1
react-ui/src/pages/Dataset/components/ResourceInfo/index.less View File

@@ -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;
} }
} }
} }


+ 36
- 2
react-ui/src/pages/Dataset/components/ResourceInfo/index.tsx View File

@@ -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}


+ 20
- 4
react-ui/src/pages/Dataset/components/ResourceItem/index.less View File

@@ -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;
}
} }
} }

+ 10
- 6
react-ui/src/pages/Dataset/components/ResourceItem/index.tsx View File

@@ -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>


+ 2
- 0
react-ui/src/pages/Dataset/config.tsx View File

@@ -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; // 是否点赞
} }


// 数据集数据 // 数据集数据


+ 11
- 11
react-ui/src/pages/DevelopmentEnvironment/List/index.tsx View File

@@ -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"


+ 6
- 17
react-ui/src/pages/Experiment/Info/index.jsx View File

@@ -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']}>
状态: 状态:


+ 4
- 19
react-ui/src/pages/Experiment/components/ExperimentDrawer/index.tsx View File

@@ -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


react-ui/src/pages/Experiment/components/ExperimentInstance/index.less → react-ui/src/pages/Experiment/components/ExperimentInstanceList/index.less View File


react-ui/src/pages/Experiment/components/ExperimentInstance/index.tsx → react-ui/src/pages/Experiment/components/ExperimentInstanceList/index.tsx View File

@@ -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;

+ 70
- 0
react-ui/src/pages/Experiment/components/ExperimentInstanceList/instance.tsx View File

@@ -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;

+ 91
- 42
react-ui/src/pages/Experiment/index.jsx View File

@@ -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],


+ 5
- 4
react-ui/src/pages/HyperParameter/Instance/index.tsx View File

@@ -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
/> />
), ),


+ 10
- 8
react-ui/src/pages/HyperParameter/components/HyperParameterBasic/index.tsx View File

@@ -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 && (


+ 3
- 2
react-ui/src/pages/Mirror/Info/index.tsx View File

@@ -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),
}, },
{ {


+ 7
- 0
react-ui/src/pages/ModelDeployment/CreateVersion/index.less View File

@@ -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;
}
} }
} }
} }

+ 13
- 21
react-ui/src/pages/ModelDeployment/CreateVersion/index.tsx View File

@@ -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


+ 8
- 8
react-ui/src/pages/ModelDeployment/ServiceInfo/index.tsx View File

@@ -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',


+ 6
- 6
react-ui/src/pages/ModelDeployment/components/VersionBasicInfo/index.tsx View File

@@ -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,


+ 1
- 0
react-ui/src/pages/Points/components/Statistics/index.less View File

@@ -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;


+ 10
- 3
react-ui/src/pages/Points/components/Statistics/index.tsx View File

@@ -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>
))} ))}


+ 5
- 2
react-ui/src/pages/Workspace/components/UserPoints/index.tsx View File

@@ -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']}


+ 15
- 0
react-ui/src/services/dataset/index.js View File

@@ -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',
});
}

+ 8
- 0
react-ui/src/services/experiment/index.js View File

@@ -151,3 +151,11 @@ export function getExpMetricsReq(data) {
data, data,
}); });
} }

// 获取服务器的当前时间
export function getSeverTimeReq(data) {
return request(`/api/mmp/experimentIns/time`, {
method: 'GET',
});
}


+ 2
- 2
react-ui/src/stories/docs/Less.mdx View File

@@ -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


+ 5
- 5
react-ui/src/stories/mockData.ts View File

@@ -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,


+ 3
- 0
react-ui/src/utils/constant.ts View File

@@ -17,3 +17,6 @@ export const VersionChangedMessage = 'versionChanged';


// 创建服务成功消息,去创建服务版本 // 创建服务成功消息,去创建服务版本
export const ServiceCreatedMessage = 'serviceCreated'; export const ServiceCreatedMessage = 'serviceCreated';

// 实验完成
export const ExperimentCompleted = 'ExperimentCompleted';

+ 2
- 1
react-ui/src/utils/date.ts View File

@@ -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 '--';
} }


+ 16
- 0
react-ui/src/utils/format.ts View File

@@ -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
}

+ 2
- 0
react-ui/src/utils/index.ts View File

@@ -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;
}; };


+ 2
- 3
ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/constant/Constant.java View File

@@ -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";


+ 2
- 2
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/activeLearn/ActiveLearnInsController.java View File

@@ -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));
} }
} }

+ 1
- 1
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/component/ComponentController.java View File

@@ -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));
} }




+ 2
- 2
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/dataset/DatasetVersionController.java View File

@@ -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));
} }




+ 23
- 20
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/experiment/ExperimentInsController.java View File

@@ -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());
}
} }



+ 1
- 1
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/icon/AssetIconController.java View File

@@ -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));
} }




+ 25
- 0
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/knowledgeGraph/KnowledgeGraphController.java View File

@@ -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
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/labelStudio/labelStudioController.java View File

@@ -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);
} }
} }

+ 1
- 1
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/model/ModelDependencyController.java View File

@@ -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));
} }




+ 1
- 1
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/model/ModelsVersionController.java View File

@@ -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));
} }




+ 1
- 1
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/resources/ComputingResourceController.java View File

@@ -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));
} }




+ 1
- 1
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/workflow/WorkflowParamController.java View File

@@ -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));
} }




+ 3
- 0
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/domain/CodeConfig.java View File

@@ -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;




+ 4
- 0
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/domain/MachineLearnIns.java View File

@@ -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;
} }

+ 1
- 1
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/scheduling/ActiveLearnInsStatusTask.java View File

@@ -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();




+ 1
- 1
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/scheduling/AutoMlInsStatusTask.java View File

@@ -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();


+ 1
- 1
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/scheduling/ExperimentInstanceStatusTask.java View File

@@ -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();


+ 1
- 1
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/scheduling/MLStatusTask.java View File

@@ -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();


+ 1
- 1
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/scheduling/RayInsStatusTask.java View File

@@ -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();




+ 1
- 1
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/AssetIconService.java View File

@@ -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);




+ 1
- 1
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/ComponentService.java View File

@@ -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;




+ 1
- 1
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/ComputingResourceService.java View File

@@ -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;


} }

+ 2
- 2
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/DatasetVersionService.java View File

@@ -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;




+ 1
- 1
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/ModelDependencyService.java View File

@@ -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;




+ 1
- 1
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/ModelsVersionService.java View File

@@ -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;




+ 1
- 1
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/WorkflowParamService.java View File

@@ -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;


/** /**
* 通过主键删除数据 * 通过主键删除数据


+ 5
- 1
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/ActiveLearnServiceImpl.java View File

@@ -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<>();


+ 3
- 3
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/AssetIconServiceImpl.java View File

@@ -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);


+ 1
- 1
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/AutoMlServiceImpl.java View File

@@ -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());


+ 1
- 1
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/CodeConfigServiceImpl.java View File

@@ -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 ? "删除成功" : "删除失败";


+ 19
- 19
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/ComponentServiceImpl.java View File

@@ -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 ? "删除成功" : "删除失败";
} }






+ 3
- 3
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/ComputingResourceServiceImpl.java View File

@@ -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);


+ 4
- 4
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/DatasetVersionServiceImpl.java View File

@@ -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);


+ 8
- 4
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/DevEnvironmentServiceImpl.java View File

@@ -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);


+ 43
- 35
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/ExperimentInsServiceImpl.java View File

@@ -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());
} }
} }




+ 3
- 0
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/ExperimentServiceImpl.java View File

@@ -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");


+ 1
- 1
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/ImageServiceImpl.java View File

@@ -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"));




+ 30
- 0
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/MachineLearnInsServiceImpl.java View File

@@ -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("未找到结果文件");
}

}
} }

+ 18
- 5
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/MachineLearnServiceImpl.java View File

@@ -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;
} }
} }


+ 2
- 2
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/ModelDependencyServiceImpl.java View File

@@ -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);


+ 1
- 1
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/ModelsVersionServiceImpl.java View File

@@ -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);


+ 6
- 1
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/RayServiceImpl.java View File

@@ -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<>();


+ 6
- 3
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/ResourceOccupyServiceImpl.java View File

@@ -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);
} }
} }

+ 3
- 3
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/WorkflowParamServiceImpl.java View File

@@ -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);


+ 4
- 3
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/WorkflowServiceImpl.java View File

@@ -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<>();


+ 1
- 1
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/utils/MinioUtil.java View File

@@ -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 {


+ 7
- 6
ruoyi-modules/management-platform/src/main/resources/mapper/managementPlatform/CodeConfigDaoMapper.xml View File

@@ -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}


+ 5
- 3
ruoyi-modules/management-platform/src/main/resources/mapper/managementPlatform/ResourceOccupy.xml View File

@@ -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>

Loading…
Cancel
Save