Develop py update schema (#37)

* feature: implement endpoints with multi-level response models
* refactor: move `/health` and `/config` endpoints to system module, remove example from base schemas
* refactor: remove unused get_standard_response_model()
This commit is contained in:
Jason Wang
2025-10-30 16:24:37 +08:00
committed by GitHub
parent 155603b1ca
commit e0884ab048
15 changed files with 89 additions and 71 deletions

View File

@@ -1,11 +1,13 @@
from fastapi import APIRouter from fastapi import APIRouter
from .system.interface import router as system_router
from .annotation.interface import router as annotation_router from .annotation.interface import router as annotation_router
router = APIRouter( router = APIRouter(
prefix="/api" prefix="/api"
) )
router.include_router(system_router)
router.include_router(annotation_router) router.include_router(annotation_router)
__all__ = ["router"] __all__ = ["router"]

View File

@@ -108,8 +108,7 @@ async def create_mapping(
response_data = DatasetMappingCreateResponse( response_data = DatasetMappingCreateResponse(
id=mapping.id, id=mapping.id,
labeling_project_id=str(mapping.labeling_project_id), labeling_project_id=str(mapping.labeling_project_id),
labeling_project_name=mapping.name or project_name, labeling_project_name=mapping.name or project_name
message="Dataset mapping created successfully"
) )
return StandardResponse( return StandardResponse(
@@ -341,8 +340,7 @@ async def delete_mapping(
message="success", message="success",
data=DeleteDatasetResponse( data=DeleteDatasetResponse(
id=id, id=id,
status="success", status="success"
message=f"Successfully deleted mapping and Label Studio project '{labeling_project_name}'"
) )
) )

View File

@@ -1,24 +1,24 @@
from .mapping import ( from .mapping import (
DatasetMappingBase, _DatasetMappingBase,
DatasetMappingCreateRequest, DatasetMappingCreateRequest,
DatasetMappingCreateResponse, DatasetMappingCreateResponse,
DatasetMappingUpdateRequest, DatasetMappingUpdateRequest,
DatasetMappingResponse, DatasetMappingResponse,
DeleteDatasetResponse DeleteDatasetResponse,
) )
from .sync import ( from .sync import (
SyncDatasetRequest, SyncDatasetRequest,
SyncDatasetResponse SyncDatasetResponse,
) )
__all__ = [ __all__ = [
"DatasetMappingBase", "_DatasetMappingBase",
"DatasetMappingCreateRequest", "DatasetMappingCreateRequest",
"DatasetMappingCreateResponse", "DatasetMappingCreateResponse",
"DatasetMappingUpdateRequest", "DatasetMappingUpdateRequest",
"DatasetMappingResponse", "DatasetMappingResponse",
"SyncDatasetRequest", "SyncDatasetRequest",
"SyncDatasetResponse", "SyncDatasetResponse",
"DeleteDatasetResponse" "DeleteDatasetResponse",
] ]

View File

@@ -3,12 +3,13 @@ from typing import Optional
from datetime import datetime from datetime import datetime
from app.module.shared.schema import BaseResponseModel from app.module.shared.schema import BaseResponseModel
from app.module.shared.schema import StandardResponse
class DatasetMappingBase(BaseResponseModel): class _DatasetMappingBase(BaseResponseModel):
"""数据集映射 基础模型""" """数据集映射 基础模型"""
dataset_id: str = Field(..., description="源数据集ID") dataset_id: str = Field(..., description="源数据集ID")
class DatasetMappingCreateRequest(DatasetMappingBase): class DatasetMappingCreateRequest(_DatasetMappingBase):
"""数据集映射 创建 请求模型""" """数据集映射 创建 请求模型"""
pass pass
@@ -17,13 +18,12 @@ class DatasetMappingCreateResponse(BaseResponseModel):
id: str = Field(..., description="映射UUID") id: str = Field(..., description="映射UUID")
labeling_project_id: str = Field(..., description="Label Studio项目ID") labeling_project_id: str = Field(..., description="Label Studio项目ID")
labeling_project_name: str = Field(..., description="Label Studio项目名称") labeling_project_name: str = Field(..., description="Label Studio项目名称")
message: str = Field(..., description="响应消息")
class DatasetMappingUpdateRequest(BaseResponseModel): class DatasetMappingUpdateRequest(BaseResponseModel):
"""数据集映射 更新 请求模型""" """数据集映射 更新 请求模型"""
dataset_id: Optional[str] = Field(None, description="源数据集ID") dataset_id: Optional[str] = Field(None, description="源数据集ID")
class DatasetMappingResponse(DatasetMappingBase): class DatasetMappingResponse(_DatasetMappingBase):
"""数据集映射 查询 响应模型""" """数据集映射 查询 响应模型"""
id: str = Field(..., description="映射UUID") id: str = Field(..., description="映射UUID")
labeling_project_id: str = Field(..., description="标注项目ID") labeling_project_id: str = Field(..., description="标注项目ID")
@@ -38,5 +38,4 @@ class DatasetMappingResponse(DatasetMappingBase):
class DeleteDatasetResponse(BaseResponseModel): class DeleteDatasetResponse(BaseResponseModel):
"""删除数据集响应模型""" """删除数据集响应模型"""
id: str = Field(..., description="映射UUID") id: str = Field(..., description="映射UUID")
status: str = Field(..., description="删除状态") status: str = Field(..., description="删除状态")
message: str = Field(..., description="响应消息")

View File

@@ -1,8 +1,7 @@
from pydantic import Field from pydantic import Field
from typing import Optional
from datetime import datetime
from app.module.shared.schema import BaseResponseModel from app.module.shared.schema import BaseResponseModel
from app.module.shared.schema import StandardResponse
class SyncDatasetRequest(BaseResponseModel): class SyncDatasetRequest(BaseResponseModel):
@@ -17,3 +16,6 @@ class SyncDatasetResponse(BaseResponseModel):
synced_files: int = Field(..., description="已同步文件数量") synced_files: int = Field(..., description="已同步文件数量")
total_files: int = Field(0, description="总文件数量") total_files: int = Field(0, description="总文件数量")
message: str = Field(..., description="响应消息") message: str = Field(..., description="响应消息")
class SyncDatasetResponseStd(StandardResponse[SyncDatasetResponse]):
pass

View File

@@ -1,33 +0,0 @@
from fastapi import APIRouter
from typing import Dict, Any
from app.core.config import settings
from app.schemas import StandardResponse
router = APIRouter()
@router.get("/health", response_model=StandardResponse[Dict[str, Any]])
async def health_check():
"""健康检查端点"""
return StandardResponse(
code=200,
message="success",
data={
"status": "healthy",
"service": "Label Studio Adapter",
"version": settings.app_version
}
)
@router.get("/config", response_model=StandardResponse[Dict[str, Any]])
async def get_config():
"""获取配置信息"""
return StandardResponse(
code=200,
message="success",
data={
"app_name": settings.app_name,
"version": settings.app_version,
"label_studio_url": settings.label_studio_base_url,
"debug": settings.debug
}
)

View File

@@ -1,7 +1,7 @@
""" """
通用响应模型 通用响应模型
""" """
from typing import Generic, TypeVar, Optional, List from typing import Generic, TypeVar, Optional, List, Type
from pydantic import BaseModel, Field from pydantic import BaseModel, Field
# 定义泛型类型变量 # 定义泛型类型变量
@@ -29,18 +29,11 @@ class StandardResponse(BaseResponseModel, Generic[T]):
""" """
code: int = Field(..., description="HTTP状态码") code: int = Field(..., description="HTTP状态码")
message: str = Field(..., description="响应消息") message: str = Field(..., description="响应消息")
data: Optional[T] = Field(None, description="响应数据") data: T = Field(..., description="响应数据")
class Config: class Config:
populate_by_name = True populate_by_name = True
alias_generator = to_camel alias_generator = to_camel
json_schema_extra = {
"example": {
"code": 200,
"message": "success",
"data": {}
}
}
class PaginatedData(BaseResponseModel, Generic[T]): class PaginatedData(BaseResponseModel, Generic[T]):
"""分页数据容器""" """分页数据容器"""
@@ -49,14 +42,3 @@ class PaginatedData(BaseResponseModel, Generic[T]):
total_elements: int = Field(..., description="总条数") total_elements: int = Field(..., description="总条数")
total_pages: int = Field(..., description="总页数") total_pages: int = Field(..., description="总页数")
content: List[T] = Field(..., description="当前页数据") content: List[T] = Field(..., description="当前页数据")
class Config:
json_schema_extra = {
"example": {
"page": 1,
"size": 20,
"totalElements": 100,
"totalPages": 5,
"content": []
}
}

View File

@@ -0,0 +1,7 @@
from fastapi import APIRouter
from .about import router as about_router
router = APIRouter()
router.include_router(about_router)

View File

@@ -0,0 +1,36 @@
from fastapi import APIRouter
from typing import Dict, Any
from app.core.config import settings
from app.module.shared.schema import StandardResponse
from ..schema import ConfigResponse, HealthResponse
router = APIRouter()
@router.get("/health", response_model=StandardResponse[HealthResponse])
async def health_check():
"""健康检查端点"""
return StandardResponse(
code=200,
message="success",
data=HealthResponse(
status="healthy",
service="Label Studio Adapter",
version=settings.app_version
)
)
@router.get("/config", response_model=StandardResponse[ConfigResponse])
async def get_config():
"""获取配置信息"""
return StandardResponse(
code=200,
message="success",
data=ConfigResponse(
app_name=settings.app_name,
version=settings.app_version,
label_studio_url=settings.label_studio_base_url,
debug=settings.debug
)
)

View File

@@ -0,0 +1,4 @@
from .config import ConfigResponse
from .health import HealthResponse
__all__ = ["ConfigResponse", "HealthResponse"]

View File

@@ -0,0 +1,11 @@
from pydantic import Field
from app.module.shared.schema import BaseResponseModel
from app.module.shared.schema import StandardResponse
class ConfigResponse(BaseResponseModel):
"""配置信息响应模型"""
app_name: str = Field(..., description="应用名称")
version: str = Field(..., description="应用版本")
label_studio_url: str = Field(..., description="Label Studio基础URL")
debug: bool = Field(..., description="调试模式状态")

View File

@@ -0,0 +1,10 @@
from pydantic import Field
from app.module.shared.schema import BaseResponseModel
from app.module.shared.schema import StandardResponse
class HealthResponse(BaseResponseModel):
"""健康检查响应模型"""
status: str = Field(..., description="服务状态")
service: str = Field(..., description="服务名称")
version: str = Field(..., description="应用版本")