Files
asus 5c7c122105 feat: add counting bench, triage, and dataset modules
This commit includes major additions and updates to the frontend and backend architectures, introducing new dataset management, live counting features, batch processing, and triage logic. Includes new UI pages, components, and API routes.
2026-08-14 16:28:52 +07:00

102 lines
3.1 KiB
Python

"""Named dataset routes: list, rename, delete, download (REQ-110…113)."""
import os
from typing import List, Optional
from fastapi import APIRouter, HTTPException
from fastapi.responses import FileResponse
from pydantic import BaseModel
from backend import dataset, datasets
from backend.api.common import project_or_404
router = APIRouter(tags=["datasets"])
class CreateRequest(BaseModel):
name: str = ""
note: str = ""
class RenameRequest(BaseModel):
name: Optional[str] = None
note: Optional[str] = None
class CombineRequest(BaseModel):
dataset_ids: List[int]
@router.get("/api/projects/{project_id}/datasets")
def list_datasets(project_id: int) -> dict:
project_or_404(project_id)
return {"datasets": datasets.listing(project_id)}
@router.post("/api/projects/{project_id}/datasets")
def create_dataset(project_id: int, body: CreateRequest) -> dict:
project_or_404(project_id)
return datasets.create(project_id, name=body.name, note=body.note)
@router.get("/api/datasets/{dataset_id}")
def get_dataset(dataset_id: int) -> dict:
found = datasets.get(dataset_id)
if found is None:
raise HTTPException(404, "No such dataset")
return found
@router.patch("/api/datasets/{dataset_id}")
def rename_dataset(dataset_id: int, body: RenameRequest) -> dict:
if datasets.get(dataset_id) is None:
raise HTTPException(404, "No such dataset")
return datasets.rename(dataset_id, name=body.name, note=body.note)
@router.delete("/api/datasets/{dataset_id}")
def delete_dataset(dataset_id: int) -> dict:
if not datasets.delete(dataset_id):
raise HTTPException(404, "No such dataset")
return {"deleted": True}
@router.post("/api/projects/{project_id}/datasets/combine-preview")
def combine_preview(project_id: int, body: CombineRequest) -> dict:
"""What a run over these datasets would actually see.
The totals of two datasets do not add up when they share frames, and being
handed 4,000 images after picking two datasets of 3,000 is the kind of
surprise that makes people distrust the numbers.
"""
project_or_404(project_id)
items = datasets.combined_items(project_id, body.dataset_ids)
report = datasets.overlap_report(project_id, body.dataset_ids, len(items))
report["splits"] = {
"train": sum(1 for item in items if item["split"] == "train"),
"val": sum(1 for item in items if item["split"] == "val"),
}
return report
@router.post("/api/datasets/{dataset_id}/resync")
def resync_dataset(dataset_id: int) -> dict:
try:
return dataset.resync(dataset_id)
except datasets.DatasetError as exc:
raise HTTPException(400, str(exc))
@router.get("/api/datasets/{dataset_id}/download")
def download_dataset(dataset_id: int):
found = datasets.get(dataset_id)
if found is None:
raise HTTPException(404, "No such dataset")
project = project_or_404(found["project_id"])
try:
path = dataset.zip_path(project, dataset_id)
except datasets.DatasetError as exc:
raise HTTPException(400, str(exc))
return FileResponse(path, media_type="application/zip",
filename=os.path.basename(path))