You've already forked DataMate
feat(annotation): 添加 labelConfig 字段并优化配置解析逻辑
- 在 DatasetMappingResponse 模型中新增 label_config 字段 - 修改前端获取 labelConfig 的逻辑,优先使用任务自身配置 - 移除模板配置的 condition 分支,统一从 XML 解析配置 - 更新后端服务从 configuration JSON 字段中提取 label_config 和 description - 优化前后端配置解析的一致性处理
This commit is contained in:
@@ -121,24 +121,13 @@ export default function CreateAnnotationTask({
|
|||||||
});
|
});
|
||||||
setSelectedDatasetId(taskDetail.datasetId);
|
setSelectedDatasetId(taskDetail.datasetId);
|
||||||
|
|
||||||
// 配置可能在 template.configuration 或 taskDetail.configuration
|
// 获取实际的 labelConfig(优先使用任务自身的配置,回退到模板配置)
|
||||||
const configuration = taskDetail.template?.configuration || taskDetail.configuration;
|
const labelConfig = taskDetail.labelConfig || taskDetail.template?.labelConfig;
|
||||||
const labelConfig = taskDetail.template?.labelConfig || taskDetail.labelConfig;
|
|
||||||
|
|
||||||
// 设置 XML 配置用于预览
|
// 设置 XML 配置用于预览
|
||||||
if (labelConfig) {
|
if (labelConfig) {
|
||||||
setCustomXml(labelConfig);
|
setCustomXml(labelConfig);
|
||||||
}
|
// 始终从 XML 解析配置,确保数据一致性
|
||||||
|
|
||||||
// 填充模板配置:优先使用 configuration,否则从 labelConfig 解析
|
|
||||||
if (configuration && configuration.objects?.length > 0) {
|
|
||||||
const { objects, labels } = configuration;
|
|
||||||
manualForm.setFieldsValue({
|
|
||||||
objects: objects || [],
|
|
||||||
labels: labels || [],
|
|
||||||
});
|
|
||||||
} else if (labelConfig) {
|
|
||||||
// 从 XML 解析配置
|
|
||||||
const parsed = parseXmlToConfig(labelConfig);
|
const parsed = parseXmlToConfig(labelConfig);
|
||||||
manualForm.setFieldsValue({
|
manualForm.setFieldsValue({
|
||||||
objects: parsed.objects,
|
objects: parsed.objects,
|
||||||
|
|||||||
@@ -48,10 +48,11 @@ class DatasetMappingResponse(BaseModel):
|
|||||||
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")
|
||||||
template: Optional['AnnotationTemplateResponse'] = Field(None, description="关联的标注模板详情")
|
template: Optional['AnnotationTemplateResponse'] = Field(None, description="关联的标注模板详情")
|
||||||
|
label_config: Optional[str] = Field(None, alias="labelConfig", description="实际使用的 Label Studio XML 配置")
|
||||||
created_at: datetime = Field(..., alias="createdAt", description="创建时间")
|
created_at: datetime = Field(..., alias="createdAt", description="创建时间")
|
||||||
updated_at: Optional[datetime] = Field(None, alias="updatedAt", description="更新时间")
|
updated_at: Optional[datetime] = Field(None, alias="updatedAt", description="更新时间")
|
||||||
deleted_at: Optional[datetime] = Field(None, alias="deletedAt", description="删除时间")
|
deleted_at: Optional[datetime] = Field(None, alias="deletedAt", description="删除时间")
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
from_attributes = True
|
from_attributes = True
|
||||||
populate_by_name = True
|
populate_by_name = True
|
||||||
|
|||||||
@@ -35,23 +35,28 @@ class DatasetMappingService:
|
|||||||
)
|
)
|
||||||
|
|
||||||
async def _to_response_from_row(
|
async def _to_response_from_row(
|
||||||
self,
|
self,
|
||||||
row,
|
row,
|
||||||
include_template: bool = False
|
include_template: bool = False
|
||||||
) -> DatasetMappingResponse:
|
) -> DatasetMappingResponse:
|
||||||
"""
|
"""
|
||||||
Convert query row (mapping + dataset_name) to response
|
Convert query row (mapping + dataset_name) to response
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
row: Query result row containing (LabelingProject, dataset_name)
|
row: Query result row containing (LabelingProject, dataset_name)
|
||||||
include_template: If True, fetch and include full template details
|
include_template: If True, fetch and include full template details
|
||||||
"""
|
"""
|
||||||
mapping = row[0] # LabelingProject object
|
mapping = row[0] # LabelingProject object
|
||||||
dataset_name = row[1] # dataset_name from join
|
dataset_name = row[1] # dataset_name from join
|
||||||
|
|
||||||
# Get template_id from mapping
|
# Get template_id from mapping
|
||||||
template_id = getattr(mapping, 'template_id', None)
|
template_id = getattr(mapping, 'template_id', None)
|
||||||
|
|
||||||
|
# 从 configuration JSON 字段中提取 label_config 和 description
|
||||||
|
configuration = getattr(mapping, 'configuration', None) or {}
|
||||||
|
label_config = configuration.get('label_config') if isinstance(configuration, dict) else None
|
||||||
|
description = configuration.get('description') if isinstance(configuration, dict) else None
|
||||||
|
|
||||||
# Optionally fetch full template details
|
# Optionally fetch full template details
|
||||||
template_response = None
|
template_response = None
|
||||||
if include_template and template_id:
|
if include_template and template_id:
|
||||||
@@ -59,31 +64,32 @@ class DatasetMappingService:
|
|||||||
template_service = AnnotationTemplateService()
|
template_service = AnnotationTemplateService()
|
||||||
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}")
|
||||||
|
|
||||||
response_data = {
|
response_data = {
|
||||||
"id": mapping.id,
|
"id": mapping.id,
|
||||||
"dataset_id": mapping.dataset_id,
|
"dataset_id": mapping.dataset_id,
|
||||||
"dataset_name": dataset_name,
|
"dataset_name": dataset_name,
|
||||||
"labeling_project_id": mapping.labeling_project_id,
|
"labeling_project_id": mapping.labeling_project_id,
|
||||||
"name": mapping.name,
|
"name": mapping.name,
|
||||||
"description": getattr(mapping, 'description', None),
|
"description": description,
|
||||||
"template_id": template_id,
|
"template_id": template_id,
|
||||||
"template": template_response,
|
"template": template_response,
|
||||||
|
"label_config": label_config,
|
||||||
"created_at": mapping.created_at,
|
"created_at": mapping.created_at,
|
||||||
"updated_at": mapping.updated_at,
|
"updated_at": mapping.updated_at,
|
||||||
"deleted_at": mapping.deleted_at,
|
"deleted_at": mapping.deleted_at,
|
||||||
}
|
}
|
||||||
|
|
||||||
return DatasetMappingResponse(**response_data)
|
return DatasetMappingResponse(**response_data)
|
||||||
|
|
||||||
async def _to_response(
|
async def _to_response(
|
||||||
self,
|
self,
|
||||||
mapping: LabelingProject,
|
mapping: LabelingProject,
|
||||||
include_template: bool = False
|
include_template: bool = False
|
||||||
) -> DatasetMappingResponse:
|
) -> DatasetMappingResponse:
|
||||||
"""
|
"""
|
||||||
Convert ORM model to response with dataset name (for single entity operations)
|
Convert ORM model to response with dataset name (for single entity operations)
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
mapping: LabelingProject ORM object
|
mapping: LabelingProject ORM object
|
||||||
include_template: If True, fetch and include full template details
|
include_template: If True, fetch and include full template details
|
||||||
@@ -96,10 +102,15 @@ class DatasetMappingService:
|
|||||||
select(Dataset.name).where(Dataset.id == dataset_id)
|
select(Dataset.name).where(Dataset.id == dataset_id)
|
||||||
)
|
)
|
||||||
dataset_name = dataset_result.scalar_one_or_none()
|
dataset_name = dataset_result.scalar_one_or_none()
|
||||||
|
|
||||||
# Get template_id from mapping
|
# Get template_id from mapping
|
||||||
template_id = getattr(mapping, 'template_id', None)
|
template_id = getattr(mapping, 'template_id', None)
|
||||||
|
|
||||||
|
# 从 configuration JSON 字段中提取 label_config 和 description
|
||||||
|
configuration = getattr(mapping, 'configuration', None) or {}
|
||||||
|
label_config = configuration.get('label_config') if isinstance(configuration, dict) else None
|
||||||
|
description = configuration.get('description') if isinstance(configuration, dict) else None
|
||||||
|
|
||||||
# Optionally fetch full template details
|
# Optionally fetch full template details
|
||||||
template_response = None
|
template_response = None
|
||||||
if include_template and template_id:
|
if include_template and template_id:
|
||||||
@@ -107,7 +118,7 @@ class DatasetMappingService:
|
|||||||
template_service = AnnotationTemplateService()
|
template_service = AnnotationTemplateService()
|
||||||
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}")
|
||||||
|
|
||||||
# Create response dict with all fields
|
# Create response dict with all fields
|
||||||
response_data = {
|
response_data = {
|
||||||
"id": mapping.id,
|
"id": mapping.id,
|
||||||
@@ -115,14 +126,15 @@ class DatasetMappingService:
|
|||||||
"dataset_name": dataset_name,
|
"dataset_name": dataset_name,
|
||||||
"labeling_project_id": mapping.labeling_project_id,
|
"labeling_project_id": mapping.labeling_project_id,
|
||||||
"name": mapping.name,
|
"name": mapping.name,
|
||||||
"description": getattr(mapping, 'description', None),
|
"description": description,
|
||||||
"template_id": template_id,
|
"template_id": template_id,
|
||||||
"template": template_response,
|
"template": template_response,
|
||||||
|
"label_config": label_config,
|
||||||
"created_at": mapping.created_at,
|
"created_at": mapping.created_at,
|
||||||
"updated_at": mapping.updated_at,
|
"updated_at": mapping.updated_at,
|
||||||
"deleted_at": mapping.deleted_at,
|
"deleted_at": mapping.deleted_at,
|
||||||
}
|
}
|
||||||
|
|
||||||
return DatasetMappingResponse(**response_data)
|
return DatasetMappingResponse(**response_data)
|
||||||
|
|
||||||
async def create_mapping(
|
async def create_mapping(
|
||||||
|
|||||||
Reference in New Issue
Block a user