Task: in_loop, at_close, fields, tipo check, task_results, close_task_ids, rate limit login-pin (#53 #54 #55 #59)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-07 12:42:13 +02:00
parent e07c4d4eee
commit df1727be73
14 changed files with 493 additions and 5 deletions
+34
View File
@@ -17,6 +17,9 @@ from src.backend.models.api.task import (
TaskReorderRequest,
TaskResponse,
TaskUpdate,
check_task_loop_options,
default_in_loop,
normalize_note_fields,
validate_tolerance_order,
)
from src.backend.models.orm.vision import VisionReferenceImage
@@ -202,6 +205,13 @@ async def create_task(
This creates a NEW version via copy-on-write, then appends the task to
the new version. Requires Maker role.
"""
# #53 #55: validazione di in_loop/at_close/fields prima di toccare il db
try:
fields = normalize_note_fields(data.fields)
check_task_loop_options(data.task_type, data.at_close, fields)
except ValueError as exc:
raise HTTPException(status_code=422, detail=str(exc))
# Ensure recipe exists and is active
result = await db.execute(select(Recipe).where(Recipe.id == recipe_id))
recipe = result.scalar_one_or_none()
@@ -240,6 +250,10 @@ async def create_task(
file_type=data.file_type,
annotations_json=data.annotations_json,
vision_json=data.vision_json,
# #53 #55: ciclo (default dal tipo), chiusura, campi extra
in_loop=data.in_loop if data.in_loop is not None else default_in_loop(data.task_type),
at_close=data.at_close,
fields=fields,
)
db.add(new_task)
await db.flush()
@@ -341,6 +355,26 @@ async def update_task(
if not update_data:
return TaskResponse.model_validate(task)
# #53 #55: i tre campi nuovi si validano sulla combinazione finale (valori
# nuovi, o quelli gia' sul task per cio' che il client non manda)
if "fields" in update_data:
try:
update_data["fields"] = normalize_note_fields(update_data["fields"])
except ValueError as exc:
raise HTTPException(status_code=422, detail=str(exc))
for flag in ("in_loop", "at_close"):
if flag in update_data and update_data[flag] is None:
del update_data[flag] # i booleani non sono nullable: null = non toccare
final_type = update_data.get("task_type") or task.task_type
try:
check_task_loop_options(
final_type,
update_data.get("at_close", task.at_close),
update_data.get("fields", task.fields),
)
except ValueError as exc:
raise HTTPException(status_code=422, detail=str(exc))
task = await _task_for_write(db, task, user)
for field, value in update_data.items():