| @@ -108,7 +108,9 @@ export default defineConfig({ | |||
| * @description 内置了 babel import 插件 | |||
| * @doc https://umijs.org/docs/max/antd#antd | |||
| */ | |||
| antd: {}, | |||
| antd: { | |||
| configProvider: {}, | |||
| }, | |||
| /** | |||
| * @name 网络请求配置 | |||
| * @description 它基于 axios 和 ahooks 的 useRequest 提供了一套统一的网络请求和错误处理方案。 | |||
| @@ -5,7 +5,7 @@ | |||
| border-radius: 21px; | |||
| } | |||
| .ant-modal-header { | |||
| margin: 20px 0; | |||
| margin: 20px 0 30px; | |||
| background-color: transparent; | |||
| } | |||
| .ant-modal-footer { | |||
| @@ -3,6 +3,7 @@ | |||
| display: flex; | |||
| align-items: center; | |||
| color: @kf-primary-color; | |||
| font-weight: 400; | |||
| font-size: 20px; | |||
| &_image { | |||
| @@ -37,3 +37,27 @@ export function useAntdModal(initialValue: boolean) { | |||
| return [visible, open, close]; | |||
| } | |||
| type Callback<T> = (state: T) => void; | |||
| /** | |||
| * Generates a stateful value and a function to update it that triggers callbacks. | |||
| * | |||
| * @param initialValue - The initial value of the state. | |||
| * @return A tuple containing the current state value and a function to update the state. | |||
| */ | |||
| export function useCallbackState<T>(initialValue: T) { | |||
| const [state, _setState] = useState(initialValue); | |||
| const callbackQueue = useRef<Callback<T>[]>([]); | |||
| useEffect(() => { | |||
| callbackQueue.current.forEach((cb) => cb(state)); | |||
| callbackQueue.current = []; | |||
| }, [state]); | |||
| const setState = (newValue: T, callback: Callback<T>) => { | |||
| _setState(newValue); | |||
| if (callback && typeof callback === 'function') { | |||
| callbackQueue.current.push(callback); | |||
| } | |||
| }; | |||
| return [state, setState]; | |||
| } | |||
| @@ -39,9 +39,9 @@ const Dataset = () => { | |||
| return { | |||
| ...form.getFieldsValue(), | |||
| dataset_id: locationParams.id, | |||
| file_name: item.response.data[0].fileName, | |||
| file_size: item.response.data[0].fileSize, | |||
| url: item.response.data[0].url, | |||
| file_name: item.response.code === 200 ? item.response.data[0].fileName : null, | |||
| file_size: item.response.code === 200 ? item.response.data[0].fileSize : null, | |||
| url: item.response.code === 200 ? item.response.data[0].url : null, | |||
| }; | |||
| }), | |||
| ); | |||
| @@ -1,6 +1,6 @@ | |||
| import { getDatasetList } from '@/services/dataset/index.js'; | |||
| import { Form, Input, Tabs } from 'antd'; | |||
| import React, { useEffect, useState } from 'react'; | |||
| import { useEffect, useState } from 'react'; | |||
| import { useNavigate } from 'react-router-dom'; | |||
| import Styles from './index.less'; | |||
| import PersonalData from './personalData'; | |||
| @@ -9,7 +9,7 @@ const { Search } = Input; | |||
| const { TabPane } = Tabs; | |||
| const leftdataList = [1, 2, 3]; | |||
| const Dataset = (React.FC = () => { | |||
| const Dataset = () => { | |||
| const [queryFlow, setQueryFlow] = useState({ | |||
| page: 0, | |||
| size: 10, | |||
| @@ -52,7 +52,7 @@ const Dataset = (React.FC = () => { | |||
| console.log('Failed:', errorInfo); | |||
| }; | |||
| useEffect(() => { | |||
| getDatasetlist(); | |||
| //getDatasetlist(); | |||
| return () => {}; | |||
| }, []); | |||
| return ( | |||
| @@ -70,5 +70,5 @@ const Dataset = (React.FC = () => { | |||
| </div> | |||
| </div> | |||
| ); | |||
| }); | |||
| }; | |||
| export default Dataset; | |||
| @@ -24,7 +24,16 @@ const PublicData = (React.FC = () => { | |||
| onChange({ file, fileList }) { | |||
| if (file.status !== 'uploading') { | |||
| console.log(file, fileList); | |||
| form.setFieldsValue({ dataset_version_vos: fileList.map((item) => item.response.data[0]) }); | |||
| form.setFieldsValue({ | |||
| dataset_version_vos: fileList.map((item) => { | |||
| const data = item.response.data[0]; | |||
| return { | |||
| file_name: data.fileName, | |||
| file_size: data.fileSize, | |||
| url: data.url, | |||
| }; | |||
| }), | |||
| }); | |||
| } | |||
| }, | |||
| defaultFileList: [], | |||
| @@ -51,6 +60,7 @@ const PublicData = (React.FC = () => { | |||
| const [total, setTotal] = useState(0); | |||
| const [form] = Form.useForm(); | |||
| const [dialogTitle, setDialogTitle] = useState('新建数据'); | |||
| const [uuid, setUuid] = useState(Date.now()); | |||
| const getDatasetlist = (queryFlow) => { | |||
| getDatasetList(queryFlow).then((ret) => { | |||
| console.log(ret); | |||
| @@ -64,6 +74,7 @@ const PublicData = (React.FC = () => { | |||
| const showModal = () => { | |||
| form.resetFields(); | |||
| setDialogTitle('新建数据集'); | |||
| setUuid(Date.now()); | |||
| setIsModalOpen(true); | |||
| }; | |||
| const getAssetIconList = (params) => { | |||
| @@ -397,7 +408,7 @@ const PublicData = (React.FC = () => { | |||
| </Radio.Group> | |||
| </Form.Item> | |||
| <Form.Item label="数据文件" name="dataset_version_vos"> | |||
| <Upload {...props}> | |||
| <Upload {...props} data={{ uuid: uuid }}> | |||
| <Button | |||
| style={{ | |||
| fontSize: '14px', | |||
| @@ -0,0 +1,26 @@ | |||
| .tensorBoard-status { | |||
| display: flex; | |||
| align-items: center; | |||
| color: rgba(29, 29, 32, 0.75); | |||
| &__label { | |||
| color: rgba(29, 29, 32, 0.75); | |||
| font-size: 15px; | |||
| &--running { | |||
| color: #6ac21d; | |||
| } | |||
| &--failed { | |||
| color: #df6d6d; | |||
| } | |||
| } | |||
| &__icon { | |||
| width: 14px; | |||
| color: #6ac21d; | |||
| cursor: pointer; | |||
| & + & { | |||
| margin-left: 6px; | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,82 @@ | |||
| import exportImg from '@/assets/img/tensor-board-export.png'; | |||
| import pendingImg from '@/assets/img/tensor-board-pending.png'; | |||
| import { LoadingOutlined } from '@ant-design/icons'; | |||
| import classNames from 'classnames'; | |||
| import styles from './index.less'; | |||
| // import stopImg from '@/assets/img/tensor-board-stop.png'; | |||
| import terminatedImg from '@/assets/img/tensor-board-terminated.png'; | |||
| export enum TensorBoardStatusEnum { | |||
| Unknown = 'Unknown', // 未知 | |||
| Pending = 'Pending', // 启动中 | |||
| Running = 'Running', // 运行中 | |||
| Terminated = 'Terminated', // 未启动或者已终止 | |||
| Failed = 'Failed', // 失败 | |||
| } | |||
| const statusConfig = { | |||
| Unknown: { | |||
| label: '未知', | |||
| icon: terminatedImg, | |||
| classname: '', | |||
| }, | |||
| Terminated: { | |||
| label: '未启动', | |||
| icon: terminatedImg, | |||
| classname: '', | |||
| }, | |||
| Failed: { | |||
| label: '失败', | |||
| icon: terminatedImg, | |||
| classname: 'tensorBoard-status__label--failed', | |||
| }, | |||
| Pending: { | |||
| label: '启动中', | |||
| icon: pendingImg, | |||
| classname: '', | |||
| }, | |||
| Running: { | |||
| label: '运行中', | |||
| icon: exportImg, | |||
| classname: 'tensorBoard-status__label--running', | |||
| }, | |||
| }; | |||
| type TensorBoardStatusProps = { | |||
| status: TensorBoardStatusEnum; | |||
| onClick: () => void; | |||
| }; | |||
| function TensorBoardStatus({ | |||
| status = TensorBoardStatusEnum.Unknown, | |||
| onClick, | |||
| }: TensorBoardStatusProps) { | |||
| return ( | |||
| <div className={styles['tensorBoard-status']}> | |||
| <div | |||
| className={classNames( | |||
| styles['tensorBoard-status__label'], | |||
| styles[statusConfig[status].classname], | |||
| )} | |||
| > | |||
| {statusConfig[status].label} | |||
| </div> | |||
| {statusConfig[status].icon ? ( | |||
| <> | |||
| <div style={{ margin: '0 6px' }}>|</div> | |||
| {status === TensorBoardStatusEnum.Pending ? ( | |||
| <LoadingOutlined className={styles['tensorBoard-status__icon']} /> | |||
| ) : ( | |||
| <img | |||
| className={styles['tensorBoard-status__icon']} | |||
| src={statusConfig[status].icon} | |||
| onClick={onClick} | |||
| /> | |||
| )} | |||
| </> | |||
| ) : null} | |||
| </div> | |||
| ); | |||
| } | |||
| export default TensorBoardStatus; | |||
| @@ -1,6 +1,6 @@ | |||
| .params_container { | |||
| max-height: 230px; | |||
| padding: 15px; | |||
| padding: 15px 15px 0; | |||
| border: 1px solid #e6e6e6; | |||
| border-radius: 8px; | |||
| @@ -4,10 +4,12 @@ import { | |||
| getExperiment, | |||
| getExperimentById, | |||
| getQueryByExperimentId, | |||
| getTensorBoardStatusReq, | |||
| postExperiment, | |||
| putExperiment, | |||
| putQueryByExperimentInsId, | |||
| runExperiments, | |||
| runTensorBoardReq, | |||
| } from '@/services/experiment/index.js'; | |||
| import { getWorkflow } from '@/services/pipeline/index.js'; | |||
| import { elapsedTime } from '@/utils/date'; | |||
| @@ -23,10 +25,14 @@ import { Button, Modal, Space, Table, message } from 'antd'; | |||
| import momnet from 'moment'; | |||
| import { useEffect, useRef, useState } from 'react'; | |||
| import { useNavigate } from 'react-router-dom'; | |||
| import TensorBoardStatus, { TensorBoardStatusEnum } from './components/TensorBoardStatus'; | |||
| import AddExperimentModal from './experimentText/addExperimentModal'; | |||
| import Styles from './index.less'; | |||
| import { experimentStatusInfo } from './status'; | |||
| // 定时器 | |||
| const timerIds = new Map(); | |||
| function Experiment() { | |||
| const navgite = useNavigate(); | |||
| const [experimentList, setExperimentList] = useState([]); | |||
| @@ -44,9 +50,13 @@ function Experiment() { | |||
| const [isAdd, setIsAdd] = useState(true); | |||
| const [isModalOpen, setIsModalOpen] = useState(false); | |||
| const [addFormData, setAddFormData] = useState({}); | |||
| useEffect(() => { | |||
| getList(); | |||
| getWorkflowList(); | |||
| return () => { | |||
| clearExperimentInTimers(); | |||
| }; | |||
| }, []); | |||
| // 获取实验列表 | |||
| const getList = async () => { | |||
| @@ -72,11 +82,32 @@ function Experiment() { | |||
| setWorkflowList(res.data.content); | |||
| } | |||
| }; | |||
| // 获取实验实例 | |||
| const getQueryByExperiment = (val) => { | |||
| getQueryByExperimentId(val).then((ret) => { | |||
| setExpandedRowKeys(val); | |||
| if (ret.code === 200 && ret.data && ret.data.length > 0) { | |||
| setExperimentInList(ret.data); | |||
| if (ret && ret.data && ret.data.length > 0) { | |||
| try { | |||
| const list = ret.data.map((v) => { | |||
| const nodes_result = v.nodes_result ? JSON.parse(v.nodes_result) : {}; | |||
| return { | |||
| ...v, | |||
| nodes_result, | |||
| }; | |||
| }); | |||
| setExperimentInList(list); | |||
| // 获取 TensorBoard 状态 | |||
| list.forEach((item) => { | |||
| if (item.nodes_result.tensorboard_log) { | |||
| const timerId = setTimeout(() => { | |||
| getTensorBoardStatus(item); | |||
| }, 0); | |||
| timerIds.set(item.id, timerId); | |||
| } | |||
| }); | |||
| } catch (error) { | |||
| setExperimentInList([]); | |||
| } | |||
| getList(); | |||
| } else { | |||
| setExperimentInList([]); | |||
| @@ -84,13 +115,66 @@ function Experiment() { | |||
| } | |||
| }); | |||
| }; | |||
| // 运行 TensorBoard | |||
| const runTensorBoard = async (experimentIn) => { | |||
| const params = { | |||
| namespace: experimentIn.nodes_result.tensorboard_log.namespace, | |||
| path: experimentIn.nodes_result.tensorboard_log.path, | |||
| pvc_name: experimentIn.nodes_result.tensorboard_log.pvc_name, | |||
| }; | |||
| const [res] = await to(runTensorBoardReq(params)); | |||
| if (res) { | |||
| experimentIn.tensorboardUrl = res.data; | |||
| const timerId = timerIds.get(experimentIn.id); | |||
| if (timerId) { | |||
| clearTimeout(timerId); | |||
| timerIds.delete(experimentIn.id); | |||
| getTensorBoardStatus(experimentIn); | |||
| } | |||
| } | |||
| }; | |||
| // 获取 TensorBoard 状态 | |||
| const getTensorBoardStatus = async (experimentIn) => { | |||
| const params = { | |||
| namespace: experimentIn.nodes_result.tensorboard_log.namespace, | |||
| path: experimentIn.nodes_result.tensorboard_log.path, | |||
| pvc_name: experimentIn.nodes_result.tensorboard_log.pvc_name, | |||
| }; | |||
| const [res] = await to(getTensorBoardStatusReq(params)); | |||
| if (res && res.data) { | |||
| setExperimentInList((prevList) => { | |||
| const newList = [...prevList]; | |||
| const index = prevList.findIndex((item) => item.id === experimentIn.id); | |||
| const preObj = prevList[index]; | |||
| const newObj = { | |||
| ...preObj, | |||
| tensorBoardStatus: res.data.status, | |||
| tensorboardUrl: res.data.url, | |||
| }; | |||
| newList.splice(index, 1, newObj); | |||
| return newList; | |||
| }); | |||
| const timerId = setTimeout(() => { | |||
| getTensorBoardStatus(experimentIn); | |||
| }, 10000); | |||
| timerIds.set(experimentIn.id, timerId); | |||
| } | |||
| }; | |||
| const expandChange = (e, record) => { | |||
| if (record.id === expandedRowKeys) { | |||
| clearExperimentInTimers(); | |||
| setExpandedRowKeys(null); | |||
| } else { | |||
| getQueryByExperiment(record.id); | |||
| } | |||
| }; | |||
| // 终止实验实例获取 TensorBoard 状态的定时器 | |||
| const clearExperimentInTimers = () => { | |||
| timerIds.values().forEach((timerId) => { | |||
| clearTimeout(timerId); | |||
| }); | |||
| timerIds.clear(); | |||
| }; | |||
| // 创建实验 | |||
| const createExperiment = () => { | |||
| setIsAdd(true); | |||
| @@ -174,6 +258,19 @@ function Experiment() { | |||
| navgite({ pathname: `/experiment/pytorchtext/${record.workflow_id}/${item.id}` }); | |||
| }; | |||
| const handleTensorboard = async (experimentIn) => { | |||
| if ( | |||
| experimentIn.tensorBoardStatus === TensorBoardStatusEnum.Terminated || | |||
| experimentIn.tensorBoardStatus === TensorBoardStatusEnum.Failed | |||
| ) { | |||
| await runTensorBoard(experimentIn); | |||
| } else if ( | |||
| experimentIn.tensorBoardStatus === TensorBoardStatusEnum.Running && | |||
| experimentIn.tensorboardUrl | |||
| ) { | |||
| window.open(experimentIn.tensorboardUrl, '_blank'); | |||
| } | |||
| }; | |||
| const columns = [ | |||
| { | |||
| title: '实验名称', | |||
| @@ -198,7 +295,6 @@ function Experiment() { | |||
| key: 'status_list', | |||
| render: (text) => { | |||
| let newText = text && text.replace(/\s+/g, '').split(','); | |||
| console.log(newText); | |||
| return ( | |||
| <> | |||
| {newText && newText.length > 0 | |||
| @@ -306,15 +402,17 @@ function Experiment() { | |||
| columns={columns} | |||
| dataSource={experimentList} | |||
| pagination={paginationProps} | |||
| rowKey="id" | |||
| expandable={{ | |||
| expandedRowRender: (record) => ( | |||
| <div> | |||
| {experimentInList && experimentInList.length > 0 ? ( | |||
| <div className={Styles.tableExpandBox} style={{ paddingBottom: '16px' }}> | |||
| <div style={{ width: '50px' }}>序号</div> | |||
| <div style={{ width: '200px' }}>状态</div> | |||
| <div style={{ width: '150px' }}>序号</div> | |||
| <div style={{ width: '300px' }}>TensorBoard</div> | |||
| <div style={{ width: '300px' }}>运行时长</div> | |||
| <div style={{ width: '300px' }}>开始时间</div> | |||
| <div style={{ width: '200px' }}>状态</div> | |||
| <div style={{ width: '200px' }}>操作</div> | |||
| </div> | |||
| ) : ( | |||
| @@ -332,9 +430,27 @@ function Experiment() { | |||
| height: '45px', | |||
| }} | |||
| > | |||
| <a style={{ width: '50px' }} onClick={(e) => routerToText(e, item, record)}> | |||
| <a style={{ width: '150px' }} onClick={(e) => routerToText(e, item, record)}> | |||
| {index + 1} | |||
| </a> | |||
| <div style={{ width: '300px' }}> | |||
| {item.nodes_result.tensorboard_log ? ( | |||
| <TensorBoardStatus | |||
| status={item.tensorBoardStatus} | |||
| onClick={() => handleTensorboard(item)} | |||
| ></TensorBoardStatus> | |||
| ) : ( | |||
| '-' | |||
| )} | |||
| </div> | |||
| <div style={{ width: '300px' }}> | |||
| {item.finish_time | |||
| ? elapsedTime(new Date(item.create_time), new Date(item.finish_time)) | |||
| : elapsedTime(new Date(item.create_time), new Date())} | |||
| </div> | |||
| <div style={{ width: '300px' }}> | |||
| {momnet(item.create_time).format('YYYY-MM-DD HH:mm:ss')} | |||
| </div> | |||
| <div className={Styles.statusBox} style={{ width: '200px' }}> | |||
| <img | |||
| style={{ width: '17px', marginRight: '7px' }} | |||
| @@ -347,14 +463,6 @@ function Experiment() { | |||
| {experimentStatusInfo[item.status]?.label} | |||
| </span> | |||
| </div> | |||
| <div style={{ width: '300px' }}> | |||
| {item.finish_time | |||
| ? elapsedTime(new Date(item.create_time), new Date(item.finish_time)) | |||
| : elapsedTime(new Date(item.create_time), new Date())} | |||
| </div> | |||
| <div style={{ width: '300px' }}> | |||
| {momnet(item.create_time).format('YYYY-MM-DD HH:mm:ss')} | |||
| </div> | |||
| <div style={{ width: '200px' }}> | |||
| <Button | |||
| type="link" | |||
| @@ -39,9 +39,9 @@ const Dataset = () => { | |||
| return { | |||
| ...form.getFieldsValue(), | |||
| models_id: locationParams.id, | |||
| file_name: item.response.data[0].fileName, | |||
| file_size: item.response.data[0].fileSize, | |||
| url: item.response.data[0].url, | |||
| file_name: item.response.code === 200 ? item.response.data[0].fileName : null, | |||
| file_size: item.response.code === 200 ? item.response.data[0].fileSize : null, | |||
| url: item.response.code === 200 ? item.response.data[0].url : null, | |||
| }; | |||
| }), | |||
| ); | |||
| @@ -59,6 +59,7 @@ const Dataset = () => { | |||
| const locationParams = useParams(); //新版本获取路由参数接口 | |||
| console.log(locationParams); | |||
| const [wordList, setWordList] = useState([]); | |||
| const [uuid, setUuid] = useState(Date.now()); | |||
| const getModelByDetail = () => { | |||
| getModelById(locationParams.id).then((ret) => { | |||
| console.log(ret); | |||
| @@ -68,7 +69,7 @@ const Dataset = () => { | |||
| const getModelVersionsList = () => { | |||
| getModelVersionsById(locationParams.id).then((ret) => { | |||
| console.log(ret); | |||
| if (ret.data && ret.data.length > 0) { | |||
| if (ret && ret.data && ret.data.length > 0) { | |||
| setVersionList( | |||
| ret.data.map((item) => { | |||
| return { | |||
| @@ -77,6 +78,8 @@ const Dataset = () => { | |||
| }; | |||
| }), | |||
| ); | |||
| setVersion(ret.data[0]); | |||
| getModelVersions({ version: ret.data[0], models_id: locationParams.id }); | |||
| } | |||
| }); | |||
| }; | |||
| @@ -90,6 +93,7 @@ const Dataset = () => { | |||
| form.setFieldsValue({ name: datasetDetailObj.name }); | |||
| setDialogTitle('创建新版本'); | |||
| setUuid(Date.now()); | |||
| setIsModalOpen(true); | |||
| }; | |||
| const handleCancel = () => { | |||
| @@ -104,9 +108,7 @@ const Dataset = () => { | |||
| onOk: () => { | |||
| deleteModelVersion({ models_id: locationParams.id, version }).then((ret) => { | |||
| setVersion(null); | |||
| getModelVersionsList(); | |||
| getModelVersions({ version, models_id: locationParams.id }); | |||
| message.success('删除成功'); | |||
| }); | |||
| }, | |||
| @@ -368,7 +370,7 @@ const Dataset = () => { | |||
| }, | |||
| ]} | |||
| > | |||
| <Upload {...props}> | |||
| <Upload {...props} data={{ uuid: uuid }}> | |||
| <Button | |||
| style={{ | |||
| fontSize: '14px', | |||
| @@ -23,14 +23,23 @@ const PublicData = () => { | |||
| onChange({ file, fileList }) { | |||
| if (file.status !== 'uploading') { | |||
| console.log(file, fileList); | |||
| form.setFieldsValue({ dataset_version_vos: fileList.map((item) => item.response.data[0]) }); | |||
| form.setFieldsValue({ | |||
| models_version_vos: fileList.map((item) => { | |||
| const data = item.response.data[0]; | |||
| return { | |||
| file_name: data.fileName, | |||
| file_size: data.fileSize, | |||
| url: data.url, | |||
| }; | |||
| }), | |||
| }); | |||
| } | |||
| }, | |||
| defaultFileList: [], | |||
| }; | |||
| const [queryFlow, setQueryFlow] = useState({ | |||
| page: 0, | |||
| size: 10, | |||
| size: 20, | |||
| name: null, | |||
| available_range: 0, | |||
| }); | |||
| @@ -49,6 +58,7 @@ const PublicData = () => { | |||
| const [total, setTotal] = useState(0); | |||
| const [form] = Form.useForm(); | |||
| const [dialogTitle, setDialogTitle] = useState('新建模型'); | |||
| const [uuid, setUuid] = useState(Date.now()); | |||
| const getModelLists = (queryFlow) => { | |||
| getModelList(queryFlow).then((ret) => { | |||
| console.log(ret); | |||
| @@ -62,6 +72,7 @@ const PublicData = () => { | |||
| const showModal = () => { | |||
| form.resetFields(); | |||
| setDialogTitle('新建模型'); | |||
| setUuid(Date.now()); | |||
| setIsModalOpen(true); | |||
| }; | |||
| const getAssetIconList = (params) => { | |||
| @@ -324,6 +335,21 @@ const PublicData = () => { | |||
| <Input placeholder="请输入模型名称" showCount maxLength={64} /> | |||
| </Form.Item> | |||
| <Form.Item | |||
| label="模型版本" | |||
| name="version" | |||
| rules={ | |||
| [ | |||
| // { | |||
| // required: true, | |||
| // message: 'Please input your username!', | |||
| // }, | |||
| ] | |||
| } | |||
| > | |||
| <Input placeholder="请输入模型版本" /> | |||
| </Form.Item> | |||
| <Form.Item | |||
| label="模型描述" | |||
| name="description" | |||
| @@ -370,9 +396,19 @@ const PublicData = () => { | |||
| > | |||
| <Select allowClear placeholder="请选择模型标签" options={[]} /> | |||
| </Form.Item> | |||
| <Form.Item label="模型文件" name="dataset_version_vos"> | |||
| <Upload {...props}> | |||
| <Button icon={<UploadOutlined style={{ color: '#1664ff' }} />}>上传文件</Button> | |||
| <Form.Item label="模型文件" name="models_version_vos"> | |||
| <Upload {...props} data={{ uuid: uuid }}> | |||
| <Button | |||
| style={{ | |||
| fontSize: '14px', | |||
| border: '1px solid', | |||
| borderColor: '#1664ff', | |||
| background: '#fff', | |||
| }} | |||
| icon={<UploadOutlined style={{ color: '#1664ff', fontSize: '14px' }} />} | |||
| > | |||
| 上传文件 | |||
| </Button> | |||
| </Upload> | |||
| </Form.Item> | |||
| </Form> | |||
| @@ -10,7 +10,7 @@ const leftdataList = [1, 2, 3]; | |||
| const PublicData = () => { | |||
| const [queryFlow, setQueryFlow] = useState({ | |||
| page: 0, | |||
| size: 10, | |||
| size: 20, | |||
| name: null, | |||
| available_range: 1, | |||
| }); | |||
| @@ -42,6 +42,7 @@ const modelMenus = ({ onParDragEnd }) => { | |||
| const { Panel } = Collapse; | |||
| return ( | |||
| <div style={{ width: '250px', height: '99%' }} className={Styles.collapse}> | |||
| <div className={Styles.modelMenusTitle}>组件库</div> | |||
| <Collapse collapsible="header" defaultActiveKey={['1']} expandIconPosition="end"> | |||
| {modelMenusList && modelMenusList.length > 0 | |||
| ? modelMenusList.map((item) => ( | |||
| @@ -12,6 +12,7 @@ | |||
| } | |||
| .collapseItem:hover { | |||
| background: rgba(22, 100, 255, 0.08); | |||
| color:#1664ff; | |||
| } | |||
| .collapse { | |||
| :global { | |||
| @@ -23,10 +24,12 @@ | |||
| margin-bottom: 5px; | |||
| background-color: #fff; | |||
| border-color: transparent; | |||
| padding: 20px 16px 15px 16px; | |||
| } | |||
| .ant-collapse > .ant-collapse-item { | |||
| margin: 0 10px; | |||
| border-color: rgba(20, 49, 179, 0.12); | |||
| border-bottom:0.5px dashed rgba(20, 49, 179, 0.12); | |||
| border-radius: 0px; | |||
| } | |||
| .ant-collapse .ant-collapse-content { | |||
| @@ -38,3 +41,10 @@ | |||
| } | |||
| } | |||
| } | |||
| .modelMenusTitle{ | |||
| padding: 12px 25px; | |||
| margin-bottom: 10px; | |||
| color:#111111; | |||
| font-size:16px; | |||
| font-family: 'Alibaba'; | |||
| } | |||
| @@ -243,7 +243,7 @@ const Pipeline = () => { | |||
| 新建流水线 | |||
| </Button> | |||
| </div> | |||
| <Table columns={columns} dataSource={pipeList} pagination={paginationProps} /> | |||
| <Table columns={columns} dataSource={pipeList} pagination={paginationProps} rowKey="id" /> | |||
| <Modal | |||
| title={ | |||
| <div style={{ display: 'flex', alignItems: 'center', fontWeight: 500 }}> | |||
| @@ -25,7 +25,7 @@ export function addDatesetAndVesion(data) { | |||
| } | |||
| // 新增模型 | |||
| export function addModel(data) { | |||
| return request(`/api/mmp//models`, { | |||
| return request(`/api/mmp/models/addModelAndVersion`, { | |||
| method: 'POST', | |||
| headers: { | |||
| 'Content-Type': 'application/json;charset=UTF-8', | |||
| @@ -100,3 +100,19 @@ export function putExperiment(data) { | |||
| data, | |||
| }); | |||
| } | |||
| // 启动tensorBoard | |||
| export function runTensorBoardReq(data) { | |||
| return request(`/api/mmp/tensorBoard/run`, { | |||
| method: 'POST', | |||
| data, | |||
| }); | |||
| } | |||
| // 启动tensorBoard | |||
| export function getTensorBoardStatusReq(data) { | |||
| return request(`/api/mmp/tensorBoard/getStatus`, { | |||
| method: 'POST', | |||
| data, | |||
| }); | |||
| } | |||
| @@ -52,13 +52,13 @@ public class DatasetController { | |||
| @ApiOperation("数据集广场公开数据集分页查询,根据data_type筛选,1公开0私有") | |||
| public AjaxResult queryByPage(Dataset dataset, @RequestParam("page") int page, | |||
| @RequestParam("size") int size, | |||
| //@RequestParam("available_range") int availableRange , | |||
| @RequestParam(value = "available_range") int availableRange , | |||
| @RequestParam(value = "data_type", required = false) String dataType) { | |||
| if (dataType != null) { // 仅当dataType有值时设置 | |||
| dataset.setDataType(dataType); | |||
| } | |||
| dataset.setAvailableRange(1); | |||
| dataset.setAvailableRange(availableRange); | |||
| PageRequest pageRequest = PageRequest.of(page, size); | |||
| return AjaxResult.success(this.datasetService.queryByPage(dataset, pageRequest)); | |||
| } | |||
| @@ -182,16 +182,15 @@ public class DatasetController { | |||
| /** | |||
| * 上传数据集 | |||
| * | |||
| // * @param datasetId 数据集ID | |||
| * @param files 上传的数据集文件 | |||
| * | |||
| * @param uuid 上传唯一标识,构建url | |||
| * @return 上传结果 | |||
| */ | |||
| @CrossOrigin(origins = "*", allowedHeaders = "*") | |||
| @PostMapping("/upload") | |||
| @ApiOperation(value = "上传数据集", notes = "根据数据集版本表id上传数据集文件,并将信息存入数据库。") | |||
| public AjaxResult uploadDataset(@RequestParam("file") MultipartFile[] files) throws Exception { | |||
| return AjaxResult.success(this.datasetService.uploadDataset(files)); | |||
| public AjaxResult uploadDataset(@RequestParam("file") MultipartFile[] files, @RequestParam("uuid") String uuid) throws Exception { | |||
| return AjaxResult.success(this.datasetService.uploadDataset(files,uuid)); | |||
| } | |||
| @@ -176,8 +176,8 @@ public class ModelsController extends BaseController { | |||
| @CrossOrigin(origins = "*", allowedHeaders = "*") | |||
| @PostMapping("/upload") | |||
| @ApiOperation(value = "上传模型", notes = "根据模型id上传模型文件,并将信息存入数据库。") | |||
| public GenericsAjaxResult<List<Map<String, String>>> uploadModels(@RequestParam("file") MultipartFile[] files) throws Exception { | |||
| return genericsSuccess(this.modelsService.uploadModels(files)); | |||
| public GenericsAjaxResult<List<Map<String, String>>> uploadModels(@RequestParam("file") MultipartFile[] files , @RequestParam("uuid") String uuid) throws Exception { | |||
| return genericsSuccess(this.modelsService.uploadModels(files,uuid)); | |||
| } | |||
| @@ -4,6 +4,7 @@ import com.ruoyi.common.core.web.controller.BaseController; | |||
| import com.ruoyi.common.core.web.domain.GenericsAjaxResult; | |||
| import com.ruoyi.platform.service.TensorBoardService; | |||
| import com.ruoyi.platform.vo.FrameLogPathVo; | |||
| import com.ruoyi.platform.vo.TensorboardStatusVo; | |||
| import io.swagger.annotations.Api; | |||
| import io.swagger.annotations.ApiOperation; | |||
| import io.swagger.v3.oas.annotations.responses.ApiResponse; | |||
| @@ -36,7 +37,7 @@ public class TensorBoardController extends BaseController { | |||
| } | |||
| @PostMapping("/getStatus") | |||
| @ApiResponse | |||
| public GenericsAjaxResult<String> getStatus(@RequestBody FrameLogPathVo frameLogPathVo) throws Exception { | |||
| public GenericsAjaxResult<TensorboardStatusVo> getStatus(@RequestBody FrameLogPathVo frameLogPathVo) throws Exception { | |||
| return genericsSuccess(tensorBoardService.getTensorBoardStatus(frameLogPathVo)); | |||
| } | |||
| } | |||
| @@ -72,7 +72,7 @@ DatasetService { | |||
| ResponseEntity<InputStreamResource> downloadDataset(Integer id) throws Exception; | |||
| List<Map<String, String>> uploadDataset(MultipartFile[] files) throws Exception; | |||
| List<Map<String, String>> uploadDataset(MultipartFile[] files, String uuid) throws Exception; | |||
| Map uploadDatasetPipeline(DatasetVersion datasetVersion) throws Exception; | |||
| @@ -70,7 +70,7 @@ public interface ModelsService { | |||
| List<Map<String, String>> uploadModels(MultipartFile[] files) throws Exception; | |||
| List<Map<String, String>> uploadModels(MultipartFile[] files, String uuid) throws Exception; | |||
| Map uploadModelsPipeline(ModelsVersion modelsVersion) throws Exception; | |||
| @@ -1,11 +1,12 @@ | |||
| package com.ruoyi.platform.service; | |||
| import com.ruoyi.platform.vo.FrameLogPathVo; | |||
| import com.ruoyi.platform.vo.TensorboardStatusVo; | |||
| public interface TensorBoardService { | |||
| String getTensorBoardStatus(FrameLogPathVo frameLogPathVo); | |||
| TensorboardStatusVo getTensorBoardStatus(FrameLogPathVo frameLogPathVo); | |||
| /** | |||
| * 在集群中启动TensorBoard容器,并且返回地址,4小时后销毁 | |||
| * @param frameLogPathVo | |||
| @@ -12,7 +12,7 @@ import com.ruoyi.platform.service.DatasetVersionService; | |||
| import com.ruoyi.platform.utils.BeansUtils; | |||
| import com.ruoyi.platform.utils.FileUtil; | |||
| import com.ruoyi.platform.utils.MinioUtil; | |||
| import com.ruoyi.platform.vo.DatasetVersionVo; | |||
| import com.ruoyi.platform.vo.VersionVo; | |||
| import com.ruoyi.platform.vo.DatasetVo; | |||
| import com.ruoyi.system.api.model.LoginUser; | |||
| import org.apache.commons.lang3.StringUtils; | |||
| @@ -203,16 +203,16 @@ public class DatasetServiceImpl implements DatasetService { | |||
| /** | |||
| * 上传数据集 | |||
| * | |||
| * @param files 文件 | |||
| * @param files 文件 | |||
| * @param uuid | |||
| * @return 是否成功 | |||
| */ | |||
| @Override | |||
| public List<Map<String, String>> uploadDataset(MultipartFile[] files) throws Exception { | |||
| public List<Map<String, String>> uploadDataset(MultipartFile[] files, String uuid) throws Exception { | |||
| List<Map<String, String>> results = new ArrayList<>(); | |||
| //时间戳统一定在外面,一次上传就定好 | |||
| Date createTime = new Date(); | |||
| String timestamp = new SimpleDateFormat("yyyyMMdd-HHmmss").format(createTime); | |||
| // //时间戳统一定在外面,一次上传就定好 | |||
| // Date createTime = new Date(); | |||
| // String timestamp = new SimpleDateFormat("yyyyMMdd-HHmmss").format(createTime); | |||
| for (MultipartFile file:files){ | |||
| if (file.isEmpty()) { | |||
| @@ -225,20 +225,12 @@ public class DatasetServiceImpl implements DatasetService { | |||
| // 其余操作基于 modelsVersionToUse | |||
| String username = SecurityUtils.getLoginUser().getUsername(); | |||
| String fileName = file.getOriginalFilename(); | |||
| String objectName = "datasets/" + username + "/" + timestamp + "/" + fileName; | |||
| String objectName = "datasets/" + username + "/" + uuid + "/" + fileName; | |||
| // 上传文件到MinIO并将记录新增到数据库中 | |||
| try (InputStream inputStream = file.getInputStream()) { | |||
| minioUtil.uploadObject(bucketName, objectName, inputStream); | |||
| // DatasetVersion datasetVersion = new DatasetVersion(); | |||
| // datasetVersion.setDatasetId(id); | |||
| // datasetVersion.setVersion(version); | |||
| // datasetVersion.setUrl(objectName); | |||
| // datasetVersion.setFileName(fileName); | |||
| // datasetVersion.setFileSize(formattedSize); | |||
| // | |||
| // //返回插入结果 | |||
| // DatasetVersion insertedDatasetversion = datasetVersionService.insert(datasetVersion); | |||
| Map<String, String> fileResult = new HashMap<>(); | |||
| fileResult.put("fileName", file.getOriginalFilename()); | |||
| fileResult.put("url", objectName); // objectName根据实际情况定义 | |||
| @@ -313,8 +305,8 @@ public class DatasetServiceImpl implements DatasetService { | |||
| @Override | |||
| @Transactional | |||
| public String insertDatasetAndVersion(DatasetVo datasetVo) throws Exception { | |||
| List<DatasetVersionVo> datasetVersionVos = datasetVo.getDatasetVersionVos(); | |||
| if (datasetVersionVos==null||datasetVersionVos.size()==0){ | |||
| List<VersionVo> datasetVersionVos = datasetVo.getDatasetVersionVos(); | |||
| if (datasetVersionVos==null || datasetVersionVos.isEmpty()){ | |||
| throw new Exception("数据集版本信息错误"); | |||
| } | |||
| @@ -329,7 +321,7 @@ public class DatasetServiceImpl implements DatasetService { | |||
| throw new Exception("新增数据集失败"); | |||
| } | |||
| for (DatasetVersionVo datasetVersionVo :datasetVersionVos){ | |||
| for (VersionVo datasetVersionVo :datasetVersionVos){ | |||
| DatasetVersion datasetVersion = new DatasetVersion(); | |||
| datasetVersion.setDatasetId(datasetInsert.getId()); | |||
| datasetVersion.setVersion(datasetVo.getVersion()); | |||
| @@ -138,9 +138,10 @@ public class DatasetVersionServiceImpl implements DatasetVersionService { | |||
| Map<String, Object> response = new HashMap<>(); | |||
| List<DatasetVersion> datasetVersionList = this.datasetVersionDao.queryAllByDatasetVersion(datasetId, version); | |||
| datasetVersionList.stream(). | |||
| findFirst(). | |||
| ifPresent(datasetVersion -> { | |||
| datasetVersionList.stream() | |||
| .filter(datasetVersion -> datasetVersion.getUrl() != null && !datasetVersion.getUrl().isEmpty()) | |||
| .findFirst() | |||
| .ifPresent(datasetVersion -> { | |||
| String url = datasetVersion.getUrl(); | |||
| String path = bucketName + '/' + url.substring(0, url.lastIndexOf('/')); | |||
| response.put("path", path); | |||
| @@ -1,7 +1,6 @@ | |||
| package com.ruoyi.platform.service.impl; | |||
| import com.ruoyi.common.security.utils.SecurityUtils; | |||
| import com.ruoyi.platform.domain.DatasetVersion; | |||
| import com.ruoyi.platform.domain.Models; | |||
| import com.ruoyi.platform.domain.ModelsVersion; | |||
| import com.ruoyi.platform.mapper.ModelsDao; | |||
| @@ -12,16 +11,15 @@ import com.ruoyi.platform.utils.BeansUtils; | |||
| import com.ruoyi.platform.utils.FileUtil; | |||
| import com.ruoyi.platform.utils.MinioUtil; | |||
| import com.ruoyi.platform.vo.ModelsVo; | |||
| import com.ruoyi.platform.vo.VersionVo; | |||
| import com.ruoyi.system.api.model.LoginUser; | |||
| import io.minio.MinioClient; | |||
| import io.netty.util.Version; | |||
| import org.apache.commons.lang3.StringUtils; | |||
| import org.springframework.beans.factory.annotation.Value; | |||
| import org.springframework.core.io.InputStreamResource; | |||
| import org.springframework.data.domain.Page; | |||
| import org.springframework.data.domain.PageImpl; | |||
| import org.springframework.data.domain.PageRequest; | |||
| import org.springframework.http.HttpHeaders; | |||
| import org.springframework.http.HttpStatus; | |||
| import org.springframework.http.MediaType; | |||
| import org.springframework.http.ResponseEntity; | |||
| import org.springframework.stereotype.Service; | |||
| @@ -33,7 +31,6 @@ import java.io.ByteArrayInputStream; | |||
| import java.io.ByteArrayOutputStream; | |||
| import java.io.InputStream; | |||
| import java.net.URLEncoder; | |||
| import java.text.SimpleDateFormat; | |||
| import java.util.*; | |||
| import java.util.stream.Collectors; | |||
| import java.util.zip.ZipEntry; | |||
| @@ -169,6 +166,7 @@ public class ModelsServiceImpl implements ModelsService { | |||
| ModelsVersion modelsVersion = this.modelsVersionDao.queryById(id); | |||
| if (modelsVersion == null) { | |||
| throw new Exception("未找到该模型下版本记录"); | |||
| } | |||
| // 从数据库中获取存储路径(即MinIO中的对象名称) | |||
| String objectName = modelsVersion.getUrl(); | |||
| @@ -199,15 +197,16 @@ public class ModelsServiceImpl implements ModelsService { | |||
| * 上传模型 | |||
| * | |||
| * @param files 文件 | |||
| * @param uuid | |||
| * @return 是否成功 | |||
| */ | |||
| @Override | |||
| public List<Map<String, String>> uploadModels(MultipartFile[] files) throws Exception { | |||
| public List<Map<String, String>> uploadModels(MultipartFile[] files, String uuid) throws Exception { | |||
| List<Map<String, String>> results = new ArrayList<>(); | |||
| //时间戳统一定在外面,一次上传就定好 | |||
| Date createTime = new Date(); | |||
| String timestamp = new SimpleDateFormat("yyyyMMdd-HHmmss").format(createTime); | |||
| // //时间戳统一定在外面,一次上传就定好 | |||
| // Date createTime = new Date(); | |||
| // String timestamp = new SimpleDateFormat("yyyyMMdd-HHmmss").format(createTime); | |||
| for (MultipartFile file:files){ | |||
| if (file.isEmpty()) { | |||
| @@ -217,22 +216,18 @@ public class ModelsServiceImpl implements ModelsService { | |||
| long sizeInBytes = file.getSize(); | |||
| String formattedSize = FileUtil.formatFileSize(sizeInBytes); // 格式化文件大小 | |||
| // 其余操作基于 modelsVersionToUse | |||
| String username = SecurityUtils.getLoginUser().getUsername(); | |||
| String fileName = file.getOriginalFilename(); | |||
| String objectName = "models/" + username + "/" + timestamp + "/" + fileName; | |||
| String objectName = "models/" + username + "/" + uuid + "/" + fileName; | |||
| // 上传文件到MinIO并将记录新增到数据库中 | |||
| try (InputStream inputStream = file.getInputStream()) { | |||
| minioUtil.uploadObject(bucketName, objectName, inputStream); | |||
| // ModelsVersion modelsVersion = new ModelsVersion(); | |||
| // modelsVersion.setModelsId(id); | |||
| // modelsVersion.setVersion(version); | |||
| // modelsVersion.setUrl(objectName); | |||
| // modelsVersion.setFileName(fileName); | |||
| // modelsVersion.setFileSize(formattedSize); | |||
| // //返回插入结果 | |||
| // ModelsVersion insertedModelsVersion = modelsVersionService.insert(modelsVersion); | |||
| // | |||
| Map<String, String> fileResult = new HashMap<>(); | |||
| fileResult.put("fileName", file.getOriginalFilename()); | |||
| fileResult.put("url", objectName); // objectName根据实际情况定义 | |||
| @@ -361,6 +356,11 @@ public class ModelsServiceImpl implements ModelsService { | |||
| @Override | |||
| @Transactional | |||
| public String insertModelAndVersion(ModelsVo modelsVo) throws Exception { | |||
| List<VersionVo> modelsVersionVos = modelsVo.getModelsVersionVos(); | |||
| if (modelsVersionVos==null || modelsVersionVos.isEmpty()){ | |||
| throw new Exception("模型版本信息错误"); | |||
| } | |||
| Models models = new Models(); | |||
| models.setName(modelsVo.getName()); | |||
| models.setDescription(modelsVo.getDescription()); | |||
| @@ -371,16 +371,20 @@ public class ModelsServiceImpl implements ModelsService { | |||
| if (modelsInsert == null){ | |||
| throw new Exception("新增模型失败"); | |||
| } | |||
| ModelsVersion modelsVersion = new ModelsVersion(); | |||
| modelsVersion.setModelsId(modelsInsert.getId()); | |||
| modelsVersion.setVersion(modelsVo.getVersion()); | |||
| modelsVersion.setUrl(modelsVo.getUrl()); | |||
| modelsVersion.setFileName(modelsVo.getFileName()); | |||
| modelsVersion.setFileSize(modelsVo.getFileSize()); | |||
| ModelsVersion modelsVersionInsert = this.modelsVersionService.insert(modelsVersion); | |||
| if (modelsVersionInsert == null) { | |||
| throw new Exception("新增模型失败"); | |||
| //遍历版本信息列表,把文件信息插入数据库 | |||
| for(VersionVo modelsVersionVo : modelsVersionVos){ | |||
| ModelsVersion modelsVersion = new ModelsVersion(); | |||
| modelsVersion.setModelsId(modelsInsert.getId()); | |||
| modelsVersion.setVersion(modelsVo.getVersion()); | |||
| modelsVersion.setUrl(modelsVersionVo.getUrl()); | |||
| modelsVersion.setFileName(modelsVersionVo.getFileName()); | |||
| modelsVersion.setFileSize(modelsVersionVo.getFileSize()); | |||
| ModelsVersion modelsVersionInsert = this.modelsVersionService.insert(modelsVersion); | |||
| if (modelsVersionInsert == null) { | |||
| throw new Exception("新增模型版本失败"); | |||
| } | |||
| } | |||
| return "新增模型成功"; | |||
| } | |||
| @@ -166,9 +166,10 @@ public class ModelsVersionServiceImpl implements ModelsVersionService { | |||
| Map<String,Object> response = new HashMap<>(); | |||
| List<ModelsVersion> modelsVersionList = this.modelsVersionDao.queryAllByModelsVersion(modelsId, version); | |||
| modelsVersionList.stream(). | |||
| findFirst(). | |||
| ifPresent(modelsVersion -> { | |||
| modelsVersionList.stream() | |||
| .filter(modelsVersion -> modelsVersion.getUrl() != null && !modelsVersion.getUrl().isEmpty()) | |||
| .findFirst() | |||
| .ifPresent(modelsVersion -> { | |||
| String url = modelsVersion.getUrl(); | |||
| String path = bucketName + '/' + url.substring(0, url.lastIndexOf('/')); | |||
| response.put("path", path); | |||
| @@ -1,11 +1,13 @@ | |||
| package com.ruoyi.platform.service.impl; | |||
| import com.ruoyi.common.core.utils.StringUtils; | |||
| import com.ruoyi.common.redis.service.RedisService; | |||
| import com.ruoyi.common.security.utils.SecurityUtils; | |||
| import com.ruoyi.platform.domain.PodStatus; | |||
| import com.ruoyi.platform.service.TensorBoardService; | |||
| import com.ruoyi.platform.utils.K8sClientUtil; | |||
| import com.ruoyi.platform.vo.FrameLogPathVo; | |||
| import com.ruoyi.platform.vo.TensorboardStatusVo; | |||
| import com.ruoyi.system.api.model.LoginUser; | |||
| import org.springframework.beans.factory.annotation.Value; | |||
| import org.springframework.stereotype.Service; | |||
| @@ -23,12 +25,16 @@ public class TensorBoardServiceImpl implements TensorBoardService { | |||
| @Value("${tensorBoard.masterIp}") | |||
| private String masterIp; | |||
| @Resource | |||
| private RedisService redisService; | |||
| @Resource | |||
| private K8sClientUtil k8sClientUtil; | |||
| @Override | |||
| public String getTensorBoardStatus(FrameLogPathVo frameLogPathVo){ | |||
| public TensorboardStatusVo getTensorBoardStatus(FrameLogPathVo frameLogPathVo){ | |||
| String status = PodStatus.Terminated.getName(); | |||
| TensorboardStatusVo tensorboardStatusVo = new TensorboardStatusVo(); | |||
| tensorboardStatusVo.setStatus(status); | |||
| if (StringUtils.isEmpty(frameLogPathVo.getPath())){ | |||
| return status; | |||
| return tensorboardStatusVo; | |||
| } | |||
| LoginUser loginUser = SecurityUtils.getLoginUser(); | |||
| String podName = loginUser.getUsername().toLowerCase()+"-"+frameLogPathVo.getPath().split("/")[2]+ "-tensorboard-pod"; | |||
| @@ -42,9 +48,12 @@ public class TensorBoardServiceImpl implements TensorBoardService { | |||
| } | |||
| } | |||
| } catch (Exception e) { | |||
| return PodStatus.Terminated.getName(); | |||
| return tensorboardStatusVo; | |||
| } | |||
| return status; | |||
| String url = redisService.getCacheObject(podName); | |||
| tensorboardStatusVo.setStatus(status); | |||
| tensorboardStatusVo.setUrl(url); | |||
| return tensorboardStatusVo; | |||
| } | |||
| @Override | |||
| @@ -55,6 +64,7 @@ public class TensorBoardServiceImpl implements TensorBoardService { | |||
| 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(), frameLogPathVo.getPvcName(), image); | |||
| redisService.setCacheObject(podName,masterIp + ":" + podPort); | |||
| return masterIp + ":" + podPort; | |||
| } | |||
| } | |||
| @@ -30,7 +30,9 @@ public class DatasetVo implements Serializable { | |||
| */ | |||
| @ApiModelProperty(name = "version") | |||
| private String version; | |||
| private List<DatasetVersionVo> datasetVersionVos; | |||
| @ApiModelProperty(name = "dataset_version_vos") | |||
| private List<VersionVo> datasetVersionVos; | |||
| /** | |||
| * 可用集群 | |||
| */ | |||
| @@ -85,11 +87,11 @@ public class DatasetVo implements Serializable { | |||
| this.version = version; | |||
| } | |||
| public List<DatasetVersionVo> getDatasetVersionVos() { | |||
| public List<VersionVo> getDatasetVersionVos() { | |||
| return datasetVersionVos; | |||
| } | |||
| public void setDatasetVersionVos(List<DatasetVersionVo> datasetVersionVos) { | |||
| public void setDatasetVersionVos(List<VersionVo> datasetVersionVos) { | |||
| this.datasetVersionVos = datasetVersionVos; | |||
| } | |||
| @@ -2,9 +2,11 @@ package com.ruoyi.platform.vo; | |||
| import com.fasterxml.jackson.databind.PropertyNamingStrategy; | |||
| import com.fasterxml.jackson.databind.annotation.JsonNaming; | |||
| import com.ruoyi.platform.domain.ModelsVersion; | |||
| import io.swagger.annotations.ApiModelProperty; | |||
| import java.io.Serializable; | |||
| import java.util.List; | |||
| @JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) | |||
| public class ModelsVo implements Serializable { | |||
| @@ -36,27 +38,16 @@ public class ModelsVo implements Serializable { | |||
| */ | |||
| @ApiModelProperty(name = "version") | |||
| private String version; | |||
| /** | |||
| * 模型存储地址 | |||
| */ | |||
| @ApiModelProperty(name = "url") | |||
| private String url; | |||
| /** | |||
| * 文件名 | |||
| */ | |||
| @ApiModelProperty(name = "file_name") | |||
| private String fileName; | |||
| /** | |||
| * 文件大小 | |||
| */ | |||
| @ApiModelProperty(name = "file_size") | |||
| private String fileSize; | |||
| /** | |||
| * 状态 | |||
| */ | |||
| @ApiModelProperty(name = "status") | |||
| private Integer status; | |||
| @ApiModelProperty(name = "models_version_vos") | |||
| private List<VersionVo> modelsVersionVos; | |||
| public String getName() { | |||
| return name; | |||
| @@ -107,35 +98,21 @@ public class ModelsVo implements Serializable { | |||
| this.version = version; | |||
| } | |||
| public String getUrl() { | |||
| return url; | |||
| } | |||
| public void setUrl(String url) { | |||
| this.url = url; | |||
| } | |||
| public String getFileName() { | |||
| return fileName; | |||
| public Integer getStatus() { | |||
| return status; | |||
| } | |||
| public void setFileName(String fileName) { | |||
| this.fileName = fileName; | |||
| public void setStatus(Integer status) { | |||
| this.status = status; | |||
| } | |||
| public String getFileSize() { | |||
| return fileSize; | |||
| public List<VersionVo> getModelsVersionVos() { | |||
| return modelsVersionVos; | |||
| } | |||
| public void setFileSize(String fileSize) { | |||
| this.fileSize = fileSize; | |||
| public void setModelsVersionVos(List<VersionVo> modelsVersionVos) { | |||
| this.modelsVersionVos = modelsVersionVos; | |||
| } | |||
| public Integer getStatus() { | |||
| return status; | |||
| } | |||
| public void setStatus(Integer status) { | |||
| this.status = status; | |||
| } | |||
| } | |||
| @@ -0,0 +1,28 @@ | |||
| package com.ruoyi.platform.vo; | |||
| import com.fasterxml.jackson.databind.PropertyNamingStrategy; | |||
| import com.fasterxml.jackson.databind.annotation.JsonNaming; | |||
| import java.io.Serializable; | |||
| @JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) | |||
| public class TensorboardStatusVo implements Serializable { | |||
| private String status; | |||
| private String url; | |||
| public String getStatus() { | |||
| return status; | |||
| } | |||
| public void setStatus(String status) { | |||
| this.status = status; | |||
| } | |||
| public String getUrl() { | |||
| return url; | |||
| } | |||
| public void setUrl(String url) { | |||
| this.url = url; | |||
| } | |||
| } | |||
| @@ -7,7 +7,7 @@ import io.swagger.annotations.ApiModelProperty; | |||
| import java.io.Serializable; | |||
| @JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) | |||
| public class DatasetVersionVo implements Serializable { | |||
| public class VersionVo implements Serializable { | |||
| /** | |||
| * 数据集存储地址 | |||