Files
DataMate/runtime/datamate-python/app/module/annotation/interface/template.py
Jason Wang 45743f39f5 feat: add labeling template. refactor: switch to Poetry, build and deploy of backend Python (#79)
* feat: Enhance annotation module with template management and validation

- Added DatasetMappingCreateRequest and DatasetMappingUpdateRequest schemas to handle dataset mapping requests with camelCase and snake_case support.
- Introduced Annotation Template schemas including CreateAnnotationTemplateRequest, UpdateAnnotationTemplateRequest, and AnnotationTemplateResponse for managing annotation templates.
- Implemented AnnotationTemplateService for creating, updating, retrieving, and deleting annotation templates, including validation of configurations and XML generation.
- Added utility class LabelStudioConfigValidator for validating Label Studio configurations and XML formats.
- Updated database schema for annotation templates and labeling projects to include new fields and constraints.
- Seeded initial annotation templates for various use cases including image classification, object detection, and text classification.

* feat: Enhance TemplateForm with improved validation and dynamic field rendering; update LabelStudio config validation for camelCase support

* feat: Update docker-compose.yml to mark datamate dataset volume and network as external

* feat: Add tag configuration management and related components

- Introduced new components for tag selection and browsing in the frontend.
- Added API endpoint to fetch tag configuration from the backend.
- Implemented tag configuration management in the backend, including loading from YAML.
- Enhanced template service to support dynamic tag rendering based on configuration.
- Updated validation utilities to incorporate tag configuration checks.
- Refactored existing code to utilize the new tag configuration structure.

* feat: Refactor LabelStudioTagConfig for improved configuration loading and validation

* feat: Update Makefile to include backend-python-docker-build in the build process

* feat: Migrate to poetry for better deps management

* Add pyyaml dependency and update Dockerfile to use Poetry for dependency management

- Added pyyaml (>=6.0.3,<7.0.0) to pyproject.toml dependencies.
- Updated Dockerfile to install Poetry and manage dependencies using it.
- Improved layer caching by copying only dependency files before the application code.
- Removed unnecessary installation of build dependencies to keep the final image size small.

* feat: Remove duplicated backend-python-docker-build target from Makefile

* fix: airflow is not ready for adding yet

* feat: update Python version to 3.12 and remove project installation step in Dockerfile
2025-11-13 15:32:30 +08:00

133 lines
4.1 KiB
Python

"""
Annotation Template API Endpoints
"""
from typing import Optional
from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.ext.asyncio import AsyncSession
from app.db.session import get_db
from app.module.shared.schema import StandardResponse
from app.module.annotation.schema import (
CreateAnnotationTemplateRequest,
UpdateAnnotationTemplateRequest,
AnnotationTemplateResponse,
AnnotationTemplateListResponse
)
from app.module.annotation.service.template import AnnotationTemplateService
router = APIRouter(prefix="/template", tags=["annotation/template"])
template_service = AnnotationTemplateService()
@router.post(
"",
response_model=StandardResponse[AnnotationTemplateResponse],
)
async def create_template(
request: CreateAnnotationTemplateRequest,
db: AsyncSession = Depends(get_db)
):
"""
创建新的标注模板
- **name**: 模板名称(必填,最多100字符)
- **description**: 模板描述(可选,最多500字符)
- **dataType**: 数据类型(必填)
- **labelingType**: 标注类型(必填)
- **configuration**: 标注配置(必填,包含labels和objects)
- **style**: 样式配置(默认horizontal)
- **category**: 模板分类(默认custom)
"""
template = await template_service.create_template(db, request)
return StandardResponse(code=200, message="success", data=template)
@router.get(
"/{template_id}",
response_model=StandardResponse[AnnotationTemplateResponse],
)
async def get_template(
template_id: str,
db: AsyncSession = Depends(get_db)
):
"""
根据ID获取模板详情
"""
template = await template_service.get_template(db, template_id)
if not template:
raise HTTPException(status_code=404, detail="Template not found")
return StandardResponse(code=200, message="success", data=template)
@router.get(
"",
response_model=StandardResponse[AnnotationTemplateListResponse],
)
async def list_template(
page: int = Query(1, ge=1, description="页码"),
size: int = Query(10, ge=1, le=100, description="每页大小"),
category: Optional[str] = Query(None, description="分类筛选"),
dataType: Optional[str] = Query(None, alias="dataType", description="数据类型筛选"),
labelingType: Optional[str] = Query(None, alias="labelingType", description="标注类型筛选"),
builtIn: Optional[bool] = Query(None, alias="builtIn", description="是否内置模板"),
db: AsyncSession = Depends(get_db)
):
"""
获取模板列表,支持分页和筛选
- **page**: 页码(从1开始)
- **size**: 每页大小(1-100)
- **category**: 模板分类筛选
- **dataType**: 数据类型筛选
- **labelingType**: 标注类型筛选
- **builtIn**: 是否只显示内置模板
"""
templates = await template_service.list_templates(
db=db,
page=page,
size=size,
category=category,
data_type=dataType,
labeling_type=labelingType,
built_in=builtIn
)
return StandardResponse(code=200, message="success", data=templates)
@router.put(
"/{template_id}",
response_model=StandardResponse[AnnotationTemplateResponse],
)
async def update_template(
template_id: str,
request: UpdateAnnotationTemplateRequest,
db: AsyncSession = Depends(get_db)
):
"""
更新模板信息
所有字段都是可选的,只更新提供的字段
"""
template = await template_service.update_template(db, template_id, request)
if not template:
raise HTTPException(status_code=404, detail="Template not found")
return StandardResponse(code=200, message="success", data=template)
@router.delete(
"/{template_id}",
response_model=StandardResponse[bool],
)
async def delete_template(
template_id: str,
db: AsyncSession = Depends(get_db)
):
"""
删除模板(软删除)
"""
success = await template_service.delete_template(db, template_id)
if not success:
raise HTTPException(status_code=404, detail="Template not found")
return StandardResponse(code=200, message="success", data=True)