#4 - api/routers/tasks.py: update_task, delete_task, create_subtask, update_subtask, delete_subtask controllavano solo is_current, non version_has_measurements: la versione con misure veniva modificata sul posto, cambiando i limiti di quote gia' giudicate pass/fail. Nuovi helper _task_for_write / _subtask_for_write riusano recipe_service.create_new_version (lo stesso copy-on-write di create_task): se la versione ha misure creano una nuova versione, ritrovano il task per order_index e la quota per marker_number (chiavi che _copy_tasks_to_version preserva), e la scrittura va sulla copia. La versione misurata resta intatta. Trovato e corretto nello stesso punto: il task ritrovato nella copia non ha .version precaricato (new_version.tasks viene da selectinload, che non popola il backref figlio->genitore), e TaskResponse.recipe_id legge task.version.recipe_id - senza assegnare esplicitamente candidate.version = new_version, update_task va in MissingGreenlet (lazy load fuori da un await) invece di rispondere 200. #26 - models/api/task.py: nuovo validate_tolerance_order (ltl<=lwl<=nominal<= uwl<=utl sui soli campi valorizzati), usato come model_validator su SubtaskCreate e richiamato dal router in update_subtask sui valori risultanti (esistenti + parziali) prima del flush, perche' SubtaskUpdate non ha un insieme fisso di campi da validare da solo. Test: src/backend/tests/test_tasks.py, TestCopyOnWriteOnEdit (5) e TestToleranceOrder (4); adattato test_update_subtask esistente (nominal 12.0 violava il nuovo ordine contro utl=10.5 invariato). PROVA: uv run --extra server --extra dev pytest src/backend/tests/test_tasks.py -q -> 16 passed Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
"""Pydantic schemas for RecipeTask and RecipeSubtask operations."""
|
||||
from typing import Any, Literal, Optional
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
from pydantic import BaseModel, ConfigDict, Field, model_validator
|
||||
|
||||
from src.backend.models.orm.task import TASK_TYPES
|
||||
|
||||
@@ -9,6 +9,31 @@ from src.backend.models.orm.task import TASK_TYPES
|
||||
TaskType = Literal[TASK_TYPES]
|
||||
|
||||
|
||||
def validate_tolerance_order(
|
||||
ltl: Optional[float],
|
||||
lwl: Optional[float],
|
||||
nominal: Optional[float],
|
||||
uwl: Optional[float],
|
||||
utl: Optional[float],
|
||||
) -> None:
|
||||
"""ltl <= lwl <= nominal <= uwl <= utl, checked only across the values set.
|
||||
|
||||
A one-sided tolerance - only utl, say - is not an error: what is wrong is
|
||||
limits that invert the sequence they do share, like utl landing below ltl.
|
||||
Shared by SubtaskCreate's own validator and by the router's partial-update
|
||||
check (SubtaskUpdate has no fixed set of fields to compare).
|
||||
"""
|
||||
ordered = [
|
||||
("ltl", ltl), ("lwl", lwl), ("nominal", nominal), ("uwl", uwl), ("utl", utl),
|
||||
]
|
||||
present = [(name, value) for name, value in ordered if value is not None]
|
||||
for (prev_name, prev), (name, value) in zip(present, present[1:]):
|
||||
if prev > value:
|
||||
raise ValueError(
|
||||
f"Tolerance limits out of order: {prev_name}={prev} > {name}={value}"
|
||||
)
|
||||
|
||||
|
||||
class SubtaskCreate(BaseModel):
|
||||
"""Schema for creating a subtask."""
|
||||
marker_number: int = Field(..., ge=1)
|
||||
@@ -23,6 +48,11 @@ class SubtaskCreate(BaseModel):
|
||||
image_path: Optional[str] = Field(None, max_length=500)
|
||||
vision_output: Optional[str] = None
|
||||
|
||||
@model_validator(mode="after")
|
||||
def _check_tolerance_order(self) -> "SubtaskCreate":
|
||||
validate_tolerance_order(self.ltl, self.lwl, self.nominal, self.uwl, self.utl)
|
||||
return self
|
||||
|
||||
|
||||
class SubtaskUpdate(BaseModel):
|
||||
"""Schema for updating a subtask."""
|
||||
|
||||
Reference in New Issue
Block a user