From cd5f5ef6da6f02f588db1d0e28f9df35ea4e1162 Mon Sep 17 00:00:00 2001 From: Jerry Yan <792602257@qq.com> Date: Fri, 6 Feb 2026 15:22:57 +0800 Subject: [PATCH] fix(annotation): fix use_new_version to support files without annotation Problem: use_new_version returned 404 annotation not found for files without annotation, preventing users from switching to new versions. Solution: 1. Query latest file by logical_path 2. Update LabelingProjectFile to point to latest version 3. If annotation exists: clear it and update file_id 4. If no annotation: just update project file snapshot 5. Return new file_id in response --- .../app/module/annotation/service/editor.py | 162 +++++++++++------- 1 file changed, 103 insertions(+), 59 deletions(-) diff --git a/runtime/datamate-python/app/module/annotation/service/editor.py b/runtime/datamate-python/app/module/annotation/service/editor.py index 7160f77..f075088 100644 --- a/runtime/datamate-python/app/module/annotation/service/editor.py +++ b/runtime/datamate-python/app/module/annotation/service/editor.py @@ -1723,16 +1723,19 @@ class AnnotationEditorService: """ 使用文件新版本并清空标注 + 如果文件有多个版本(通过 logical_path 关联),将标注切换到最新版本。 + 如果存在标注记录,会清空标注内容。 + Args: project_id: 标注项目ID - file_id: 文件ID + file_id: 文件ID(当前关联的文件ID) Returns: 操作结果 """ project = await self._get_project_or_404(project_id) - # 获取文件信息 + # 获取当前文件信息 file_result = await self.db.execute( select(DatasetFiles) .join(LabelingProjectFile, LabelingProjectFile.file_id == DatasetFiles.id) @@ -1748,7 +1751,46 @@ class AnnotationEditorService: status_code=404, detail=f"文件不存在或不属于该项目: {file_id}" ) - # 获取标注信息 + # 获取同一逻辑路径下的最新版本文件 + logical_path = file_record.logical_path + latest_file_result = await self.db.execute( + select(DatasetFiles) + .where( + DatasetFiles.dataset_id == project.dataset_id, + DatasetFiles.logical_path == logical_path, + DatasetFiles.status == "ACTIVE", + ) + .order_by(DatasetFiles.version.desc()) + .limit(1) + ) + latest_file = latest_file_result.scalar_one_or_none() + + if not latest_file: + raise HTTPException( + status_code=404, detail=f"找不到该文件的最新版本: {logical_path}" + ) + + # 如果当前文件已经是最新版本,无需更新 + if latest_file.id == file_id: + return { + "fileId": file_id, + "previousFileVersion": file_record.version, + "currentFileVersion": latest_file.version, + "message": "当前文件已是最新版本,无需切换", + } + + # 更新 LabelingProjectFile 指向最新版本 + project_file_result = await self.db.execute( + select(LabelingProjectFile).where( + LabelingProjectFile.project_id == project.id, + LabelingProjectFile.file_id == file_id, + ) + ) + project_file = project_file_result.scalar_one_or_none() + if project_file: + project_file.file_id = str(latest_file.id) + + # 获取标注信息(基于原 file_id) annotation_result = await self.db.execute( select(AnnotationResult) .where( @@ -1759,64 +1801,66 @@ class AnnotationEditorService: ) annotation = annotation_result.scalar_one_or_none() - current_file_version = file_record.version - - if not annotation: - raise HTTPException(status_code=404, detail=f"标注不存在: {file_id}") - - previous_file_version = annotation.file_version - - if annotation.file_version is not None: - if current_file_version <= annotation.file_version: - raise HTTPException( - status_code=400, - detail=f"文件版本({current_file_version})未更新或低于标注版本({annotation.file_version})", - ) - - # 清空标注并更新版本号 now = datetime.utcnow() - cleared_payload: Dict[str, Any] = {} - if isinstance(annotation.annotation, dict) and self._is_segmented_annotation( - annotation.annotation - ): - segments = self._extract_segment_annotations(annotation.annotation) - cleared_segments: Dict[str, Dict[str, Any]] = {} - for segment_id, segment_data in segments.items(): - if not isinstance(segment_data, dict): - continue - normalized = dict(segment_data) - normalized[SEGMENT_RESULT_KEY] = [] - cleared_segments[str(segment_id)] = normalized - total_segments = self._resolve_segment_total(annotation.annotation) - if total_segments is None: - total_segments = len(cleared_segments) + if annotation: + # 存在标注记录:清空标注并更新文件版本 + previous_file_version = annotation.file_version - cleared_payload = { - SEGMENTED_KEY: True, - "version": annotation.annotation.get("version", 1), - SEGMENTS_KEY: cleared_segments, - SEGMENT_TOTAL_KEY: total_segments, + cleared_payload: Dict[str, Any] = {} + if isinstance(annotation.annotation, dict) and self._is_segmented_annotation( + annotation.annotation + ): + segments = self._extract_segment_annotations(annotation.annotation) + cleared_segments: Dict[str, Dict[str, Any]] = {} + for segment_id, segment_data in segments.items(): + if not isinstance(segment_data, dict): + continue + normalized = dict(segment_data) + normalized[SEGMENT_RESULT_KEY] = [] + cleared_segments[str(segment_id)] = normalized + + total_segments = self._resolve_segment_total(annotation.annotation) + if total_segments is None: + total_segments = len(cleared_segments) + + cleared_payload = { + SEGMENTED_KEY: True, + "version": annotation.annotation.get("version", 1), + SEGMENTS_KEY: cleared_segments, + SEGMENT_TOTAL_KEY: total_segments, + } + + # 更新标注记录指向新文件 + annotation.file_id = str(latest_file.id) + annotation.annotation = cleared_payload + annotation.annotation_status = ANNOTATION_STATUS_NO_ANNOTATION + annotation.file_version = latest_file.version + annotation.updated_at = now + + await self.db.commit() + await self.db.refresh(annotation) + + await self._sync_annotation_to_knowledge( + project, + latest_file, + cleared_payload, + annotation.updated_at or now, + ) + + return { + "fileId": str(latest_file.id), + "previousFileVersion": previous_file_version, + "currentFileVersion": latest_file.version, + "message": "已切换到新版本并清空标注", } + else: + # 不存在标注记录:只需更新项目文件快照 + await self.db.commit() - annotation.annotation = cleared_payload - annotation.annotation_status = ANNOTATION_STATUS_NO_ANNOTATION - annotation.file_version = current_file_version - annotation.updated_at = now - - await self.db.commit() - await self.db.refresh(annotation) - - await self._sync_annotation_to_knowledge( - project, - file_record, - cleared_payload, - annotation.updated_at or now, - ) - - return { - "fileId": file_id, - "previousFileVersion": previous_file_version, - "currentFileVersion": current_file_version, - "message": "已使用新版本并清空标注", - } + return { + "fileId": str(latest_file.id), + "previousFileVersion": file_record.version, + "currentFileVersion": latest_file.version, + "message": "已切换到新版本", + }