Browse Source

Merge pull request '合并dev' (#87) from dev into master

dev-lhz
cp3hnu 1 year ago
parent
commit
a15d654ce0
13 changed files with 331 additions and 37 deletions
  1. +5
    -0
      react-ui/config/routes.ts
  2. +1
    -1
      react-ui/src/pages/Dataset/components/ResourceIntro/index.tsx
  3. +9
    -1
      react-ui/src/pages/DevelopmentEnvironment/List/index.tsx
  4. +21
    -0
      react-ui/src/pages/Experiment/Comparison/index.less
  5. +212
    -0
      react-ui/src/pages/Experiment/Comparison/index.tsx
  6. +31
    -2
      react-ui/src/pages/Experiment/index.jsx
  7. +1
    -1
      react-ui/src/pages/Experiment/index.less
  8. +1
    -1
      react-ui/src/requestConfig.ts
  9. +14
    -0
      react-ui/src/services/experiment/index.js
  10. +3
    -8
      react-ui/src/services/session.ts
  11. +5
    -5
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/domain/DevEnvironment.java
  12. +12
    -2
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/JupyterServiceImpl.java
  13. +16
    -16
      ruoyi-modules/management-platform/src/main/resources/mapper/managementPlatform/DevEnvironmentDaoMapper.xml

+ 5
- 0
react-ui/config/routes.ts View File

@@ -96,6 +96,11 @@ export default [
path: ':workflowId/:id',
component: './Experiment/training/index',
},
{
name: '实验对比',
path: 'compare',
component: './Experiment/Comparison/index',
},
],
},
],


+ 1
- 1
react-ui/src/pages/Dataset/components/ResourceIntro/index.tsx View File

@@ -58,7 +58,7 @@ const ResourceIntro = ({ resourceType }: ResourceIntroProps) => {
};
}),
);
if (versionParam) {
if (versionParam && res.data.includes(versionParam)) {
setVersion(versionParam);
versionParam = null;
} else {


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

@@ -38,6 +38,7 @@ export type EditorData = {
computing_resource: string;
update_by: string;
create_time: string;
url: string;
};

function EditorList() {
@@ -140,7 +141,14 @@ function EditorList() {
dataIndex: 'name',
key: 'name',
width: '30%',
render: CommonTableCell(),
render: (text, record) =>
record.url ? (
<a href={record.url} target="_blank" rel="noreferrer">
{text}
</a>
) : (
<span>{text ?? '--'}</span>
),
},
{
title: '状态',


+ 21
- 0
react-ui/src/pages/Experiment/Comparison/index.less View File

@@ -0,0 +1,21 @@
.experiment-comparison {
height: 100%;
&__header {
display: flex;
align-items: center;
height: 50px;
margin-bottom: 10px;
padding: 0 30px;
background-image: url(@/assets/img/page-title-bg.png);
background-repeat: no-repeat;
background-position: top center;
background-size: 100% 100%;
}

&__table {
height: calc(100% - 60px);
padding: 20px 30px 0;
background-color: white;
border-radius: 10px;
}
}

+ 212
- 0
react-ui/src/pages/Experiment/Comparison/index.tsx View File

@@ -0,0 +1,212 @@
import CommonTableCell from '@/components/CommonTableCell';
import { useCacheState } from '@/hooks/pageCacheState';
import { getExpEvaluateInfosReq, getExpTrainInfosReq } from '@/services/experiment';
import { to } from '@/utils/promise';
import { useSearchParams } from '@umijs/max';
import { Button, Table, TablePaginationConfig, TableProps } from 'antd';
import classNames from 'classnames';
import { useEffect, useState } from 'react';
import styles from './index.less';

export enum ComparisonType {
Train = 'train',
Evaluate = 'evaluate',
}

function ExperimentComparison() {
const [searchParams] = useSearchParams();
const comparisonType = searchParams.get('type');
const experimentId = searchParams.get('experimentId');
const [tableData, setTableData] = useState([]);
const [cacheState, setCacheState] = useCacheState();
const [total, setTotal] = useState(0);
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
const [pagination, setPagination] = useState<TablePaginationConfig>(
cacheState?.pagination ?? {
current: 1,
pageSize: 10,
},
);

useEffect(() => {
getComparisonData();
}, [experimentId]);

// 获取对比数据列表
const getComparisonData = async () => {
const request =
comparisonType === ComparisonType.Train ? getExpTrainInfosReq : getExpEvaluateInfosReq;
const [res] = await to(request(experimentId));
if (res && res.data) {
const { content = [], totalElements = 0 } = res.data;
setTableData(content);
setTotal(totalElements);
}
};

// 分页切换
const handleTableChange: TableProps['onChange'] = (pagination, filters, sorter, { action }) => {
if (action === 'paginate') {
setPagination(pagination);
}
// console.log(pagination, filters, sorter, action);
};

const rowSelection: TableProps['rowSelection'] = {
type: 'checkbox',
selectedRowKeys,
onChange: (selectedRowKeys: React.Key[], selectedRows: any[]) => {
console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows);
setSelectedRowKeys(selectedRowKeys);
},
};

const columns: TableProps['columns'] = [
{
title: '基本信息',
children: [
{
title: '实例ID',
dataIndex: 'name',
key: 'name',
width: '30%',
render: CommonTableCell(),
},
{
title: '运行时间',
dataIndex: 'name',
key: 'name',
width: '30%',
render: CommonTableCell(),
},
{
title: '运行状态',
dataIndex: 'name',
key: 'name',
width: '30%',
render: CommonTableCell(),
},
{
title: '训练数据集',
dataIndex: 'name',
key: 'name',
width: '30%',
render: CommonTableCell(),
},
{
title: '增量训练',
dataIndex: 'name',
key: 'name',
width: '30%',
render: CommonTableCell(),
},
],
},
{
title: '训练参数',
children: [
{
title: 'batchsize',
dataIndex: 'name',
key: 'name',
width: '30%',
render: CommonTableCell(),
},
{
title: 'config',
dataIndex: 'name',
key: 'name',
width: '30%',
render: CommonTableCell(),
},
{
title: 'epoch',
dataIndex: 'name',
key: 'name',
width: '30%',
render: CommonTableCell(),
},
{
title: 'lr',
dataIndex: 'name',
key: 'name',
width: '30%',
render: CommonTableCell(),
},
{
title: 'warmup_iters',
dataIndex: 'name',
key: 'name',
width: '30%',
render: CommonTableCell(),
},
],
},
{
title: '训练指标',
children: [
{
title: 'metrc_name',
dataIndex: 'name',
key: 'name',
width: '30%',
render: CommonTableCell(),
},
{
title: 'test_1',
dataIndex: 'name',
key: 'name',
width: '30%',
render: CommonTableCell(),
},
{
title: 'test_2',
dataIndex: 'name',
key: 'name',
width: '30%',
render: CommonTableCell(),
},
{
title: 'test_3',
dataIndex: 'name',
key: 'name',
width: '30%',
render: CommonTableCell(),
},
{
title: 'test_4',
dataIndex: 'name',
key: 'name',
width: '30%',
render: CommonTableCell(),
},
],
},
];

return (
<div className={styles['experiment-comparison']}>
<div className={styles['experiment-comparison__header']}>
<Button type="default">可视化对比</Button>
</div>
<div className={classNames('vertical-scroll-table', styles['experiment-comparison__table'])}>
<Table
dataSource={tableData}
columns={columns}
rowSelection={rowSelection}
scroll={{ y: 'calc(100% - 55px)' }}
pagination={{
...pagination,
total: total,
showSizeChanger: true,
showQuickJumper: true,
}}
onChange={handleTableChange}
rowKey="id"
/>
</div>
</div>
);
}

export default ExperimentComparison;

+ 31
- 2
react-ui/src/pages/Experiment/index.jsx View File

@@ -18,11 +18,12 @@ import themes from '@/styles/theme.less';
import { elapsedTime, formatDate } from '@/utils/date';
import { to } from '@/utils/promise';
import { modalConfirm } from '@/utils/ui';
import { App, Button, ConfigProvider, Space, Table, Tooltip } from 'antd';
import { App, Button, ConfigProvider, Dropdown, Space, Table, Tooltip } from 'antd';
import classNames from 'classnames';
import { useEffect, useRef, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import AddExperimentModal from './components/AddExperimentModal';
import { ComparisonType } from './components/ComparisonModal/config';
import TensorBoardStatus, { TensorBoardStatusEnum } from './components/TensorBoardStatus';
import Styles from './index.less';
import { experimentStatusInfo } from './status';
@@ -270,6 +271,26 @@ function Experiment() {
window.open(experimentIn.tensorboardUrl, '_blank');
}
};

// 实验对比菜单
const getComparisonMenu = (experimentId) => {
return {
items: [
{
label: <span>训练对比</span>,
key: ComparisonType.Train,
},
{
label: <span>评估对比</span>,
key: ComparisonType.Evaluate,
},
],
onClick: ({ key }) => {
navgite(`/pipeline/experiment/compare?type=${key}&id=${experimentId}`);
},
};
};

const columns = [
{
title: '实验名称',
@@ -320,7 +341,7 @@ function Experiment() {
{
title: '操作',
key: 'action',
width: 300,
width: 350,
render: (_, record) => (
<Space size="small">
<Button
@@ -345,6 +366,14 @@ function Experiment() {
>
编辑
</Button>
<Dropdown key="comparison" menu={getComparisonMenu(record.id)}>
<a onClick={(e) => e.preventDefault()}>
<Space style={{ padding: '0 7px' }}>
<KFIcon type="icon-shiyanduibi" />
实验对比
</Space>
</a>
</Dropdown>
<ConfigProvider
theme={{
token: {


+ 1
- 1
react-ui/src/pages/Experiment/index.less View File

@@ -58,7 +58,7 @@
}

.operation {
width: 284px;
width: 334px;
}
}
.tableExpandBoxContent {


+ 1
- 1
react-ui/src/requestConfig.ts View File

@@ -40,7 +40,7 @@ export const requestConfig: RequestConfig = {
[
(response: AxiosResponse) => {
const { status, data } = response || {};
console.log(message, data);
// console.log(message, data);
if (status >= 200 && status < 300) {
if (data && (data instanceof Blob || data.code === 200)) {
return response;


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

@@ -116,3 +116,17 @@ export function getTensorBoardStatusReq(data) {
data,
});
}

// 获取当前实验的模型推理指标信息
export function getExpEvaluateInfosReq(experimentId) {
return request(`/api/mmp/aim/getExpEvaluateInfos/${experimentId}`, {
method: 'GET',
});
}

// 获取当前实验的模型训练指标信息
export function getExpTrainInfosReq(experimentId) {
return request(`/api/mmp//aim/getExpTrainInfos/${experimentId}`, {
method: 'GET',
});
}

+ 3
- 8
react-ui/src/services/session.ts View File

@@ -60,8 +60,8 @@ function patchRouteItems(route: any, menu: any, parentPath: string) {
element: React.createElement(lazy(() => import('@/pages/' + path))),
path: parentPath + menuItem.path,
};
console.log(newRoute);
// console.log(newRoute);
route.children.push(newRoute);
route.routes.push(newRoute);
}
@@ -74,10 +74,7 @@ export function patchRouteWithRemoteMenus(routes: any) {
}
let proLayout = null;
for (const routeItem of routes) {

if (routeItem.id === 'ant-design-pro-layout') {
proLayout = routeItem;
break;
}
@@ -101,7 +98,6 @@ export async function refreshToken() {
}

export function convertCompatRouters(childrens: API.RoutersMenuItem[]): any[] {

return childrens.map((item: API.RoutersMenuItem) => {
return {
path: item.path,
@@ -149,12 +145,11 @@ export function getMatchMenuItem(
const subpath = path.substr(item.path.length + 1);
const subItem: MenuDataItem[] = getMatchMenuItem(subpath, item.routes);
items = items.concat(subItem);

} else {
const paths = path.split('/');
if (paths.length >= 2 && paths[0] === item.path && paths[1] === 'index') {
console.log(item);
items.push(item);
}
}


+ 5
- 5
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/domain/DevEnvironment.java View File

@@ -54,7 +54,7 @@ public class DevEnvironment implements Serializable {
/**
* 备用字段1
*/
private String altField1;
private String url;
/**
* 备用字段2
*/
@@ -153,12 +153,12 @@ public class DevEnvironment implements Serializable {
this.model = model;
}

public String getAltField1() {
return altField1;
public String getUrl() {
return url;
}

public void setAltField1(String altField1) {
this.altField1 = altField1;
public void setUrl(String url) {
this.url = url;
}

public String getAltField2() {


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

@@ -101,7 +101,15 @@ public class JupyterServiceImpl implements JupyterService {

// 调用修改后的 createPod 方法,传入额外的参数
Integer podPort = k8sClientUtil.createConfiguredPod(podName, namespace, port, mountPath, pvc, image, minioPvcName, datasetPath, modelPath);
return masterIp + ":" + podPort;
// 简单的延迟,以便 Pod 有时间启动
Thread.sleep(2500);
//查询pod状态,更新到数据库
String podStatus = k8sClientUtil.getPodStatus(podName, namespace);
String url = masterIp + ":" + podPort;
devEnvironment.setStatus(podStatus);
devEnvironment.setUrl(url);
this.devEnvironmentService.update(devEnvironment);
return url ;


}
@@ -112,7 +120,6 @@ public class JupyterServiceImpl implements JupyterService {
if (devEnvironment==null){
throw new Exception("开发环境配置不存在");
}

LoginUser loginUser = SecurityUtils.getLoginUser();
//手动构造pod名称
String podName = loginUser.getUsername().toLowerCase() +"-editor-pod" + "-" + id;
@@ -124,6 +131,9 @@ public class JupyterServiceImpl implements JupyterService {
// 使用 Kubernetes API 删除 Pod
String deleteResult = k8sClientUtil.deletePod(podName, namespace);


devEnvironment.setStatus("Terminating");
this.devEnvironmentService.update(devEnvironment);
return deleteResult + ",编辑器已停止";
}



+ 16
- 16
ruoyi-modules/management-platform/src/main/resources/mapper/managementPlatform/DevEnvironmentDaoMapper.xml View File

@@ -12,7 +12,7 @@
<result property="image" column="image" jdbcType="VARCHAR"/>
<result property="dataset" column="dataset" jdbcType="VARCHAR"/>
<result property="model" column="model" jdbcType="VARCHAR"/>
<result property="altField1" column="alt_field1" jdbcType="VARCHAR"/>
<result property="url" column="url" jdbcType="VARCHAR"/>
<result property="altField2" column="alt_field2" jdbcType="VARCHAR"/>
<result property="createBy" column="create_by" jdbcType="VARCHAR"/>
<result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
@@ -24,7 +24,7 @@
<!--查询单个-->
<select id="queryById" resultMap="DevEnvironmentMap">
select
id,name,status,computing_resource,standard,env_variable,image,dataset,model,alt_field1,alt_field2,create_by,create_time,update_by,update_time,state
id,name,status,computing_resource,standard,env_variable,image,dataset,model,url,alt_field2,create_by,create_time,update_by,update_time,state
from dev_environment
where id = #{id} and state = 1
</select>
@@ -32,7 +32,7 @@
<!--查询指定行数据-->
<select id="queryAllByLimit" resultMap="DevEnvironmentMap">
select
id,name,status,computing_resource,standard,env_variable,image,dataset,model,alt_field1,alt_field2,create_by,create_time,update_by,update_time,state
id,name,status,computing_resource,standard,env_variable,image,dataset,model,url,alt_field2,create_by,create_time,update_by,update_time,state
from dev_environment
<where>
state = 1
@@ -63,8 +63,8 @@
<if test="devEnvironment.model != null and devEnvironment.model != ''">
and model = #{devEnvironment.model}
</if>
<if test="devEnvironment.altField1 != null and devEnvironment.altField1 != ''">
and alt_field1 = #{devEnvironment.altField1}
<if test="devEnvironment.url != null and devEnvironment.url != ''">
and url = #{devEnvironment.url}
</if>
<if test="devEnvironment.altField2 != null and devEnvironment.altField2 != ''">
and alt_field2 = #{devEnvironment.altField2}
@@ -122,8 +122,8 @@
<if test="devEnvironment.model != null and devEnvironment.model != ''">
and model = #{devEnvironment.model}
</if>
<if test="devEnvironment.altField1 != null and devEnvironment.altField1 != ''">
and alt_field1 = #{devEnvironment.altField1}
<if test="devEnvironment.url != null and devEnvironment.url != ''">
and url = #{devEnvironment.url}
</if>
<if test="devEnvironment.altField2 != null and devEnvironment.altField2 != ''">
and alt_field2 = #{devEnvironment.altField2}
@@ -148,7 +148,7 @@

<!--新增所有列-->
<insert id="insert" keyProperty="id" useGeneratedKeys="true">
insert into dev_environment(name,status,computing_resource,standard,env_variable,image,dataset,model,alt_field1,alt_field2,create_by,create_time,update_by,update_time,state)
insert into dev_environment(name,status,computing_resource,standard,env_variable,image,dataset,model,url,alt_field2,create_by,create_time,update_by,update_time,state)
values (#{devEnvironment.name},
#{devEnvironment.status},
#{devEnvironment.computingResource},
@@ -157,7 +157,7 @@
#{devEnvironment.image},
#{devEnvironment.dataset},
#{devEnvironment.model},
#{devEnvironment.altField1},
#{devEnvironment.url},
#{devEnvironment.altField2},
#{devEnvironment.createBy},
#{devEnvironment.createTime},
@@ -168,21 +168,21 @@
</insert>

<insert id="insertBatch" keyProperty="id" useGeneratedKeys="true">
insert into dev_environment(name,status,computing_resource,standard,env_variable,image,dataset,model,alt_field1,alt_field2,create_by,create_time,update_by,update_time,state )
insert into dev_environment(name,status,computing_resource,standard,env_variable,image,dataset,model,url,alt_field2,create_by,create_time,update_by,update_time,state )
values
<foreach collection="entities" item="entity" separator=",">
(#{entity.name},#{entity.status},#{entity.computingResource},#{entity.standard},#{entity.envVariable},#{entity.image},#{entity.dataset},#{entity.model},#{entity.altField1},#{entity.altField2},#{entity.createBy},#{entity.createTime},#{entity.updateBy},#{entity.updateTime},#{entity.state})
(#{entity.name},#{entity.status},#{entity.computingResource},#{entity.standard},#{entity.envVariable},#{entity.image},#{entity.dataset},#{entity.model},#{entity.url},#{entity.altField2},#{entity.createBy},#{entity.createTime},#{entity.updateBy},#{entity.updateTime},#{entity.state})
</foreach>
</insert>

<insert id="insertOrUpdateBatch" keyProperty="id" useGeneratedKeys="true">
insert into dev_environment(name,status,computing_resource,standard,env_variable,image,dataset,model,alt_field1,alt_field2,create_by,create_time,update_by,update_time,state)
insert into dev_environment(name,status,computing_resource,standard,env_variable,image,dataset,model,url,alt_field2,create_by,create_time,update_by,update_time,state)
values
<foreach collection="entities" item="entity" separator=",">
(#{entity.name}#{entity.status}#{entity.computingResource}#{entity.standard}#{entity.envVariable}#{entity.image}#{entity.dataset}#{entity.model}#{entity.altField1}#{entity.altField2}#{entity.createBy}#{entity.createTime}#{entity.updateBy}#{entity.updateTime}#{entity.state})
(#{entity.name}#{entity.status}#{entity.computingResource}#{entity.standard}#{entity.envVariable}#{entity.image}#{entity.dataset}#{entity.model}#{entity.url}#{entity.altField2}#{entity.createBy}#{entity.createTime}#{entity.updateBy}#{entity.updateTime}#{entity.state})
</foreach>
on duplicate key update
name = values(name)status = values(status)computing_resource = values(computing_resource)standard = values(standard)env_variable = values(env_variable)image = values(image)dataset = values(dataset)model = values(model)alt_field1 = values(alt_field1)alt_field2 = values(alt_field2)create_by = values(create_by)create_time = values(create_time)update_by = values(update_by)update_time = values(update_time)state = values(state)
name = values(name)status = values(status)computing_resource = values(computing_resource)standard = values(standard)env_variable = values(env_variable)image = values(image)dataset = values(dataset)model = values(model)url = values(url)alt_field2 = values(alt_field2)create_by = values(create_by)create_time = values(create_time)update_by = values(update_by)update_time = values(update_time)state = values(state)
</insert>

<!--通过主键修改数据-->
@@ -213,8 +213,8 @@ name = values(name)status = values(status)computing_resource = values(computing_
<if test="devEnvironment.model != null and devEnvironment.model != ''">
model = #{devEnvironment.model},
</if>
<if test="devEnvironment.altField1 != null and devEnvironment.altField1 != ''">
alt_field1 = #{devEnvironment.altField1},
<if test="devEnvironment.url != null and devEnvironment.url != ''">
url = #{devEnvironment.url},
</if>
<if test="devEnvironment.altField2 != null and devEnvironment.altField2 != ''">
alt_field2 = #{devEnvironment.altField2},


Loading…
Cancel
Save