Files
geointel/backend/app/schemas/detection.py
T
JensandClaude Opus 5 5b3839dc89 page detection and segmentation results instead of returning all of them
/detection/runs/{id}/detections and its GeoJSON sibling returned every
persisted detection, as did the segmentation equivalents. A regional run holds
tens of thousands, and these are the endpoints the results table and the map
overlay call after every run.

They now take limit and offset, default to 2.000, and report total, limit,
offset and truncated so the complete population stays visible while what is
transferred does not. The GeoJSON responses carry the same window in a
geointel_result_window foreign member.

Rows are ordered by confidence, so a capped overlay draws the strongest
detections rather than an arbitrary slice, and the lab says how many of how
many are being shown rather than silently presenting a page as the whole run.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-22 15:24:46 +02:00

191 lines
5.0 KiB
Python

from __future__ import annotations
from datetime import datetime
from uuid import UUID
from pydantic import BaseModel, ConfigDict, Field
class DetectionModelCapability(BaseModel):
model_config = ConfigDict(protected_namespaces=())
model_id: str
display_name: str
framework: str
task_type: str
supported_classes: list[str]
configured: bool
status: str
limitation_message: str
version: str | None = None
training_scope: str | None = None
validation_scope: str | None = None
validated_regions: list[str] = Field(default_factory=list)
nationally_validated: bool = False
operator_review_required: bool = True
class DetectionModelsResponse(BaseModel):
models: list[DetectionModelCapability]
class ModelAssetRead(BaseModel):
model_config = ConfigDict(protected_namespaces=())
model_asset_id: str
filename: str
display_name: str
model_path: str
suffix: str
framework: str
task_type: str
size_bytes: int
sha256: str
active: bool
status: str
limitation_message: str
will_download_models: bool = False
class ModelAssetListResponse(BaseModel):
model_config = ConfigDict(protected_namespaces=())
items: list[ModelAssetRead]
total: int
model_directory: str
class DetectionRunRequest(BaseModel):
model_config = ConfigDict(protected_namespaces=())
project_id: UUID
dataset_id: UUID
model_id: str
model_asset_id: str | None = None
confidence_threshold: float = Field(default=0.5, ge=0.0, le=1.0)
class_filter: list[str] | None = None
tile_manifest_path: str | None = None
parameters_json: dict = Field(default_factory=dict)
class DetectionQaRequest(BaseModel):
reference_dataset_id: UUID
iou_threshold: float = Field(default=0.5, ge=0.0, le=1.0)
class_name: str | None = None
min_confidence: float | None = Field(default=None, ge=0.0, le=1.0)
class DetectionRunResponse(BaseModel):
model_config = ConfigDict(protected_namespaces=())
analysis_run_id: UUID
job_id: UUID
project_id: UUID
dataset_id: UUID
model_id: str
status: str
detection_count: int
error_code: str | None = None
message: str
class DetectionRunRead(BaseModel):
model_config = ConfigDict(from_attributes=True, protected_namespaces=())
id: UUID
project_id: UUID
dataset_id: UUID | None = None
job_id: UUID | None = None
analysis_type: str
status: str
model_name: str | None = None
model_version: str | None = None
parameters_json: dict
result_json: dict | None = None
error_message: str | None = None
created_at: datetime | None = None
started_at: datetime | None = None
finished_at: datetime | None = None
class DetectionRunListResponse(BaseModel):
items: list[DetectionRunRead]
total: int
class DetectionRead(BaseModel):
model_config = ConfigDict(from_attributes=True, protected_namespaces=())
id: UUID
project_id: UUID
dataset_id: UUID | None = None
analysis_run_id: UUID | None = None
job_id: UUID | None = None
model_name: str
model_version: str | None = None
class_name: str
confidence: float
bbox_json: dict | None = None
source_tile_path: str | None = None
properties_json: dict | None = None
created_at: datetime | None = None
class DetectionListResponse(BaseModel):
items: list[DetectionRead]
# ``total`` is the complete population; ``items`` is one page of it.
total: int
limit: int | None = None
offset: int = 0
truncated: bool = False
class YoloPreflightChecks(BaseModel):
model_config = ConfigDict(protected_namespaces=())
enabled: bool
dependencies_available: bool | None = None
accelerator_ready: bool | None = None
model_path_set: bool | None = None
model_file_exists: bool | None = None
model_provenance_manifest_path: str | None = None
model_provenance_valid: bool | None = None
model_load_requested: bool
model_load_ok: bool | None = None
manifest_path_set: bool | None = None
manifest_valid: bool | None = None
tile_paths_exist: bool | None = None
tile_limit_ok: bool | None = None
class YoloRuntimeDetails(BaseModel):
model_config = ConfigDict(protected_namespaces=())
dependencies_assumed: bool
model_directory: str | None = None
yolo_config_dir: str | None = None
torch_version: str | None = None
ultralytics_version: str | None = None
cuda_available: bool | None = None
configured_device: str
cuda_required: bool
class YoloPreflightResponse(BaseModel):
model_config = ConfigDict(protected_namespaces=())
model_id: str
model_asset_id: str | None = None
model_path: str | None = None
tile_manifest_path: str | None = None
status: str
message: str
checks: YoloPreflightChecks
tile_count: int
max_tiles: int
will_download_models: bool
will_run_inference: bool
runtime: YoloRuntimeDetails
error_code: str | None = None
details: dict | None = None