backend: camera e modo di analisi nel task, /vision/check senza salvataggio (Refs #97)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -2,8 +2,9 @@
|
||||
import re
|
||||
from typing import Any, Literal, Optional
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field, model_validator
|
||||
from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator
|
||||
|
||||
from src.backend.models.api.station import _normalize_http_url
|
||||
from src.backend.models.orm.task import DEFAULT_LOOP_TASK_TYPES, TASK_TYPES
|
||||
|
||||
# Kept in step with the ORM enum so the two cannot drift apart.
|
||||
@@ -159,6 +160,30 @@ class SubtaskResponse(BaseModel):
|
||||
vision_output: Optional[str] = None
|
||||
|
||||
|
||||
class AcquisitionConfig(BaseModel):
|
||||
"""#97: camera (servizio + sorgente) e modo di analisi scelti nel task."""
|
||||
camera_url: str = Field(..., max_length=500)
|
||||
camera_source: str = Field(..., min_length=1, max_length=200)
|
||||
modo: Literal["impulsiva", "in_linea"] = "impulsiva"
|
||||
|
||||
@field_validator("camera_url")
|
||||
@classmethod
|
||||
def _check_camera_url(cls, value: str) -> str:
|
||||
# stessa normalizzazione delle stazioni; qui il vuoto non e' ammesso
|
||||
normalized = _normalize_http_url(value, "camera_url")
|
||||
if normalized is None:
|
||||
raise ValueError("camera_url must not be empty")
|
||||
return normalized
|
||||
|
||||
@field_validator("camera_source")
|
||||
@classmethod
|
||||
def _check_camera_source(cls, value: str) -> str:
|
||||
value = value.strip()
|
||||
if not value:
|
||||
raise ValueError("camera_source must not be empty")
|
||||
return value
|
||||
|
||||
|
||||
class TaskCreate(BaseModel):
|
||||
"""Schema for creating a task."""
|
||||
title: str = Field(..., min_length=1, max_length=255)
|
||||
@@ -169,6 +194,7 @@ class TaskCreate(BaseModel):
|
||||
file_type: Optional[str] = Field(None, pattern="^(image|pdf)$")
|
||||
annotations_json: Optional[dict[str, Any]] = None
|
||||
vision_json: Optional[dict] = None
|
||||
acquisition_json: Optional[AcquisitionConfig] = None # #97
|
||||
# #53: assente = default dal tipo (misure e check nel ciclo)
|
||||
in_loop: Optional[bool] = None
|
||||
at_close: bool = False
|
||||
@@ -186,6 +212,7 @@ class TaskUpdate(BaseModel):
|
||||
file_type: Optional[str] = Field(None, pattern="^(image|pdf)$")
|
||||
annotations_json: Optional[dict[str, Any]] = None
|
||||
vision_json: Optional[dict] = None
|
||||
acquisition_json: Optional[AcquisitionConfig] = None # #97
|
||||
in_loop: Optional[bool] = None
|
||||
at_close: Optional[bool] = None
|
||||
fields: Optional[list[dict[str, Any]]] = None
|
||||
@@ -207,6 +234,7 @@ class TaskResponse(BaseModel):
|
||||
file_type: Optional[str] = None
|
||||
annotations_json: Optional[dict[str, Any]] = None
|
||||
vision_json: Optional[dict] = None
|
||||
acquisition_json: Optional[AcquisitionConfig] = None # #97
|
||||
in_loop: bool = False
|
||||
at_close: bool = False
|
||||
fields: Optional[list[dict[str, Any]]] = None
|
||||
|
||||
@@ -24,6 +24,16 @@ class VisionExecuteResponse(BaseModel):
|
||||
failures: list[dict] = []
|
||||
|
||||
|
||||
class VisionCheckResponse(BaseModel):
|
||||
"""What `POST /api/vision/check` returns (#97): the analysis, nothing saved."""
|
||||
outcome: str | None = None
|
||||
overlay: list[dict] = []
|
||||
failures: list[dict] = []
|
||||
measurements: list[VisionMeasurementResult] = []
|
||||
# esito della lettura della calibrazione: ok | no_camera | not_calibrated | unreachable
|
||||
calibration: str
|
||||
|
||||
|
||||
class VisionRecipeOutput(BaseModel):
|
||||
"""One numeric output a recipe offers to the quotes."""
|
||||
key: str
|
||||
|
||||
Reference in New Issue
Block a user