feat: 完善数据标注导出格式兼容性验证

- 后端:添加 YOLO 格式对 TEXT 数据集的限制验证
- 后端:统一 COCO/YOLO 兼容性校验规则(仅允许图像类或目标检测类数据集)
- 后端:修复 datasetType 字段传递,在任务列表响应中补充 dataset_type
- 前端:在导出对话框中禁用 TEXT 数据集的 COCO/YOLO 选项
- 前端:添加 datasetType 和 labelingType 字段传递
- 前端:对齐前后端 COCO/YOLO 兼容性规则
- 前端:优化提示文案,明确说明格式适用范围

修改文件:
- runtime/datamate-python/app/module/annotation/service/export.py
- runtime/datamate-python/app/module/annotation/service/mapping.py
- runtime/datamate-python/app/module/annotation/schema/mapping.py
- frontend/src/pages/DataAnnotation/Home/ExportAnnotationDialog.tsx
- frontend/src/pages/DataAnnotation/Home/DataAnnotation.tsx
- frontend/src/pages/DataAnnotation/annotation.const.tsx
This commit is contained in:
2026-02-07 16:05:27 +08:00
parent 36b410ba7b
commit 3dd4035005
6 changed files with 73 additions and 19 deletions

View File

@@ -84,7 +84,7 @@ DATASET_TYPE_IMAGE = "IMAGE"
DATASET_TYPE_OBJECT_DETECTION = "OBJECT_DETECTION"
LABELING_TYPE_CONFIG_KEY = "labeling_type"
LABELING_TYPE_OBJECT_DETECTION = "OBJECT_DETECTION"
COCO_COMPATIBLE_DATASET_TYPES = {
DETECTION_COMPATIBLE_DATASET_TYPES = {
DATASET_TYPE_IMAGE,
DATASET_TYPE_OBJECT_DETECTION,
}
@@ -236,7 +236,7 @@ class AnnotationExportService:
project: LabelingProject,
format_type: ExportFormat,
) -> None:
if format_type != ExportFormat.COCO:
if format_type not in (ExportFormat.COCO, ExportFormat.YOLO):
return
dataset_type = self._normalize_type_value(
@@ -249,11 +249,11 @@ class AnnotationExportService:
if dataset_type == DATASET_TYPE_TEXT:
raise HTTPException(
status_code=400,
detail="导出格式 COCO 不支持文本类数据集(TEXT),请改用 JSON/JSONL/CSV 格式",
detail=f"导出格式 {format_type.value.upper()} 不支持文本类数据集(TEXT),请改用 JSON/JSONL/CSV 格式",
)
if (
dataset_type in COCO_COMPATIBLE_DATASET_TYPES
dataset_type in DETECTION_COMPATIBLE_DATASET_TYPES
or labeling_type == LABELING_TYPE_OBJECT_DETECTION
):
return
@@ -261,7 +261,7 @@ class AnnotationExportService:
raise HTTPException(
status_code=400,
detail=(
"导出格式 COCO 仅适用于图像类或目标检测类数据集,"
f"导出格式 {format_type.value.upper()} 仅适用于图像类或目标检测类数据集,"
f"当前数据集类型: {dataset_type or 'UNKNOWN'}"
f"标注类型: {labeling_type or 'UNKNOWN'}"
),

View File

@@ -32,7 +32,8 @@ class DatasetMappingService:
"""Build base query with dataset name joined"""
return select(
LabelingProject,
Dataset.name.label('dataset_name')
Dataset.name.label('dataset_name'),
Dataset.dataset_type.label('dataset_type'),
).outerjoin(
Dataset,
LabelingProject.dataset_id == Dataset.id
@@ -98,6 +99,7 @@ class DatasetMappingService:
"""
mapping = row[0] # LabelingProject object
dataset_name = row[1] # dataset_name from join
dataset_type = row[2] # dataset_type from join
# Get template_id from mapping
template_id = getattr(mapping, 'template_id', None)
@@ -134,6 +136,7 @@ class DatasetMappingService:
"id": mapping.id,
"dataset_id": mapping.dataset_id,
"dataset_name": dataset_name,
"dataset_type": dataset_type,
"labeling_project_id": mapping.labeling_project_id,
"name": mapping.name,
"description": description,
@@ -166,12 +169,15 @@ class DatasetMappingService:
"""
# Fetch dataset name
dataset_name = None
dataset_type = None
dataset_id = getattr(mapping, 'dataset_id', None)
if dataset_id:
dataset_result = await self.db.execute(
select(Dataset.name).where(Dataset.id == dataset_id)
select(Dataset.name, Dataset.dataset_type).where(Dataset.id == dataset_id)
)
dataset_name = dataset_result.scalar_one_or_none()
dataset_row = dataset_result.one_or_none()
if dataset_row:
dataset_name, dataset_type = dataset_row
# Get template_id from mapping
template_id = getattr(mapping, 'template_id', None)
@@ -211,6 +217,7 @@ class DatasetMappingService:
"id": mapping.id,
"dataset_id": dataset_id,
"dataset_name": dataset_name,
"dataset_type": dataset_type,
"labeling_project_id": mapping.labeling_project_id,
"name": mapping.name,
"description": description,