| @@ -18,7 +18,11 @@ spec: | |||
| image: ${k8s-7management-image} | |||
| ports: | |||
| - containerPort: 9213 | |||
| env: | |||
| - name: http_proxy | |||
| value: "http://172.20.32.253:3128" | |||
| - name: https_proxy | |||
| value: "http://172.20.32.253:3128" | |||
| --- | |||
| apiVersion: v1 | |||
| kind: Service | |||
| @@ -9,6 +9,7 @@ import { | |||
| } from '@/utils/sessionStorage'; | |||
| import classNames from 'classnames'; | |||
| import { useEffect, useState } from 'react'; | |||
| import { createPortal } from 'react-dom'; | |||
| import './index.less'; | |||
| export enum IframePageType { | |||
| @@ -61,7 +62,7 @@ function IframePage({ type, className, style }: IframePageProps) { | |||
| return ( | |||
| <div className={classNames('kf-iframe-page', className)} style={style}> | |||
| {loading && <KFSpin size="large" />} | |||
| {loading && createPortal(<KFSpin size="large" />, document.body)} | |||
| <FullScreenFrame url={iframeUrl} onload={hideLoading} onerror={hideLoading} /> | |||
| </div> | |||
| ); | |||
| @@ -14,7 +14,15 @@ const filterResourceStandard: SelectProps<string, ComputingResource>['filterOpti | |||
| }; | |||
| // id 从 number 转换为 string | |||
| const convertId = (item: any) => ({ ...item, id: `${item.id}-${item.identifier}` }); | |||
| const convertId = (item: any) => ({ | |||
| ...item, | |||
| id: JSON.stringify({ | |||
| id: item.id, | |||
| name: item.name, | |||
| identifier: item.identifier, | |||
| owner: item.owner, | |||
| }), | |||
| }); | |||
| export type SelectPropsConfig = { | |||
| getOptions: () => Promise<any>; // 获取下拉数据 | |||
| @@ -37,7 +37,7 @@ function ResourceSelect({ type, value, onChange, ...rest }: ResourceSelectProps) | |||
| onOk: (res) => { | |||
| setSelectedResource(res); | |||
| if (res) { | |||
| const { activeTab, id, name, version, path } = res; | |||
| const { activeTab, id, name, version, path, identifier, owner } = res; | |||
| if (type === ResourceSelectorType.Mirror) { | |||
| onChange?.({ | |||
| value: path, | |||
| @@ -50,8 +50,11 @@ function ResourceSelect({ type, value, onChange, ...rest }: ResourceSelectProps) | |||
| } else { | |||
| const jsonObj = { | |||
| id, | |||
| name, | |||
| version, | |||
| path, | |||
| identifier, | |||
| owner, | |||
| }; | |||
| const jsonObjStr = JSON.stringify(jsonObj); | |||
| const showValue = `${name}:${version}`; | |||
| @@ -34,12 +34,19 @@ | |||
| } | |||
| &__bottom { | |||
| position: relative; | |||
| height: calc(100% - 135px); | |||
| padding: 8px 30px 20px; | |||
| background: #ffffff; | |||
| border-radius: 10px; | |||
| box-shadow: 0px 2px 12px rgba(180, 182, 191, 0.09); | |||
| &__legend { | |||
| position: absolute; | |||
| top: 20px; | |||
| right: 30px; | |||
| } | |||
| :global { | |||
| .ant-tabs { | |||
| height: 100%; | |||
| @@ -11,14 +11,13 @@ import { | |||
| ResourceVersionData, | |||
| resourceConfig, | |||
| } from '@/pages/Dataset/config'; | |||
| import GraphLegend from '@/pages/Model/components/GraphLegend'; | |||
| import ModelEvolution from '@/pages/Model/components/ModelEvolution'; | |||
| import { openAntdModal } from '@/utils/modal'; | |||
| import { to } from '@/utils/promise'; | |||
| import { getSessionStorageItem, resourceItemKey } from '@/utils/sessionStorage'; | |||
| import { modalConfirm } from '@/utils/ui'; | |||
| import { useParams, useSearchParams } from '@umijs/max'; | |||
| import { App, Button, Flex, Select, Tabs } from 'antd'; | |||
| import { pick } from 'lodash'; | |||
| import { useEffect, useState } from 'react'; | |||
| import AddVersionModal from '../AddVersionModal'; | |||
| import ResourceIntro from '../ResourceIntro'; | |||
| @@ -40,30 +39,32 @@ const ResourceInfo = ({ resourceType }: ResourceInfoProps) => { | |||
| const [info, setInfo] = useState<ResourceData>({} as ResourceData); | |||
| const locationParams = useParams(); | |||
| const [searchParams] = useSearchParams(); | |||
| const resourceId = Number(locationParams.id); | |||
| // 模型演化传入的 tab | |||
| const defaultTab = searchParams.get('tab') || ResourceInfoTabKeys.Introduction; | |||
| // 模型演化传入的版本 | |||
| let versionParam = searchParams.get('version'); | |||
| const name = searchParams.get('name') || ''; | |||
| const owner = searchParams.get('owner') || ''; | |||
| const identifier = searchParams.get('identifier') || ''; | |||
| const [versionList, setVersionList] = useState<ResourceVersionData[]>([]); | |||
| const [version, setVersion] = useState<string | undefined>(undefined); | |||
| const [activeTab, setActiveTab] = useState<string>(defaultTab); | |||
| const resourceId = Number(locationParams.id); | |||
| const config = resourceConfig[resourceType]; | |||
| const typeName = config.name; // 数据集/模型 | |||
| const { message } = App.useApp(); | |||
| useEffect(() => { | |||
| const info = getSessionStorageItem(resourceItemKey, true); | |||
| if (info) { | |||
| setInfo(info); | |||
| getVersionList(pick(info, ['owner', 'identifier'])); | |||
| } | |||
| }, [resourceId]); | |||
| getVersionList(); | |||
| }, [resourceId, owner, identifier]); | |||
| useEffect(() => { | |||
| if (version) { | |||
| getResourceDetail({ | |||
| ...pick(info, ['owner', 'name', 'id', 'identifier']), | |||
| id: resourceId, | |||
| owner, | |||
| name, | |||
| identifier, | |||
| version, | |||
| }); | |||
| } | |||
| @@ -85,9 +86,14 @@ const ResourceInfo = ({ resourceType }: ResourceInfoProps) => { | |||
| }; | |||
| // 获取版本列表 | |||
| const getVersionList = async (params: { owner: string; identifier: string }) => { | |||
| const getVersionList = async () => { | |||
| const request = config.getVersions; | |||
| const [res] = await to(request(params)); | |||
| const [res] = await to( | |||
| request({ | |||
| owner, | |||
| identifier, | |||
| }), | |||
| ); | |||
| if (res && res.data && res.data.length > 0) { | |||
| setVersionList(res.data); | |||
| if ( | |||
| @@ -112,7 +118,7 @@ const ResourceInfo = ({ resourceType }: ResourceInfoProps) => { | |||
| resoureName: info.name, | |||
| identifier: info.identifier, | |||
| onOk: () => { | |||
| getVersionList(pick(info, ['owner', 'identifier'])); | |||
| getVersionList(); | |||
| close(); | |||
| }, | |||
| }); | |||
| @@ -127,13 +133,16 @@ const ResourceInfo = ({ resourceType }: ResourceInfoProps) => { | |||
| const deleteVersion = async () => { | |||
| const request = config.deleteVersion; | |||
| const params = { | |||
| ...pick(info, ['id', 'owner', 'identifier', 'relative_paths']), | |||
| id: resourceId, | |||
| owner, | |||
| identifier, | |||
| relative_paths: info.relative_paths, | |||
| version, | |||
| }; | |||
| const [res] = await to(request(params)); | |||
| if (res) { | |||
| message.success('删除成功'); | |||
| getVersionList(pick(info, ['owner', 'identifier'])); | |||
| getVersionList(); | |||
| } | |||
| }; | |||
| @@ -174,7 +183,7 @@ const ResourceInfo = ({ resourceType }: ResourceInfoProps) => { | |||
| <ModelEvolution | |||
| resourceId={resourceId} | |||
| version={version} | |||
| identifier={info.identifier} | |||
| identifier={identifier} | |||
| isActive={activeTab === ResourceInfoTabKeys.Evolution} | |||
| onVersionChange={handleVersionChange} | |||
| ></ModelEvolution> | |||
| @@ -228,6 +237,9 @@ const ResourceInfo = ({ resourceType }: ResourceInfoProps) => { | |||
| </div> | |||
| <div className={styles['resource-info__bottom']}> | |||
| <Tabs activeKey={activeTab} items={items} onChange={(key) => setActiveTab(key)}></Tabs> | |||
| <div className={styles['resource-info__bottom__legend']}> | |||
| {activeTab === ResourceInfoTabKeys.Evolution && <GraphLegend />} | |||
| </div> | |||
| </div> | |||
| </div> | |||
| ); | |||
| @@ -4,7 +4,6 @@ import { CommonTabKeys } from '@/enums'; | |||
| import AddModelModal from '@/pages/Dataset/components/AddModelModal'; | |||
| import { openAntdModal } from '@/utils/modal'; | |||
| import { to } from '@/utils/promise'; | |||
| import { resourceItemKey, setSessionStorageItem } from '@/utils/sessionStorage'; | |||
| import { modalConfirm } from '@/utils/ui'; | |||
| import { useNavigate } from '@umijs/max'; | |||
| import { App, Button, Input, Pagination, PaginationProps } from 'antd'; | |||
| @@ -138,8 +137,9 @@ function ResourceList( | |||
| activeTag: dataTag, | |||
| }); | |||
| const prefix = config.prefix; | |||
| setSessionStorageItem(resourceItemKey, record, true); | |||
| navigate(`/dataset/${prefix}/info/${record.id}`); | |||
| navigate( | |||
| `/dataset/${prefix}/info/${record.id}?name=${record.name}&owner=${record.owner}&identifier=${record.identifier}`, | |||
| ); | |||
| }; | |||
| // 分页切换 | |||
| @@ -1,16 +1,11 @@ | |||
| .model-evolution { | |||
| width: 100%; | |||
| height: 100%; | |||
| overflow-x: hidden; | |||
| background-color: white; | |||
| &__top { | |||
| padding: 30px 0; | |||
| color: @text-color; | |||
| font-size: @font-size-content; | |||
| } | |||
| &__graph { | |||
| height: calc(100% - 92px); | |||
| height: calc(100%); | |||
| background-color: @background-color; | |||
| background-image: url(@/assets/img/pipeline-canvas-bg.png); | |||
| background-size: 100% 100%; | |||
| @@ -9,9 +9,8 @@ import { getModelAtlasReq } from '@/services/dataset/index.js'; | |||
| import themes from '@/styles/theme.less'; | |||
| import { to } from '@/utils/promise'; | |||
| import G6, { G6GraphEvent, Graph, INode } from '@antv/g6'; | |||
| import { Flex } from 'antd'; | |||
| import { useEffect, useRef, useState } from 'react'; | |||
| import GraphLegend from '../GraphLegend'; | |||
| import NodeTooltips from '../NodeTooltips'; | |||
| import styles from './index.less'; | |||
| import type { ModelDepsData, ProjectDependency, TrainDataset } from './utils'; | |||
| @@ -145,14 +144,15 @@ function ModelEvolution({ | |||
| // 更加缩放,调整 tooltip 位置 | |||
| const offsetX = (nodeWidth * zoom) / 4; | |||
| const offsetY = (nodeHeight * zoom) / 4; | |||
| point.x += offsetX; | |||
| const canvasWidth = graphRef.current!.clientWidth; | |||
| if (point.x + 300 > canvasWidth) { | |||
| point.x = canvasWidth - 300; | |||
| if (point.x + 300 > canvasWidth + 30) { | |||
| point.x = canvasWidth + 30 - 300; | |||
| } | |||
| setHoverNodeData(model); | |||
| setNodeToolTipX(point.x + offsetX); | |||
| setNodeToolTipX(point.x); | |||
| setNodeToolTipY(graphRef.current!.clientHeight - point.y + offsetY); | |||
| setShowNodeTooltip(true); | |||
| }); | |||
| @@ -248,9 +248,6 @@ function ModelEvolution({ | |||
| return ( | |||
| <div className={styles['model-evolution']}> | |||
| <Flex align="center" className={styles['model-evolution__top']}> | |||
| <GraphLegend style={{ marginRight: 0, marginLeft: 'auto' }}></GraphLegend> | |||
| </Flex> | |||
| <div className={styles['model-evolution__graph']} id="canvas" ref={graphRef}></div> | |||
| {(showNodeTooltip || enterTooltip) && ( | |||
| <NodeTooltips | |||
| @@ -38,9 +38,11 @@ export type TrainTask = { | |||
| }; | |||
| export interface TrainDataset extends NodeConfig { | |||
| dataset_id: number; | |||
| dataset_name: string; | |||
| dataset_version: string; | |||
| repo_id: number; | |||
| name: string; | |||
| version: string; | |||
| identifier: string; | |||
| owner: string; | |||
| model_type: NodeType.TestDataset | NodeType.TrainDataset; | |||
| } | |||
| @@ -51,34 +53,33 @@ export interface ProjectDependency extends NodeConfig { | |||
| model_type: NodeType.Project; | |||
| } | |||
| export type ModalDetail = { | |||
| export type ModelMeta = { | |||
| train_datasets?: TrainDataset[]; | |||
| test_datasets?: TrainDataset[]; | |||
| project_depency?: ProjectDependency; | |||
| train_task?: TrainTask; | |||
| name: string; | |||
| available_range: number; | |||
| file_name: string; | |||
| file_size: string; | |||
| description: string; | |||
| model_type_name: string; | |||
| model_tag_name: string; | |||
| version: string; | |||
| model_source: string; | |||
| model_type: string; | |||
| create_time: string; | |||
| file_size: string; | |||
| is_public: boolean; | |||
| }; | |||
| export interface ModelDepsAPIData { | |||
| current_model_id: number; | |||
| repo_id: number; | |||
| model_name: string; | |||
| version: string; | |||
| workflow_id: number; | |||
| exp_ins_id: number; | |||
| model_type: NodeType.Children | NodeType.Current | NodeType.Parent; | |||
| current_model_name: string; | |||
| project_dependency?: ProjectDependency; | |||
| test_dataset: TrainDataset[]; | |||
| train_dataset: TrainDataset[]; | |||
| train_task: TrainTask; | |||
| model_version_dependcy_vo: ModalDetail; | |||
| children_models: ModelDepsAPIData[]; | |||
| parent_models: ModelDepsAPIData[]; | |||
| model_meta: ModelMeta; | |||
| child_model_list: ModelDepsAPIData[]; | |||
| parent_model_vo?: ModelDepsAPIData; | |||
| } | |||
| export interface ModelDepsData extends Omit<ModelDepsAPIData, 'children_models'>, TreeGraphData { | |||
| export interface ModelDepsData extends Omit<ModelDepsAPIData, 'child_model_list'>, TreeGraphData { | |||
| children: ModelDepsData[]; | |||
| expanded: boolean; // 是否展开 | |||
| level: number; // 层级,从 0 开始 | |||
| @@ -92,8 +93,11 @@ export function normalizeChildren(data: ModelDepsData[]) { | |||
| item.model_type = NodeType.Children; | |||
| item.expanded = false; | |||
| item.level = 0; | |||
| item.datasetLen = item.train_dataset.length + item.test_dataset.length; | |||
| item.id = `$M_${item.current_model_id}_${item.version}`; | |||
| item.datasetLen = getDatasetLen( | |||
| item.model_meta.train_datasets, | |||
| item.model_meta.test_datasets, | |||
| ); | |||
| item.id = `$M_${item.repo_id}_${item.version}`; | |||
| item.label = getLabel(item); | |||
| item.style = getStyle(NodeType.Children); | |||
| normalizeChildren(item.children); | |||
| @@ -104,16 +108,17 @@ export function normalizeChildren(data: ModelDepsData[]) { | |||
| // 获取 label | |||
| export function getLabel(node: ModelDepsData | ModelDepsAPIData) { | |||
| return ( | |||
| fittingString( | |||
| `${node.model_version_dependcy_vo.name ?? ''}`, | |||
| nodeWidth - labelPadding, | |||
| nodeFontSize, | |||
| ) + | |||
| fittingString(`${node.model_name ?? ''}`, nodeWidth - labelPadding, nodeFontSize) + | |||
| '\n' + | |||
| fittingString(`${node.version}`, nodeWidth - labelPadding, nodeFontSize) | |||
| ); | |||
| } | |||
| // 获取数据集数量 | |||
| export function getDatasetLen(train?: TrainDataset[], test?: TrainDataset[]) { | |||
| return (train?.length || 0) + (test?.length || 0); | |||
| } | |||
| // 获取 style | |||
| export function getStyle(model_type: NodeType) { | |||
| let fill = ''; | |||
| @@ -148,41 +153,43 @@ export function getStyle(model_type: NodeType) { | |||
| export function normalizeTreeData(apiData: ModelDepsAPIData): ModelDepsData { | |||
| // 将 children_models 转换成 children | |||
| let normalizedData = changePropertyName(apiData, { | |||
| children_models: 'children', | |||
| child_model_list: 'children', | |||
| }) as ModelDepsData; | |||
| // 设置当前模型的数据 | |||
| normalizedData.model_type = NodeType.Current; | |||
| normalizedData.id = `$M_${normalizedData.current_model_id}_${normalizedData.version}`; | |||
| normalizedData.id = `$M_${normalizedData.repo_id}_${normalizedData.version}`; | |||
| normalizedData.label = getLabel(normalizedData); | |||
| normalizedData.style = getStyle(NodeType.Current); | |||
| normalizedData.expanded = true; | |||
| normalizedData.datasetLen = | |||
| normalizedData.train_dataset.length + normalizedData.test_dataset.length; | |||
| normalizedData.datasetLen = getDatasetLen( | |||
| normalizedData.model_meta.train_datasets, | |||
| normalizedData.model_meta.test_datasets, | |||
| ); | |||
| normalizeChildren(normalizedData.children as ModelDepsData[]); | |||
| normalizedData.level = 0; | |||
| // 将 parent_models 转换成树形结构 | |||
| let parent_models = normalizedData.parent_models || []; | |||
| while (parent_models.length > 0) { | |||
| const parent = parent_models[0]; | |||
| let parent_model = normalizedData.parent_model_vo; | |||
| while (parent_model) { | |||
| const parent = parent_model; | |||
| normalizedData = { | |||
| ...parent, | |||
| expanded: false, | |||
| level: 0, | |||
| datasetLen: parent.train_dataset.length + parent.test_dataset.length, | |||
| datasetLen: getDatasetLen(parent.model_meta.train_datasets, parent.model_meta.test_datasets), | |||
| model_type: NodeType.Parent, | |||
| id: `$M_${parent.current_model_id}_${parent.version}`, | |||
| id: `$M_${parent.repo_id}_${parent.version}`, | |||
| label: getLabel(parent), | |||
| style: getStyle(NodeType.Parent), | |||
| children: [ | |||
| { | |||
| ...normalizedData, | |||
| parent_models: [], | |||
| parent_model: null, | |||
| }, | |||
| ], | |||
| }; | |||
| parent_models = normalizedData.parent_models || []; | |||
| parent_model = normalizedData.parent_model_vo; | |||
| } | |||
| return normalizedData; | |||
| } | |||
| @@ -195,11 +202,12 @@ export function getGraphData(data: ModelDepsData, hierarchyNodes: ModelDepsData[ | |||
| getWidth: () => nodeWidth, | |||
| getVGap: (node: NodeConfig) => { | |||
| const model = node as ModelDepsData; | |||
| const { model_type, expanded, project_dependency } = model; | |||
| const { model_type, expanded, model_meta } = model; | |||
| const { project_depency } = model_meta; | |||
| if (model_type === NodeType.Current || model_type === NodeType.Parent) { | |||
| return vGap / 2; | |||
| } | |||
| const selfGap = expanded && project_dependency?.url ? nodeHeight + vGap : 0; | |||
| const selfGap = expanded && project_depency?.url ? nodeHeight + vGap : 0; | |||
| const nextNode = getSameHierarchyNextNode(model, hierarchyNodes); | |||
| if (!nextNode) { | |||
| return vGap / 2; | |||
| @@ -254,28 +262,35 @@ const addDatasetDependency = ( | |||
| nodes: NodeConfig[], | |||
| edges: EdgeConfig[], | |||
| ) => { | |||
| const { train_dataset, test_dataset, id } = data; | |||
| train_dataset.forEach((item) => { | |||
| item.id = `$DTrain_${id}_${item.dataset_id}_${item.dataset_version}`; | |||
| const { repo_id, model_meta } = data; | |||
| const { train_datasets, test_datasets } = model_meta; | |||
| train_datasets?.forEach((item) => { | |||
| if (!item.repo_id) { | |||
| item.repo_id = item.id; | |||
| } | |||
| item.id = `$DTrain_${repo_id}_${item.repo_id}_${item.version}`; | |||
| item.model_type = NodeType.TrainDataset; | |||
| item.style = getStyle(NodeType.TrainDataset); | |||
| }); | |||
| test_dataset.forEach((item) => { | |||
| item.id = `$DTest_${id}_${item.dataset_id}_${item.dataset_version}`; | |||
| test_datasets?.forEach((item) => { | |||
| if (!item.repo_id) { | |||
| item.repo_id = item.id; | |||
| } | |||
| item.id = `$DTest_${repo_id}_${item.repo_id}_${item.version}`; | |||
| item.model_type = NodeType.TestDataset; | |||
| item.style = getStyle(NodeType.TestDataset); | |||
| }); | |||
| datasetNodes.length = 0; | |||
| const len = train_dataset.length + test_dataset.length; | |||
| [...train_dataset, ...test_dataset].forEach((item, index) => { | |||
| const len = getDatasetLen(train_datasets, test_datasets); | |||
| [...(train_datasets ?? []), ...(test_datasets ?? [])].forEach((item, index) => { | |||
| const node = { ...item }; | |||
| node.type = 'ellipse'; | |||
| node.size = [ellipseWidth, nodeHeight]; | |||
| node.label = | |||
| fittingString(node.dataset_name, ellipseWidth - labelPadding, nodeFontSize) + | |||
| fittingString(node.name, ellipseWidth - labelPadding, nodeFontSize) + | |||
| '\n' + | |||
| fittingString(node.dataset_version, ellipseWidth - labelPadding, nodeFontSize); | |||
| fittingString(node.version, ellipseWidth - labelPadding, nodeFontSize); | |||
| const half = len / 2 - 0.5; | |||
| node.x = currentNode.x! - (half - index) * (ellipseWidth + datasetHGap); | |||
| @@ -299,10 +314,11 @@ const addProjectDependency = ( | |||
| nodes: NodeConfig[], | |||
| edges: EdgeConfig[], | |||
| ) => { | |||
| const { project_dependency, id } = data; | |||
| if (project_dependency?.url) { | |||
| const node = { ...project_dependency }; | |||
| node.id = `$P_${id}_${node.url}_${node.branch}`; | |||
| const { repo_id, model_meta } = data; | |||
| const { project_depency } = model_meta; | |||
| if (project_depency?.url) { | |||
| const node = { ...project_depency }; | |||
| node.id = `$P_${repo_id}_${node.url}_${node.branch}`; | |||
| node.model_type = NodeType.Project; | |||
| node.type = 'rect'; | |||
| node.label = fittingString(node.name, nodeWidth - labelPadding, nodeFontSize); | |||
| @@ -322,6 +338,7 @@ const addProjectDependency = ( | |||
| } | |||
| }; | |||
| /* | |||
| // 判断两个矩形是否相交 | |||
| function isRectanglesOverlap(rect1: Rect, rect2: Rect) { | |||
| const a2x = rect1.x + rect1.width / 2; | |||
| @@ -366,6 +383,7 @@ function adjustDatasetPosition(node: NodeConfig) { | |||
| }); | |||
| } | |||
| } | |||
| */ | |||
| // 层级遍历树结构 | |||
| export function traverseHierarchically(data: ModelDepsData | undefined): ModelDepsData[] { | |||
| @@ -2,6 +2,7 @@ | |||
| position: absolute; | |||
| bottom: -100px; | |||
| left: -300px; | |||
| z-index: 10; | |||
| width: 300px; | |||
| padding: 10px; | |||
| background: white; | |||
| @@ -50,6 +51,7 @@ | |||
| flex: 1; | |||
| min-width: 0; | |||
| font-weight: 500; | |||
| word-break: break-all; | |||
| &:hover { | |||
| text-decoration: underline @underline-color; | |||
| @@ -14,9 +14,9 @@ function ModelInfo({ resourceId, data, onVersionChange }: ModelInfoProps) { | |||
| const navigate = useNavigate(); | |||
| const gotoExperimentPage = () => { | |||
| if (data.train_task?.ins_id) { | |||
| if (data.model_meta.train_task?.ins_id) { | |||
| const { origin } = location; | |||
| const url = `${origin}/pipeline/experiment/instance/${data.workflow_id}/${data.train_task.ins_id}`; | |||
| const url = `${origin}/pipeline/experiment/instance/${data.model_meta.train_task.task_id}/${data.model_meta.train_task.ins_id}`; | |||
| window.open(url, '_blank'); | |||
| } | |||
| }; | |||
| @@ -25,10 +25,10 @@ function ModelInfo({ resourceId, data, onVersionChange }: ModelInfoProps) { | |||
| if (data.model_type === NodeType.Current) { | |||
| return; | |||
| } | |||
| if (data.current_model_id === resourceId) { | |||
| if (data.repo_id === resourceId) { | |||
| onVersionChange?.(data.version); | |||
| } else { | |||
| const path = `/dataset/model/info/${data.current_model_id}?tab=${ResourceInfoTabKeys.Evolution}&version=${data.version}`; | |||
| const path = `/dataset/model/info/${data.repo_id}?tab=${ResourceInfoTabKeys.Evolution}&version=${data.version}&name=${data.model_name}&owner=${data.owner}&identifier=${data.identifier}`; | |||
| navigate(path); | |||
| } | |||
| }; | |||
| @@ -40,12 +40,10 @@ function ModelInfo({ resourceId, data, onVersionChange }: ModelInfoProps) { | |||
| <div className={styles['node-tooltips__row']}> | |||
| <span className={styles['node-tooltips__row__title']}>模型名称:</span> | |||
| {data.model_type === NodeType.Current ? ( | |||
| <span className={styles['node-tooltips__row__value']}> | |||
| {data.model_version_dependcy_vo?.name || '--'} | |||
| </span> | |||
| <span className={styles['node-tooltips__row__value']}>{data.model_name || '--'}</span> | |||
| ) : ( | |||
| <ValueLink | |||
| value={data.model_version_dependcy_vo?.name} | |||
| value={data.model_name} | |||
| className={styles['node-tooltips__row__link']} | |||
| nullClassName={styles['node-tooltips__row__value']} | |||
| onClick={gotoModelPage} | |||
| @@ -59,25 +57,25 @@ function ModelInfo({ resourceId, data, onVersionChange }: ModelInfoProps) { | |||
| <div className={styles['node-tooltips__row']}> | |||
| <span className={styles['node-tooltips__row__title']}>模型框架:</span> | |||
| <span className={styles['node-tooltips__row__value']}> | |||
| {data.model_version_dependcy_vo?.model_type_name || '--'} | |||
| {data.model_meta.model_type || '--'} | |||
| </span> | |||
| </div> | |||
| <div className={styles['node-tooltips__row']}> | |||
| <span className={styles['node-tooltips__row__title']}>模型大小:</span> | |||
| <span className={styles['node-tooltips__row__value']}> | |||
| {data.model_version_dependcy_vo?.file_size || '--'} | |||
| {data.model_meta.file_size || '--'} | |||
| </span> | |||
| </div> | |||
| <div className={styles['node-tooltips__row']}> | |||
| <span className={styles['node-tooltips__row__title']}>创建时间:</span> | |||
| <span className={styles['node-tooltips__row__value']}> | |||
| {formatDate(data.model_version_dependcy_vo?.create_time)} | |||
| {formatDate(data.model_meta.create_time || '--')} | |||
| </span> | |||
| </div> | |||
| <div className={styles['node-tooltips__row']}> | |||
| <span className={styles['node-tooltips__row__title']}>模型权限:</span> | |||
| <span className={styles['node-tooltips__row__value']}> | |||
| {data.model_version_dependcy_vo?.available_range === 1 ? '公开' : '私有'} | |||
| {data.model_meta.is_public ? '公开' : '私有'} | |||
| </span> | |||
| </div> | |||
| </div> | |||
| @@ -86,7 +84,7 @@ function ModelInfo({ resourceId, data, onVersionChange }: ModelInfoProps) { | |||
| <div className={styles['node-tooltips__row']}> | |||
| <span className={styles['node-tooltips__row__title']}>训练任务:</span> | |||
| <ValueLink | |||
| value={data.train_task?.name} | |||
| value={data.model_meta.train_task?.name} | |||
| className={styles['node-tooltips__row__link']} | |||
| nullClassName={styles['node-tooltips__row__value']} | |||
| onClick={gotoExperimentPage} | |||
| @@ -100,7 +98,7 @@ function ModelInfo({ resourceId, data, onVersionChange }: ModelInfoProps) { | |||
| function DatasetInfo({ data }: { data: TrainDataset }) { | |||
| const gotoDatasetPage = () => { | |||
| const { origin } = location; | |||
| const url = `${origin}/dataset/dataset/info/${data.dataset_id}?tab=${ResourceInfoTabKeys.Version}&version=${data.dataset_version}`; | |||
| const url = `${origin}/dataset/dataset/info/${data.repo_id}?tab=${ResourceInfoTabKeys.Version}&version=${data.version}&name=${data.name}&owner=${data.owner}&identifier=${data.identifier}`; | |||
| window.open(url, '_blank'); | |||
| }; | |||
| @@ -111,7 +109,7 @@ function DatasetInfo({ data }: { data: TrainDataset }) { | |||
| <div className={styles['node-tooltips__row']}> | |||
| <span className={styles['node-tooltips__row__title']}>数据集名称:</span> | |||
| <ValueLink | |||
| value={data.dataset_name} | |||
| value={data.name} | |||
| className={styles['node-tooltips__row__link']} | |||
| nullClassName={styles['node-tooltips__row__value']} | |||
| onClick={gotoDatasetPage} | |||
| @@ -119,9 +117,7 @@ function DatasetInfo({ data }: { data: TrainDataset }) { | |||
| </div> | |||
| <div className={styles['node-tooltips__row']}> | |||
| <span className={styles['node-tooltips__row__title']}>数据集版本:</span> | |||
| <span className={styles['node-tooltips__row__value']}> | |||
| {data.dataset_version || '--'} | |||
| </span> | |||
| <span className={styles['node-tooltips__row__value']}>{data.version || '--'}</span> | |||
| </div> | |||
| </div> | |||
| </> | |||
| @@ -204,11 +204,14 @@ const PipelineNodeParameter = forwardRef(({ onFormChange }: PipelineNodeParamete | |||
| }); | |||
| } | |||
| } else { | |||
| const { activeTab, id, name, version, path } = res; | |||
| const { activeTab, id, name, version, path, identifier, owner } = res; | |||
| const value = JSON.stringify({ | |||
| id, | |||
| name, | |||
| version, | |||
| path, | |||
| identifier, | |||
| owner, | |||
| }); | |||
| const showValue = `${name}:${version}`; | |||
| form.setFieldValue(formItemName, { | |||
| @@ -74,121 +74,6 @@ const convertMirrorVersionToTreeData = ( | |||
| })); | |||
| }; | |||
| // 从树形数据节点 id 中获取数据集版本列表的参数 | |||
| // const parseDatasetVersionId = (id: string) => { | |||
| // const list = id.split('-'); | |||
| // return { | |||
| // id: Number(list[0]), | |||
| // name: list[1], | |||
| // owner: list[2], | |||
| // identifier: list[3], | |||
| // version: list[4], | |||
| // }; | |||
| // }; | |||
| // 从树形数据节点 id 中获取数据集版本列表的参数 | |||
| // const parseMirrorVersionId = (id: string) => { | |||
| // const list = id.split('-'); | |||
| // return { | |||
| // parentId: Number(list[0]), | |||
| // id: list[1], | |||
| // url: list[2], | |||
| // }; | |||
| // }; | |||
| // export type MirrorVersion = { | |||
| // id: number; // 镜像版本 id | |||
| // status: MirrorVersionStatus; // 镜像版本状态 | |||
| // tag_name: string; // 镜像版本 name | |||
| // url: string; // 镜像版本路径 | |||
| // }; | |||
| // export type SelectorTypeInfo = { | |||
| // getList: (params: any) => Promise<any>; // 获取资源列表 | |||
| // getVersions: (params: any) => Promise<any>; // 获取资源版本列表 | |||
| // getFiles: (params: any) => Promise<any>; // 获取资源文件列表 | |||
| // handleVersionResponse: (res: any) => any[]; // 处理版本列表接口数据 | |||
| // dataToTreeData: (data: any) => TreeDataNode[]; // 数据转树形结构 | |||
| // parseTreeNodeId: (id: string) => any; // 获取版本列表请求参数 | |||
| // modalIcon: string; // modal icon | |||
| // buttonIcon: string; // button icon | |||
| // name: string; // 名称 | |||
| // litReqParamKey: 'available_range' | 'image_type'; // 表示是公开还是私有的参数名称,获取资源列表接口使用 | |||
| // tabItems: TabsProps['items']; // tab 列表 | |||
| // buttontTitle: string; // 按钮 title | |||
| // }; | |||
| // export const selectorTypeConfig: Record<ResourceSelectorType, SelectorTypeInfo> = { | |||
| // [ResourceSelectorType.Model]: { | |||
| // getList: getModelList, | |||
| // getVersions: getModelVersionList, | |||
| // getFiles: getModelVersionIdList, | |||
| // name: '模型', | |||
| // modalIcon: modelImg, | |||
| // buttonIcon: 'icon-xuanzemoxing', | |||
| // litReqParamKey: 'available_range', | |||
| // tabItems: [ | |||
| // { | |||
| // key: CommonTabKeys.Private, | |||
| // label: '我的模型', | |||
| // }, | |||
| // { | |||
| // key: CommonTabKeys.Public, | |||
| // label: '公开模型', | |||
| // }, | |||
| // ], | |||
| // buttontTitle: '选择模型', | |||
| // }, | |||
| // [ResourceSelectorType.Dataset]: { | |||
| // getList: getDatasetList, | |||
| // getVersions: getDatasetVersionList, | |||
| // getFiles: getDatasetInfo, | |||
| // name: '数据集', | |||
| // modalIcon: datasetImg, | |||
| // buttonIcon: 'icon-xuanzeshujuji', | |||
| // litReqParamKey: 'available_range', | |||
| // tabItems: [ | |||
| // { | |||
| // key: CommonTabKeys.Private, | |||
| // label: '我的数据集', | |||
| // }, | |||
| // { | |||
| // key: CommonTabKeys.Public, | |||
| // label: '公开数据集', | |||
| // }, | |||
| // ], | |||
| // buttontTitle: '选择数据集', | |||
| // }, | |||
| // [ResourceSelectorType.Mirror]: { | |||
| // getList: getMirrorListReq, | |||
| // getVersions: (id: number) => getMirrorVersionListReq({ image_id: id, page: 0, size: 200 }), | |||
| // getFiles: getMirrorFilesReq, | |||
| // handleVersionResponse: (res) => | |||
| // res.data?.content?.filter( | |||
| // (v: MirrorVersionData) => v.status === MirrorVersionStatus.Available, | |||
| // ) || [], | |||
| // dataToTreeData: convertMirrorToTreeData, | |||
| // parseTreeNodeId: (id: string) => id, | |||
| // name: '镜像', | |||
| // modalIcon: mirrorImg, | |||
| // buttonIcon: 'icon-xuanzejingxiang', | |||
| // litReqParamKey: 'image_type', | |||
| // tabItems: [ | |||
| // { | |||
| // key: CommonTabKeys.Private, | |||
| // label: '我的镜像', | |||
| // }, | |||
| // { | |||
| // key: CommonTabKeys.Public, | |||
| // label: '公开镜像', | |||
| // }, | |||
| // ], | |||
| // buttontTitle: '选择镜像', | |||
| // }, | |||
| // }; | |||
| interface SelectorTypeInfo { | |||
| getList: (isPublic: boolean) => Promise<any>; // 获取资源列表 | |||
| getVersions: (parentKey: string, parentNode: any) => Promise<any>; // 获取资源版本列表 | |||
| @@ -22,6 +22,8 @@ export type ResourceSelectorResponse = { | |||
| name: string; // 数据集\模型\镜像 name | |||
| version: string; // 数据集\模型\镜像版本 | |||
| path: string; // 数据集\模型\镜像版本路径 | |||
| identifier: string; // 数据集\模型 identifier | |||
| owner: string; // 数据集\模型 owner | |||
| activeTab: CommonTabKeys; // 是我的还是公开的 | |||
| }; | |||
| @@ -221,12 +223,17 @@ function ResourceSelectorModal({ | |||
| if (checkedKeys.length > 0) { | |||
| const last = checkedKeys[0] as string; | |||
| const { id, version } = getIdAndVersion(last); | |||
| const name = (treeData.find((v) => v.key === id)?.title ?? '') as string; | |||
| const treeNode = treeData.find((v) => v.key === id) as any; | |||
| const name = (treeNode?.title ?? '') as string; | |||
| const identifier = (treeNode?.identifier ?? '') as string; | |||
| const owner = (treeNode?.owner ?? '') as string; | |||
| const res = { | |||
| id, | |||
| name, | |||
| path: versionPath, | |||
| version, | |||
| identifier, | |||
| owner, | |||
| activeTab: activeTab as CommonTabKeys, | |||
| }; | |||
| onOk?.(res); | |||
| @@ -12,17 +12,17 @@ public interface GitService { | |||
| String checkoutToken(); | |||
| //输入token,项目名,tag,创建新项目,返回项目地址 | |||
| Map createProject(GitProjectVo gitProjectVo) throws Exception; | |||
| Map createProject(String token,GitProjectVo gitProjectVo) throws Exception; | |||
| void createBranch(String owner, String projectName, String branchName, String oldBranchName) throws Exception; | |||
| void createBranch(String token,String owner, String projectName, String branchName, String oldBranchName) throws Exception; | |||
| void createTopic(Integer id, String topicName) throws Exception; | |||
| void createTopic(String token,Integer id, String topicName) throws Exception; | |||
| List<Map<String, Object>> getBrancheList(String owner, String projectName) throws Exception; | |||
| List<Map<String, Object>> getBrancheList(String token,String owner, String projectName) throws Exception; | |||
| void deleteProject(String owner, String projectName) throws Exception; | |||
| void deleteProject(String token,String owner, String projectName) throws Exception; | |||
| void deleteBranch(String owner, String projectName, String branchName, String localPath) throws Exception; | |||
| void deleteBranch(String token,String owner, String projectName, String branchName, String localPath) throws Exception; | |||
| Map getUserInfo(String token) throws Exception; | |||
| } | |||
| @@ -165,7 +165,9 @@ public class DatasetVersionServiceImpl implements DatasetVersionService { | |||
| .findFirst() | |||
| .ifPresent(datasetVersion -> { | |||
| String url = datasetVersion.getUrl(); | |||
| response.put("path", url); | |||
| // response.put("path", url); | |||
| String path = bucketName + '/' + url.substring(0, url.lastIndexOf('/')); | |||
| response.put("path", path); | |||
| }); | |||
| response.put("content", datasetVersionList); | |||
| @@ -28,7 +28,8 @@ public class GitServiceImpl implements GitService { | |||
| @Value("${spring.redis.host}") | |||
| private String redisHost; | |||
| @Value("${spring.redis.port}") | |||
| private Integer redisPort; | |||
| private static final Logger log = LoggerFactory.getLogger(GitServiceImpl.class); | |||
| @Override | |||
| @@ -61,7 +62,7 @@ public class GitServiceImpl implements GitService { | |||
| LoginUser loginUser = SecurityUtils.getLoginUser(); | |||
| String ci4sUsername = loginUser.getUsername(); | |||
| // 将access_token存入Redis | |||
| Jedis jedis = new Jedis(redisHost); | |||
| Jedis jedis = new Jedis(redisHost,redisPort); | |||
| jedis.set(ci4sUsername + "_gitToken", accessToken); | |||
| jedis.set(ci4sUsername + "_gitUserInfo", userReq); | |||
| return accessToken; | |||
| @@ -72,7 +73,7 @@ public class GitServiceImpl implements GitService { | |||
| } | |||
| public String checkoutToken() { | |||
| Jedis jedis = new Jedis(redisHost); | |||
| Jedis jedis = new Jedis(redisHost,redisPort); | |||
| LoginUser loginUser = SecurityUtils.getLoginUser(); | |||
| String ci4sUsername = loginUser.getUsername(); | |||
| String token = jedis.get(ci4sUsername + "_gitToken"); | |||
| @@ -98,20 +99,18 @@ public class GitServiceImpl implements GitService { | |||
| } | |||
| @Override | |||
| public Map createProject(GitProjectVo gitProjectVo) throws Exception { | |||
| String token = this.checkoutToken(); | |||
| public Map createProject(String token,GitProjectVo gitProjectVo) throws Exception { | |||
| String userReq = HttpUtils.sendPostWithToken("https://www.gitlink.org.cn/api/projects.json", JsonUtils.objectToJson(gitProjectVo), token); | |||
| return JsonUtils.jsonToMap(userReq); | |||
| } | |||
| @Override | |||
| public void createBranch(String owner, String projectName, String branchName, String oldBranchName) throws Exception { | |||
| public void createBranch(String token,String owner, String projectName, String branchName, String oldBranchName) throws Exception { | |||
| //https://www.gitlink.org.cn/api/v1/fanshuai/testdssa8755/branches.json | |||
| // { | |||
| // "new_branch_name": "SsS", | |||
| // "old_branch_name": "master" | |||
| // } | |||
| String token = this.checkoutToken(); | |||
| String createBranchUrl = "https://www.gitlink.org.cn/api/v1/" + owner + "/" + projectName + "/branches.json"; | |||
| Map<String, Object> resMap = new HashMap<>(); | |||
| resMap.put("new_branch_name", branchName); | |||
| @@ -121,9 +120,8 @@ public class GitServiceImpl implements GitService { | |||
| } | |||
| @Override | |||
| public void createTopic(Integer id, String topicName) throws Exception { | |||
| public void createTopic(String token,Integer id, String topicName) throws Exception { | |||
| // https://www.gitlink.org.cn/api/v1/project_topics.json | |||
| String token = this.checkoutToken(); | |||
| Map<String, Object> resMap = new HashMap<>(); | |||
| resMap.put("project_id", id); | |||
| resMap.put("name", topicName); | |||
| @@ -131,8 +129,7 @@ public class GitServiceImpl implements GitService { | |||
| } | |||
| @Override | |||
| public List<Map<String, Object>> getBrancheList(String owner, String projectName) throws Exception { | |||
| String token = checkoutToken(); | |||
| public List<Map<String, Object>> getBrancheList(String token,String owner, String projectName) throws Exception { | |||
| String req = HttpUtils.sendGetWithToken("https://www.gitlink.org.cn/api/v1/" + owner + "/" + projectName + "/branches/all.json", null, token); | |||
| // 解析响应JSON | |||
| if (StringUtils.isEmpty(req)) { | |||
| @@ -144,20 +141,18 @@ public class GitServiceImpl implements GitService { | |||
| } | |||
| @Override | |||
| public void deleteProject(String owner, String projectName) throws Exception { | |||
| String token = this.checkoutToken(); | |||
| public void deleteProject(String token,String owner, String projectName) throws Exception { | |||
| HttpUtils.sendDeleteRequest("https://www.gitlink.org.cn/api/" + owner + "/" + projectName + ".json", token); | |||
| } | |||
| @Override | |||
| public void deleteBranch(String owner, String projectName, String branchName, String localPath) throws Exception { | |||
| public void deleteBranch(String token,String owner, String projectName, String branchName, String localPath) throws Exception { | |||
| try (Git git = Git.open(new File(localPath))) { | |||
| git.checkout().setName("master").call(); | |||
| git.branchDelete().setBranchNames(branchName).setForce(true).call(); | |||
| } catch (IOException | GitAPIException e) { | |||
| log.error("Exception occurred while creating local branch based on master",e); | |||
| } | |||
| String token = this.checkoutToken(); | |||
| HttpUtils.sendDeleteRequest("https://www.gitlink.org.cn/api/v1/" + owner + "/" + projectName + "/branches/" + branchName + ".json", token); | |||
| } | |||
| @@ -61,7 +61,9 @@ public class ImageServiceImpl implements ImageService { | |||
| @Resource | |||
| private MinioService minioService; | |||
| @Value("${minio.dataReleaseBucketName}") | |||
| // @Value("${minio.dataReleaseBucketName}") | |||
| // private String bucketName; | |||
| @Value("${harbor.bucketName}") | |||
| private String bucketName; | |||
| @Value("${harbor.repository}") | |||
| private String repository; | |||
| @@ -90,23 +90,25 @@ public class JupyterServiceImpl implements JupyterService { | |||
| // 提取数据集,模型信息,得到数据集模型的path | |||
| Map<String, Object> dataset = JacksonUtil.parseJSONStr2Map(devEnvironment.getDataset()); | |||
| String datasetPath = (String) dataset.get("path"); | |||
| String datasetPath = "argo-workflow" + "/" + dataset.get("path"); | |||
| // String datasetPath = (String) dataset.get("path"); | |||
| Map<String, Object> model = JacksonUtil.parseJSONStr2Map(devEnvironment.getModel()); | |||
| String modelPath = (String) model.get("path"); | |||
| String modelPath = "argo-workflow" + "/" + model.get("path"); | |||
| // String modelPath = (String) model.get("path"); | |||
| LoginUser loginUser = SecurityUtils.getLoginUser(); | |||
| //构造pod名称 | |||
| String podName = loginUser.getUsername().toLowerCase() + "-editor-pod" + "-" + id; | |||
| //新建编辑器的pvc | |||
| // String pvcName = loginUser.getUsername().toLowerCase() + "-editor-pvc"; | |||
| // V1PersistentVolumeClaim pvc = k8sClientUtil.createPvc(namespace, pvcName, storage, storageClassName); | |||
| String pvcName = loginUser.getUsername().toLowerCase() + "-editor-pvc"; | |||
| V1PersistentVolumeClaim pvc = k8sClientUtil.createPvc(namespace, pvcName, storage, storageClassName); | |||
| //TODO 设置镜像可配置,这里先用默认镜像启动pod | |||
| // 调用修改后的 createPod 方法,传入额外的参数 | |||
| // Integer podPort = k8sClientUtil.createConfiguredPod(podName, namespace, port, mountPath, pvc, devEnvironment, minioPvcName, datasetPath, modelPath); | |||
| Integer podPort = k8sClientUtil.createConfiguredPod(podName, namespace, port, mountPath, null, devEnvironment, minioPvcName, datasetPath, modelPath); | |||
| Integer podPort = k8sClientUtil.createConfiguredPod(podName, namespace, port, mountPath, pvc, devEnvironment, minioPvcName, datasetPath, modelPath); | |||
| // Integer podPort = k8sClientUtil.createConfiguredPod(podName, namespace, port, mountPath, null, devEnvironment, minioPvcName, datasetPath, modelPath); | |||
| String url = masterIp + ":" + podPort; | |||
| redisService.setCacheObject(podName, masterIp + ":" + podPort); | |||
| devEnvironment.setStatus("Pending"); | |||
| @@ -86,6 +86,8 @@ public class ModelsServiceImpl implements ModelsService { | |||
| @Value("${spring.redis.host}") | |||
| private String redisHost; | |||
| @Value("${spring.redis.port}") | |||
| private Integer redisPort; | |||
| @Value("${git.endpoint}") | |||
| String gitendpoint; | |||
| @Value("${git.localPath}") | |||
| @@ -540,6 +542,7 @@ public class ModelsServiceImpl implements ModelsService { | |||
| @Override | |||
| public String newCreateModel(ModelsVo modelsVo) { | |||
| try { | |||
| String token = gitService.checkoutToken(); | |||
| LoginUser loginUser = SecurityUtils.getLoginUser(); | |||
| String ci4sUsername = loginUser.getUsername(); | |||
| String gitLinkUsername = loginUser.getSysUser().getGitLinkUsername(); | |||
| @@ -571,17 +574,17 @@ public class ModelsServiceImpl implements ModelsService { | |||
| gitProjectVo.setUserId(userId); | |||
| // 创建项目 | |||
| Map project = gitService.createProject(gitProjectVo); | |||
| Map project = gitService.createProject(token,gitProjectVo); | |||
| Integer gitlinIid = (Integer) project.get("id"); | |||
| if (gitlinIid == null) { | |||
| throw new Exception("创建模型失败:" + project.get("message")); | |||
| } | |||
| // 创建分支 | |||
| gitService.createBranch((String) userInfo.get("login"), repositoryName, modelsVo.getVersion(), "master"); | |||
| gitService.createBranch(token,(String) userInfo.get("login"), repositoryName, modelsVo.getVersion(), "master"); | |||
| // 定义标签 标签1:ci4s_model 标签2:ModelTag 标签3:ModelType | |||
| gitService.createTopic(gitlinIid, "ci4s_model"); | |||
| gitService.createTopic(gitlinIid, "modeltag_" + modelsVo.getModelTag()); | |||
| gitService.createTopic(gitlinIid, "modeltype_" + modelsVo.getModelType()); | |||
| gitService.createTopic(token,gitlinIid, "ci4s_model"); | |||
| gitService.createTopic(token,gitlinIid, "modeltag_" + modelsVo.getModelTag()); | |||
| gitService.createTopic(token,gitlinIid, "modeltype_" + modelsVo.getModelType()); | |||
| String branchName = modelsVo.getVersion(); | |||
| String owner = (String) userInfo.get("login"); | |||
| @@ -882,7 +885,8 @@ public class ModelsServiceImpl implements ModelsService { | |||
| @Override | |||
| public List<Map<String, Object>> getVersionList(String identifier, String owner) throws Exception { | |||
| List<Map<String, Object>> brancheList = gitService.getBrancheList(owner, identifier); | |||
| String token = gitService.checkoutToken(); | |||
| List<Map<String, Object>> brancheList = gitService.getBrancheList(token,owner, identifier); | |||
| return brancheList.stream() | |||
| .filter(branch -> !"master".equals(branch.get("name"))) | |||
| .collect(Collectors.toList()); | |||
| @@ -949,7 +953,8 @@ public class ModelsServiceImpl implements ModelsService { | |||
| @Override | |||
| public void deleteModel(Integer repoId, String identifier, String owner) throws Exception { | |||
| gitService.deleteProject(owner, identifier); | |||
| String token = gitService.checkoutToken(); | |||
| gitService.deleteProject(token,owner, identifier); | |||
| //删除模型依赖 | |||
| modelDependency1Dao.deleteModel(repoId, identifier, owner, null); | |||
| @@ -962,7 +967,8 @@ public class ModelsServiceImpl implements ModelsService { | |||
| @Override | |||
| public void deleteVersion(Integer repoId, String identifier, String owner, String version, String relativePath) throws Exception { | |||
| gitService.deleteBranch(owner, identifier, version, localPath + relativePath); | |||
| String token = gitService.checkoutToken(); | |||
| gitService.deleteBranch(token,owner, identifier, version, localPath + relativePath); | |||
| //删除模型依赖 | |||
| modelDependency1Dao.deleteModel(repoId, identifier, owner, version); | |||
| HashMap<String, Object> map = new HashMap<>(); | |||
| @@ -1053,7 +1059,7 @@ public class ModelsServiceImpl implements ModelsService { | |||
| } | |||
| Map<String, Object> getUserInfo(String ci4sUsername, String gitLinkUsername, String gitLinkPassword) throws IOException { | |||
| Jedis jedis = new Jedis(redisHost); | |||
| Jedis jedis = new Jedis(redisHost,redisPort); | |||
| String userReq = jedis.get(ci4sUsername + "_gitUserInfo"); | |||
| if (userReq == null) { | |||
| gitService.login(gitLinkUsername, gitLinkPassword); | |||
| @@ -50,6 +50,8 @@ public class NewDatasetServiceImpl implements NewDatasetService { | |||
| private DvcService dvcService; | |||
| @Value("${spring.redis.host}") | |||
| private String redisHost; | |||
| @Value("${spring.redis.port}") | |||
| private Integer redisPort; | |||
| @Value("${minio.accessKey}") | |||
| String accessKeyId; | |||
| @Value("${minio.secretKey}") | |||
| @@ -64,7 +66,8 @@ public class NewDatasetServiceImpl implements NewDatasetService { | |||
| private DatasetTempStorageService datasetTempStorageService; | |||
| @Override | |||
| public String newCreateDataset(NewDatasetVo datasetVo) throws Exception { | |||
| Jedis jedis = new Jedis(redisHost); | |||
| String token = gitService.checkoutToken(); | |||
| Jedis jedis = new Jedis(redisHost,redisPort); | |||
| LoginUser loginUser = SecurityUtils.getLoginUser(); | |||
| String ci4sUsername = loginUser.getUsername(); | |||
| String gitLinkUsername = loginUser.getSysUser().getGitLinkUsername(); | |||
| @@ -82,18 +85,18 @@ public class NewDatasetServiceImpl implements NewDatasetService { | |||
| gitProjectVo.setPrivate(!datasetVo.getIsPublic()); | |||
| gitProjectVo.setUserId(userId); | |||
| // 创建项目 | |||
| Map project = gitService.createProject(gitProjectVo); | |||
| Map project = gitService.createProject(token,gitProjectVo); | |||
| Integer gitlinIid = (Integer) project.get("id"); | |||
| if (gitlinIid == null){ | |||
| throw new Exception("创建数据集失败:"+project.get("message")); | |||
| } | |||
| // 创建分支 | |||
| String branchName = datasetVo.getVersion(); | |||
| gitService.createBranch((String) userInfo.get("login"), repositoryName, branchName, "master"); | |||
| gitService.createBranch(token,(String) userInfo.get("login"), repositoryName, branchName, "master"); | |||
| // 定义标签 标签1:ci4s_dataset 标签2:DataTag 标签3:DataType | |||
| gitService.createTopic(gitlinIid, "ci4s_dataset"); | |||
| gitService.createTopic(gitlinIid, "DataTag_" + datasetVo.getDataTag()); | |||
| gitService.createTopic( gitlinIid, "DataType_" + datasetVo.getDataType()); | |||
| gitService.createTopic(token,gitlinIid, "ci4s_dataset"); | |||
| gitService.createTopic(token,gitlinIid, "DataTag_" + datasetVo.getDataTag()); | |||
| gitService.createTopic(token, gitlinIid, "DataType_" + datasetVo.getDataType()); | |||
| // 得到项目地址 | |||
| String projectUrl = gitendpoint + "/" + (String) userInfo.get("login") + "/"+ repositoryName + ".git"; | |||
| @@ -145,10 +148,10 @@ public class NewDatasetServiceImpl implements NewDatasetService { | |||
| } | |||
| public String newCreateVersion(NewDatasetVo datasetVo) throws Exception { | |||
| Jedis jedis = new Jedis(redisHost); | |||
| String token = gitService.checkoutToken(); | |||
| Jedis jedis = new Jedis(redisHost,redisPort); | |||
| LoginUser loginUser = SecurityUtils.getLoginUser(); | |||
| String ci4sUsername = loginUser.getUsername(); | |||
| String token = jedis.get(ci4sUsername + "_gitToken"); | |||
| String gitLinkUsername = loginUser.getSysUser().getGitLinkUsername(); | |||
| String gitLinkPassword = loginUser.getSysUser().getGitLinkPassword(); | |||
| String userReq = jedis.get(ci4sUsername + "_gitUserInfo"); | |||
| @@ -215,10 +218,10 @@ public class NewDatasetServiceImpl implements NewDatasetService { | |||
| @Override | |||
| public Page<NewDatasetVo> newPersonalQueryByPage(Dataset dataset, PageRequest pageRequest) throws Exception { | |||
| Jedis jedis = new Jedis(redisHost); | |||
| String token = gitService.checkoutToken(); | |||
| Jedis jedis = new Jedis(redisHost,redisPort); | |||
| LoginUser loginUser = SecurityUtils.getLoginUser(); | |||
| String ci4sUsername = loginUser.getUsername(); | |||
| String token = jedis.get(ci4sUsername + "_gitToken"); | |||
| String userReq = jedis.get(ci4sUsername + "_gitUserInfo"); | |||
| Map<String, Object> userInfo = JsonUtils.jsonToMap(userReq); | |||
| // 拼接查询url | |||
| @@ -242,10 +245,10 @@ public class NewDatasetServiceImpl implements NewDatasetService { | |||
| @Override | |||
| public Page<NewDatasetVo> newPubilcQueryByPage(Dataset dataset, PageRequest pageRequest) throws Exception { | |||
| Jedis jedis = new Jedis(redisHost); | |||
| String token = gitService.checkoutToken(); | |||
| Jedis jedis = new Jedis(redisHost,redisPort); | |||
| LoginUser loginUser = SecurityUtils.getLoginUser(); | |||
| String ci4sUsername = loginUser.getUsername(); | |||
| String token = jedis.get(ci4sUsername + "_gitToken"); | |||
| String userReq = jedis.get(ci4sUsername + "_gitUserInfo"); | |||
| Map<String, Object> userInfo = JsonUtils.jsonToMap(userReq); | |||
| Integer userId = (Integer) userInfo.get("user_id"); | |||
| @@ -305,7 +308,8 @@ public class NewDatasetServiceImpl implements NewDatasetService { | |||
| @Override | |||
| public List<Map<String, Object>> getVersionList(String repo, String owner) throws Exception { | |||
| List<Map<String, Object>> brancheList = gitService.getBrancheList(owner, repo); | |||
| String token = gitService.checkoutToken(); | |||
| List<Map<String, Object>> brancheList = gitService.getBrancheList(token,owner, repo); | |||
| return brancheList.stream() | |||
| .filter(branch -> !"master".equals(branch.get("name"))) | |||
| .collect(Collectors.toList()); | |||
| @@ -313,20 +317,14 @@ public class NewDatasetServiceImpl implements NewDatasetService { | |||
| @Override | |||
| public void deleteDatasetNew(String repo, String owner) throws Exception { | |||
| Jedis jedis = new Jedis(redisHost); | |||
| LoginUser loginUser = SecurityUtils.getLoginUser(); | |||
| String ci4sUsername = loginUser.getUsername(); | |||
| String token = jedis.get(ci4sUsername+"_gitToken"); | |||
| gitService.deleteProject(owner, repo); | |||
| String token = gitService.checkoutToken(); | |||
| gitService.deleteProject(token,owner, repo); | |||
| } | |||
| @Override | |||
| public void deleteDatasetVersionNew(String repo, String owner, String version, String relativePath) throws Exception { | |||
| Jedis jedis = new Jedis(redisHost); | |||
| LoginUser loginUser = SecurityUtils.getLoginUser(); | |||
| String ci4sUsername = loginUser.getUsername(); | |||
| String token = jedis.get(ci4sUsername+"_gitToken"); | |||
| gitService.deleteBranch(owner, repo, version, localPathlocal + relativePath); | |||
| String token = gitService.checkoutToken(); | |||
| gitService.deleteBranch(token,owner, repo, version, localPathlocal + relativePath); | |||
| } | |||
| @Override | |||
| @@ -341,6 +339,7 @@ public class NewDatasetServiceImpl implements NewDatasetService { | |||
| long sizeInBytes = file.getSize(); | |||
| String formattedSize = FileUtil.formatFileSize(sizeInBytes); | |||
| File targetFile = new File(path, file.getOriginalFilename()); | |||
| // 确保目录存在 | |||
| targetFile.getParentFile().mkdirs(); | |||
| // 保存文件到目标路径 | |||
| @@ -381,24 +380,11 @@ public class NewDatasetServiceImpl implements NewDatasetService { | |||
| public ResponseEntity<InputStreamResource> downloadAllDatasetFilesNew(String name,Integer id, String version) throws Exception { | |||
| // 命令行操作 git clone 项目地址 | |||
| LoginUser loginUser = SecurityUtils.getLoginUser(); | |||
| String gitLinkUsername = loginUser.getSysUser().getGitLinkUsername(); | |||
| String gitLinkPassword = loginUser.getSysUser().getGitLinkPassword(); | |||
| String ci4sUsername = loginUser.getUsername(); | |||
| Jedis jedis = new Jedis(redisHost); | |||
| Jedis jedis = new Jedis(redisHost,redisPort); | |||
| String userReq = jedis.get(ci4sUsername + "_gitUserInfo"); | |||
| Map<String, Object> userInfo = JsonUtils.jsonToMap(userReq); | |||
| String localPath = localPathlocal+ loginUser.getUsername()+"/datasets/"+id+"/"+name; | |||
| //不需要,没意义,我查看详情已经是最新的了 | |||
| // if(folder.exists() && folder.isDirectory()){ | |||
| // //切换分支 | |||
| // DVCUtils.gitCheckoutBranch(localPath, version); | |||
| // //pull | |||
| // DVCUtils.gitPull(localPath,gitLinkUsername, gitLinkPassword); | |||
| // //dvc pull | |||
| // DVCUtils.dvcPull(localPath); | |||
| // }else { | |||
| // DVCUtils.gitClone(localPath, projectUrl, version, gitLinkUsername, gitLinkPassword); | |||
| // } | |||
| // 打包 data 文件夹 | |||
| String dataFolderPath = localPath + "/data"; | |||
| @@ -59,13 +59,17 @@ public class TensorBoardServiceImpl implements TensorBoardService { | |||
| @Override | |||
| public String runTensorBoard(FrameLogPathVo frameLogPathVo) throws Exception { | |||
| if (StringUtils.isEmpty(frameLogPathVo.getPath())) { | |||
| throw new Exception("存储路径为空"); | |||
| if (StringUtils.isEmpty(frameLogPathVo.getPath())||StringUtils.isEmpty(frameLogPathVo.getPvcName())){ | |||
| throw new Exception("存储路径或存储为空"); | |||
| } | |||
| // if (StringUtils.isEmpty(frameLogPathVo.getPath())) { | |||
| // throw new Exception("存储路径为空"); | |||
| // } | |||
| LoginUser loginUser = SecurityUtils.getLoginUser(); | |||
| String podName = loginUser.getUsername().toLowerCase()+"-"+frameLogPathVo.getPath().split("/")[2]+ "-tensorboard-pod"; | |||
| Integer podPort = k8sClientUtil.createPodWithSubPath(podName, StringUtils.isEmpty(frameLogPathVo.getNamespace()) ? "default" : frameLogPathVo.getNamespace(), port, mountPath, frameLogPathVo.getPath(), image); | |||
| Integer podPort = k8sClientUtil.createPodWithSubPath(podName, StringUtils.isEmpty(frameLogPathVo.getNamespace()) ? "default" : frameLogPathVo.getNamespace(), port, mountPath, frameLogPathVo.getPath(), frameLogPathVo.getPvcName(), image); | |||
| // Integer podPort = k8sClientUtil.createPodWithSubPath(podName, StringUtils.isEmpty(frameLogPathVo.getNamespace()) ? "default" : frameLogPathVo.getNamespace(), port, mountPath, frameLogPathVo.getPath(), image); | |||
| redisService.setCacheObject(podName, masterIp + ":" + podPort); | |||
| return masterIp + ":" + podPort; | |||
| } | |||
| @@ -34,8 +34,8 @@ import java.util.*; | |||
| @Component | |||
| public class K8sClientUtil { | |||
| @Value("${jupyter.hostPath}") | |||
| private String hostPath; | |||
| // @Value("${jupyter.hostPath}") | |||
| // private String hostPath; | |||
| private String http; | |||
| private String token; | |||
| @@ -314,8 +314,8 @@ public class K8sClientUtil { | |||
| * @param image 镜像 | |||
| * @return 创建成功的pod,的nodePort端口 | |||
| */ | |||
| public Integer createPodWithSubPath(String podName, String namespace, Integer port, String mountPath, String subPath, String image) { | |||
| public Integer createPodWithSubPath(String podName, String namespace, Integer port, String mountPath, String subPath, String pvcName, String image) { | |||
| // public Integer createPodWithSubPath(String podName, String namespace, Integer port, String mountPath, String subPath, String image) { | |||
| Map<String, String> selector = new LinkedHashMap<>(); | |||
| selector.put("k8s-jupyter", podName); | |||
| @@ -366,8 +366,8 @@ public class K8sClientUtil { | |||
| .endContainer() | |||
| .addNewVolume() | |||
| .withName("workspace") | |||
| .withHostPath(new V1HostPathVolumeSource().path(hostPath).type("DirectoryOrCreate")) | |||
| // .withPersistentVolumeClaim(new V1PersistentVolumeClaimVolumeSource().claimName(pvcName)) | |||
| // .withHostPath(new V1HostPathVolumeSource().path(hostPath).type("DirectoryOrCreate")) | |||
| .withPersistentVolumeClaim(new V1PersistentVolumeClaimVolumeSource().claimName(pvcName)) | |||
| .endVolume() | |||
| .withTerminationGracePeriodSeconds(14400L) | |||
| .endSpec() | |||
| @@ -449,25 +449,25 @@ public class K8sClientUtil { | |||
| } | |||
| } | |||
| // 配置卷和卷挂载 | |||
| List<V1VolumeMount> volumeMounts = new ArrayList<>(); | |||
| volumeMounts.add(new V1VolumeMount().name("workspace").mountPath("/opt/notebooks")); | |||
| volumeMounts.add(new V1VolumeMount().name("data").mountPath("/opt/dataset").subPath(datasetPath).readOnly(true)); | |||
| volumeMounts.add(new V1VolumeMount().name("data").mountPath("/opt/model").subPath(modelPath).readOnly(true)); | |||
| List<V1Volume> volumes = new ArrayList<>(); | |||
| volumes.add(new V1Volume().name("workspace").hostPath(new V1HostPathVolumeSource().path(hostPath + "/" + podName + "/notebooks").type("DirectoryOrCreate"))); | |||
| volumes.add(new V1Volume().name("data").hostPath(new V1HostPathVolumeSource().path(hostPath).type("DirectoryOrCreate"))); | |||
| // 配置卷和卷挂载 | |||
| // List<V1VolumeMount> volumeMounts = new ArrayList<>(); | |||
| // volumeMounts.add(new V1VolumeMount().name("workspace").mountPath(mountPath)); | |||
| // volumeMounts.add(new V1VolumeMount().name("minio-pvc").mountPath("/opt/data").subPath(datasetPath).readOnly(true)); | |||
| // volumeMounts.add(new V1VolumeMount().name("minio-pvc").mountPath("/opt/model").subPath(modelPath).readOnly(true)); | |||
| // volumeMounts.add(new V1VolumeMount().name("workspace").mountPath("/opt/notebooks")); | |||
| // volumeMounts.add(new V1VolumeMount().name("data").mountPath("/opt/dataset").subPath(datasetPath).readOnly(true)); | |||
| // volumeMounts.add(new V1VolumeMount().name("data").mountPath("/opt/model").subPath(modelPath).readOnly(true)); | |||
| // | |||
| // List<V1Volume> volumes = new ArrayList<>(); | |||
| // volumes.add(new V1Volume().name("workspace").persistentVolumeClaim(new V1PersistentVolumeClaimVolumeSource().claimName(pvc.getMetadata().getName()))); | |||
| // volumes.add(new V1Volume().name("minio-pvc").persistentVolumeClaim(new V1PersistentVolumeClaimVolumeSource().claimName(dataPvcName))); | |||
| // volumes.add(new V1Volume().name("workspace").hostPath(new V1HostPathVolumeSource().path(hostPath + "/" + podName + "/notebooks").type("DirectoryOrCreate"))); | |||
| // volumes.add(new V1Volume().name("data").hostPath(new V1HostPathVolumeSource().path(hostPath).type("DirectoryOrCreate"))); | |||
| // 配置卷和卷挂载 | |||
| List<V1VolumeMount> volumeMounts = new ArrayList<>(); | |||
| volumeMounts.add(new V1VolumeMount().name("workspace").mountPath(mountPath)); | |||
| volumeMounts.add(new V1VolumeMount().name("minio-pvc").mountPath("/opt/data").subPath(datasetPath).readOnly(true)); | |||
| volumeMounts.add(new V1VolumeMount().name("minio-pvc").mountPath("/opt/model").subPath(modelPath).readOnly(true)); | |||
| List<V1Volume> volumes = new ArrayList<>(); | |||
| volumes.add(new V1Volume().name("workspace").persistentVolumeClaim(new V1PersistentVolumeClaimVolumeSource().claimName(pvc.getMetadata().getName()))); | |||
| volumes.add(new V1Volume().name("minio-pvc").persistentVolumeClaim(new V1PersistentVolumeClaimVolumeSource().claimName(dataPvcName))); | |||
| //配置资源 | |||
| @@ -10,6 +10,7 @@ public class FrameLogPathVo implements Serializable { | |||
| String path; | |||
| String namespace; | |||
| String pvcName; | |||
| public String getPath() { | |||
| return path; | |||
| } | |||
| @@ -25,4 +26,12 @@ public class FrameLogPathVo implements Serializable { | |||
| public void setNamespace(String namespace) { | |||
| this.namespace = namespace; | |||
| } | |||
| public String getPvcName() { | |||
| return pvcName; | |||
| } | |||
| public void setPvcName(String pvcName) { | |||
| this.pvcName = pvcName; | |||
| } | |||
| } | |||