feat(annotation): implement file version management for annotation feature

Add support for detecting new file versions and switching to them:

Backend Changes:
- Add file_version column to AnnotationResult model
- Create Alembic migration for database schema update
- Implement check_file_version() method to compare annotation and file versions
- Implement use_new_version() method to clear annotations and update version
- Update upsert_annotation() to record file version when saving
- Add new API endpoints: GET /version and POST /use-new-version
- Add FileVersionCheckResponse and UseNewVersionResponse schemas

Frontend Changes:
- Add checkFileVersionUsingGet and useNewVersionUsingPost API calls
- Add version warning banner showing current vs latest file version
- Add 'Use New Version' button with confirmation dialog
- Clear version info state when switching files to avoid stale warnings

Bug Fixes:
- Fix previousFileVersion returning updated value (save before update)
- Handle null file_version for historical data compatibility
- Fix segmented annotation clearing (preserve structure, clear results)
- Fix files without annotations incorrectly showing new version warnings
- Preserve total_segments when clearing segmented annotations

Files Modified:
- frontend/src/pages/DataAnnotation/Annotate/LabelStudioTextEditor.tsx
- frontend/src/pages/DataAnnotation/annotation.api.ts
- runtime/datamate-python/app/db/models/annotation_management.py
- runtime/datamate-python/app/module/annotation/interface/editor.py
- runtime/datamate-python/app/module/annotation/schema/editor.py
- runtime/datamate-python/app/module/annotation/service/editor.py

New Files:
- runtime/datamate-python/alembic.ini
- runtime/datamate-python/alembic/env.py
- runtime/datamate-python/alembic/script.py.mako
- runtime/datamate-python/alembic/versions/20250205_0001_add_file_version.py
This commit is contained in:
2026-02-05 20:12:07 +08:00
parent 4143bc75f9
commit f5cb265667
10 changed files with 915 additions and 171 deletions

View File

@@ -21,6 +21,8 @@ from app.module.annotation.schema.editor import (
EditorTaskListResponse,
EditorTaskSegmentResponse,
EditorTaskResponse,
FileVersionCheckResponse,
UseNewVersionResponse,
UpsertAnnotationRequest,
UpsertAnnotationResponse,
)
@@ -80,7 +82,9 @@ async def list_editor_tasks(
async def get_editor_task(
project_id: str = Path(..., description="标注项目ID(t_dm_labeling_projects.id)"),
file_id: str = Path(..., description="文件ID(t_dm_dataset_files.id)"),
segment_index: Optional[int] = Query(None, alias="segmentIndex", description="段落索引(分段模式下使用)"),
segment_index: Optional[int] = Query(
None, alias="segmentIndex", description="段落索引(分段模式下使用)"
),
db: AsyncSession = Depends(get_db),
):
service = AnnotationEditorService(db)
@@ -95,7 +99,9 @@ async def get_editor_task(
async def get_editor_task_segment(
project_id: str = Path(..., description="标注项目ID(t_dm_labeling_projects.id)"),
file_id: str = Path(..., description="文件ID(t_dm_dataset_files.id)"),
segment_index: int = Query(..., ge=0, alias="segmentIndex", description="段落索引(从0开始)"),
segment_index: int = Query(
..., ge=0, alias="segmentIndex", description="段落索引(从0开始)"
),
db: AsyncSession = Depends(get_db),
):
service = AnnotationEditorService(db)
@@ -117,3 +123,36 @@ async def upsert_editor_annotation(
result = await service.upsert_annotation(project_id, file_id, request)
return StandardResponse(code=200, message="success", data=result)
@router.get(
"/projects/{project_id}/files/{file_id}/version",
response_model=StandardResponse[FileVersionCheckResponse],
)
async def check_file_version(
project_id: str = Path(..., description="标注项目ID(t_dm_labeling_projects.id)"),
file_id: str = Path(..., description="文件ID(t_dm_dataset_files.id)"),
db: AsyncSession = Depends(get_db),
):
"""
检查文件是否有新版本
"""
service = AnnotationEditorService(db)
result = await service.check_file_version(project_id, file_id)
return StandardResponse(code=200, message="success", data=result)
@router.post(
"/projects/{project_id}/files/{file_id}/use-new-version",
response_model=StandardResponse[UseNewVersionResponse],
)
async def use_new_version(
project_id: str = Path(..., description="标注项目ID(t_dm_labeling_projects.id)"),
file_id: str = Path(..., description="文件ID(t_dm_dataset_files.id)"),
db: AsyncSession = Depends(get_db),
):
"""
使用文件新版本并清空标注
"""
service = AnnotationEditorService(db)
result = await service.use_new_version(project_id, file_id)
return StandardResponse(code=200, message="success", data=result)