You've already forked DataMate
feat(annotation): 添加标注类型显示功能
- 在前端页面中新增标注类型列并使用Tag组件展示 - 添加AnnotationTypeMap常量用于标注类型的映射 - 修改接口定义支持labelingType字段的传递 - 更新后端项目创建和更新逻辑以存储标注类型 - 添加标注类型配置键常量统一管理 - 扩展数据传输对象支持标注类型属性 - 实现模板标注类型的继承逻辑
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { Card, Button, Table, message, Modal, Tabs } from "antd";
|
import { Card, Button, Table, Tag, message, Modal, Tabs } from "antd";
|
||||||
import {
|
import {
|
||||||
PlusOutlined,
|
PlusOutlined,
|
||||||
EditOutlined,
|
EditOutlined,
|
||||||
@@ -15,7 +15,11 @@ import {
|
|||||||
deleteAnnotationTaskByIdUsingDelete,
|
deleteAnnotationTaskByIdUsingDelete,
|
||||||
queryAnnotationTasksUsingGet,
|
queryAnnotationTasksUsingGet,
|
||||||
} from "../annotation.api";
|
} from "../annotation.api";
|
||||||
import { mapAnnotationTask, type AnnotationTaskListItem } from "../annotation.const";
|
import {
|
||||||
|
AnnotationTypeMap,
|
||||||
|
mapAnnotationTask,
|
||||||
|
type AnnotationTaskListItem,
|
||||||
|
} from "../annotation.const";
|
||||||
import CreateAnnotationTask from "../Create/components/CreateAnnotationTaskDialog";
|
import CreateAnnotationTask from "../Create/components/CreateAnnotationTaskDialog";
|
||||||
import ExportAnnotationDialog from "./ExportAnnotationDialog";
|
import ExportAnnotationDialog from "./ExportAnnotationDialog";
|
||||||
import { ColumnType } from "antd/es/table";
|
import { ColumnType } from "antd/es/table";
|
||||||
@@ -168,6 +172,21 @@ export default function DataAnnotation() {
|
|||||||
key: "datasetName",
|
key: "datasetName",
|
||||||
width: 180,
|
width: 180,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: "标注类型",
|
||||||
|
dataIndex: "labelingType",
|
||||||
|
key: "labelingType",
|
||||||
|
width: 160,
|
||||||
|
render: (value?: string) => {
|
||||||
|
if (!value) {
|
||||||
|
return "-";
|
||||||
|
}
|
||||||
|
const label =
|
||||||
|
AnnotationTypeMap[value as keyof typeof AnnotationTypeMap]?.label ||
|
||||||
|
value;
|
||||||
|
return <Tag color="geekblue">{label}</Tag>;
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: "数据量",
|
title: "数据量",
|
||||||
dataIndex: "totalCount",
|
dataIndex: "totalCount",
|
||||||
|
|||||||
@@ -23,6 +23,12 @@ type AnnotationTaskPayload = {
|
|||||||
datasetId?: string;
|
datasetId?: string;
|
||||||
datasetName?: string;
|
datasetName?: string;
|
||||||
dataset_name?: string;
|
dataset_name?: string;
|
||||||
|
labelingType?: string;
|
||||||
|
labeling_type?: string;
|
||||||
|
template?: {
|
||||||
|
labelingType?: string;
|
||||||
|
labeling_type?: string;
|
||||||
|
};
|
||||||
totalCount?: number;
|
totalCount?: number;
|
||||||
total_count?: number;
|
total_count?: number;
|
||||||
annotatedCount?: number;
|
annotatedCount?: number;
|
||||||
@@ -48,6 +54,7 @@ export type AnnotationTaskListItem = {
|
|||||||
description?: string;
|
description?: string;
|
||||||
datasetId?: string;
|
datasetId?: string;
|
||||||
datasetName?: string;
|
datasetName?: string;
|
||||||
|
labelingType?: string;
|
||||||
totalCount?: number;
|
totalCount?: number;
|
||||||
annotatedCount?: number;
|
annotatedCount?: number;
|
||||||
inProgressCount?: number;
|
inProgressCount?: number;
|
||||||
@@ -90,6 +97,11 @@ export function mapAnnotationTask(task: AnnotationTaskPayload): AnnotationTaskLi
|
|||||||
const labelingProjId = task?.labelingProjId || task?.labelingProjectId || task?.projId || task?.labeling_project_id || "";
|
const labelingProjId = task?.labelingProjId || task?.labelingProjectId || task?.projId || task?.labeling_project_id || "";
|
||||||
const segmentationEnabled = task?.segmentationEnabled ?? task?.segmentation_enabled ?? false;
|
const segmentationEnabled = task?.segmentationEnabled ?? task?.segmentation_enabled ?? false;
|
||||||
const inProgressCount = task?.inProgressCount ?? task?.in_progress_count ?? 0;
|
const inProgressCount = task?.inProgressCount ?? task?.in_progress_count ?? 0;
|
||||||
|
const labelingType =
|
||||||
|
task?.labelingType ||
|
||||||
|
task?.labeling_type ||
|
||||||
|
task?.template?.labelingType ||
|
||||||
|
task?.template?.labeling_type;
|
||||||
|
|
||||||
const statsArray = task?.statistics
|
const statsArray = task?.statistics
|
||||||
? [
|
? [
|
||||||
@@ -107,6 +119,7 @@ export function mapAnnotationTask(task: AnnotationTaskPayload): AnnotationTaskLi
|
|||||||
projId: labelingProjId,
|
projId: labelingProjId,
|
||||||
segmentationEnabled,
|
segmentationEnabled,
|
||||||
inProgressCount,
|
inProgressCount,
|
||||||
|
labelingType,
|
||||||
name: task.name,
|
name: task.name,
|
||||||
description: task.description || "",
|
description: task.description || "",
|
||||||
datasetName: task.datasetName || task.dataset_name || "-",
|
datasetName: task.datasetName || task.dataset_name || "-",
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ router = APIRouter(
|
|||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
TEXT_DATASET_TYPE = "TEXT"
|
TEXT_DATASET_TYPE = "TEXT"
|
||||||
SOURCE_DOCUMENT_FILE_TYPES = {"pdf", "doc", "docx", "xls", "xlsx"}
|
SOURCE_DOCUMENT_FILE_TYPES = {"pdf", "doc", "docx", "xls", "xlsx"}
|
||||||
|
LABELING_TYPE_CONFIG_KEY = "labeling_type"
|
||||||
|
|
||||||
@router.get("/{mapping_id}/login")
|
@router.get("/{mapping_id}/login")
|
||||||
async def login_label_studio(
|
async def login_label_studio(
|
||||||
@@ -82,6 +83,7 @@ async def create_mapping(
|
|||||||
|
|
||||||
# 如果提供了模板ID,获取模板配置
|
# 如果提供了模板ID,获取模板配置
|
||||||
label_config = None
|
label_config = None
|
||||||
|
template_labeling_type = None
|
||||||
if request.template_id:
|
if request.template_id:
|
||||||
logger.info(f"Using template: {request.template_id}")
|
logger.info(f"Using template: {request.template_id}")
|
||||||
template = await template_service.get_template(db, request.template_id)
|
template = await template_service.get_template(db, request.template_id)
|
||||||
@@ -91,6 +93,7 @@ async def create_mapping(
|
|||||||
detail=f"Template not found: {request.template_id}"
|
detail=f"Template not found: {request.template_id}"
|
||||||
)
|
)
|
||||||
label_config = template.label_config
|
label_config = template.label_config
|
||||||
|
template_labeling_type = getattr(template, "labeling_type", None)
|
||||||
logger.debug(f"Template label config loaded for template: {template.name}")
|
logger.debug(f"Template label config loaded for template: {template.name}")
|
||||||
|
|
||||||
# 如果直接提供了 label_config (自定义或修改后的),则覆盖模板配置
|
# 如果直接提供了 label_config (自定义或修改后的),则覆盖模板配置
|
||||||
@@ -109,6 +112,8 @@ async def create_mapping(
|
|||||||
project_configuration["description"] = project_description
|
project_configuration["description"] = project_description
|
||||||
if dataset_type == TEXT_DATASET_TYPE and request.segmentation_enabled is not None:
|
if dataset_type == TEXT_DATASET_TYPE and request.segmentation_enabled is not None:
|
||||||
project_configuration["segmentation_enabled"] = bool(request.segmentation_enabled)
|
project_configuration["segmentation_enabled"] = bool(request.segmentation_enabled)
|
||||||
|
if template_labeling_type:
|
||||||
|
project_configuration[LABELING_TYPE_CONFIG_KEY] = template_labeling_type
|
||||||
|
|
||||||
labeling_project = LabelingProject(
|
labeling_project = LabelingProject(
|
||||||
id=str(uuid.uuid4()), # Generate UUID here
|
id=str(uuid.uuid4()), # Generate UUID here
|
||||||
@@ -441,6 +446,18 @@ async def update_mapping(
|
|||||||
|
|
||||||
if request.template_id is not None:
|
if request.template_id is not None:
|
||||||
update_values["template_id"] = request.template_id
|
update_values["template_id"] = request.template_id
|
||||||
|
template_labeling_type = None
|
||||||
|
if request.template_id:
|
||||||
|
template_service = AnnotationTemplateService()
|
||||||
|
template = await template_service.get_template(db, request.template_id)
|
||||||
|
if not template:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=404,
|
||||||
|
detail=f"Template not found: {request.template_id}"
|
||||||
|
)
|
||||||
|
template_labeling_type = getattr(template, "labeling_type", None)
|
||||||
|
if template_labeling_type:
|
||||||
|
configuration[LABELING_TYPE_CONFIG_KEY] = template_labeling_type
|
||||||
|
|
||||||
if not update_values:
|
if not update_values:
|
||||||
# 没有要更新的字段,直接返回当前数据
|
# 没有要更新的字段,直接返回当前数据
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ class DatasetMappingResponse(BaseModel):
|
|||||||
name: Optional[str] = Field(None, description="标注项目名称")
|
name: Optional[str] = Field(None, description="标注项目名称")
|
||||||
description: Optional[str] = Field(None, description="标注项目描述")
|
description: Optional[str] = Field(None, description="标注项目描述")
|
||||||
template_id: Optional[str] = Field(None, alias="templateId", description="关联的模板ID")
|
template_id: Optional[str] = Field(None, alias="templateId", description="关联的模板ID")
|
||||||
|
labeling_type: Optional[str] = Field(None, alias="labelingType", description="标注类型")
|
||||||
template: Optional['AnnotationTemplateResponse'] = Field(None, description="关联的标注模板详情")
|
template: Optional['AnnotationTemplateResponse'] = Field(None, description="关联的标注模板详情")
|
||||||
label_config: Optional[str] = Field(None, alias="labelConfig", description="实际使用的 Label Studio XML 配置")
|
label_config: Optional[str] = Field(None, alias="labelConfig", description="实际使用的 Label Studio XML 配置")
|
||||||
segmentation_enabled: Optional[bool] = Field(
|
segmentation_enabled: Optional[bool] = Field(
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ from datetime import datetime
|
|||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from app.core.logging import get_logger
|
from app.core.logging import get_logger
|
||||||
from app.db.models import LabelingProject, AnnotationTemplate, AnnotationResult, LabelingProjectFile
|
from app.db.models import LabelingProject, AnnotationResult, LabelingProjectFile
|
||||||
from app.db.models.annotation_management import ANNOTATION_STATUS_IN_PROGRESS
|
from app.db.models.annotation_management import ANNOTATION_STATUS_IN_PROGRESS
|
||||||
from app.db.models.dataset_management import Dataset, DatasetFiles
|
from app.db.models.dataset_management import Dataset, DatasetFiles
|
||||||
from app.module.annotation.schema import (
|
from app.module.annotation.schema import (
|
||||||
@@ -18,6 +18,7 @@ from app.module.annotation.schema import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
LABELING_TYPE_CONFIG_KEY = "labeling_type"
|
||||||
|
|
||||||
class DatasetMappingService:
|
class DatasetMappingService:
|
||||||
"""数据集映射服务"""
|
"""数据集映射服务"""
|
||||||
@@ -106,10 +107,12 @@ class DatasetMappingService:
|
|||||||
label_config = None
|
label_config = None
|
||||||
description = None
|
description = None
|
||||||
segmentation_enabled = None
|
segmentation_enabled = None
|
||||||
|
labeling_type = None
|
||||||
if isinstance(configuration, dict):
|
if isinstance(configuration, dict):
|
||||||
label_config = configuration.get('label_config')
|
label_config = configuration.get('label_config')
|
||||||
description = configuration.get('description')
|
description = configuration.get('description')
|
||||||
segmentation_enabled = configuration.get('segmentation_enabled')
|
segmentation_enabled = configuration.get('segmentation_enabled')
|
||||||
|
labeling_type = configuration.get(LABELING_TYPE_CONFIG_KEY)
|
||||||
|
|
||||||
# Optionally fetch full template details
|
# Optionally fetch full template details
|
||||||
template_response = None
|
template_response = None
|
||||||
@@ -119,6 +122,9 @@ class DatasetMappingService:
|
|||||||
template_response = await template_service.get_template(self.db, template_id)
|
template_response = await template_service.get_template(self.db, template_id)
|
||||||
logger.debug(f"Included template details for template_id: {template_id}")
|
logger.debug(f"Included template details for template_id: {template_id}")
|
||||||
|
|
||||||
|
if not labeling_type and template_response:
|
||||||
|
labeling_type = getattr(template_response, "labeling_type", None)
|
||||||
|
|
||||||
# 获取统计数据
|
# 获取统计数据
|
||||||
total_count, annotated_count, in_progress_count = await self._get_project_stats(
|
total_count, annotated_count, in_progress_count = await self._get_project_stats(
|
||||||
mapping.id, mapping.dataset_id
|
mapping.id, mapping.dataset_id
|
||||||
@@ -132,6 +138,7 @@ class DatasetMappingService:
|
|||||||
"name": mapping.name,
|
"name": mapping.name,
|
||||||
"description": description,
|
"description": description,
|
||||||
"template_id": template_id,
|
"template_id": template_id,
|
||||||
|
"labeling_type": labeling_type,
|
||||||
"template": template_response,
|
"template": template_response,
|
||||||
"label_config": label_config,
|
"label_config": label_config,
|
||||||
"segmentation_enabled": segmentation_enabled,
|
"segmentation_enabled": segmentation_enabled,
|
||||||
@@ -174,10 +181,12 @@ class DatasetMappingService:
|
|||||||
label_config = None
|
label_config = None
|
||||||
description = None
|
description = None
|
||||||
segmentation_enabled = None
|
segmentation_enabled = None
|
||||||
|
labeling_type = None
|
||||||
if isinstance(configuration, dict):
|
if isinstance(configuration, dict):
|
||||||
label_config = configuration.get('label_config')
|
label_config = configuration.get('label_config')
|
||||||
description = configuration.get('description')
|
description = configuration.get('description')
|
||||||
segmentation_enabled = configuration.get('segmentation_enabled')
|
segmentation_enabled = configuration.get('segmentation_enabled')
|
||||||
|
labeling_type = configuration.get(LABELING_TYPE_CONFIG_KEY)
|
||||||
|
|
||||||
# Optionally fetch full template details
|
# Optionally fetch full template details
|
||||||
template_response = None
|
template_response = None
|
||||||
@@ -187,6 +196,9 @@ class DatasetMappingService:
|
|||||||
template_response = await template_service.get_template(self.db, template_id)
|
template_response = await template_service.get_template(self.db, template_id)
|
||||||
logger.debug(f"Included template details for template_id: {template_id}")
|
logger.debug(f"Included template details for template_id: {template_id}")
|
||||||
|
|
||||||
|
if not labeling_type and template_response:
|
||||||
|
labeling_type = getattr(template_response, "labeling_type", None)
|
||||||
|
|
||||||
# 获取统计数据
|
# 获取统计数据
|
||||||
total_count, annotated_count, in_progress_count = 0, 0, 0
|
total_count, annotated_count, in_progress_count = 0, 0, 0
|
||||||
if dataset_id:
|
if dataset_id:
|
||||||
@@ -203,6 +215,7 @@ class DatasetMappingService:
|
|||||||
"name": mapping.name,
|
"name": mapping.name,
|
||||||
"description": description,
|
"description": description,
|
||||||
"template_id": template_id,
|
"template_id": template_id,
|
||||||
|
"labeling_type": labeling_type,
|
||||||
"template": template_response,
|
"template": template_response,
|
||||||
"label_config": label_config,
|
"label_config": label_config,
|
||||||
"segmentation_enabled": segmentation_enabled,
|
"segmentation_enabled": segmentation_enabled,
|
||||||
|
|||||||
Reference in New Issue
Block a user