Files
tfm_ainventory/backend/schemas/items.py
Daniel Bedeleanu 4f63b3b99e feat: integrate auto-photo-save into item creation endpoint
- Extend ItemCreate schema with optional extracted_image_bytes (base64) and image_processing (dict)
- Update create_item endpoint to call _auto_save_photo_from_extraction after item creation
- Decode base64 image bytes and pass crop_bounds, rotation_degrees to helper
- Don't block item creation if photo save fails (log warning instead)
- Item returned with photo_path, photo_thumbnail_path populated if save succeeded
- Full backward compatibility: old clients without image fields work unchanged
- Add 5 integration tests covering all scenarios:
  - Create item WITH image_processing → photo auto-saved
  - Create item WITHOUT image_processing → no photo (backward compatible)
  - Create item WITH invalid image_processing → item created, photo skipped
  - Create item WITH crop_bounds=None → item created, photo skipped
  - Create item WITH bytes but NO processing metadata → item created, photo skipped
- All 158 backend tests passing, zero regressions
2026-04-21 19:00:06 +03:00

90 lines
2.1 KiB
Python

from pydantic import BaseModel, field_serializer
from typing import Optional, Dict, Any
from datetime import datetime
# --- Photo Response ---
class PhotoResponse(BaseModel):
"""Photo metadata for item responses."""
thumbnail_url: str
full_url: str
uploaded_at: datetime
# --- Categories ---
class CategoryBase(BaseModel):
name: str
description: Optional[str] = None
class CategoryCreate(CategoryBase):
pass
class Category(CategoryBase):
id: int
class Config:
from_attributes = True
# --- Colors ---
class ColorBase(BaseModel):
name: str
class ColorCreate(ColorBase):
pass
class Color(ColorBase):
id: int
class Config:
from_attributes = True
# --- Items ---
class ItemBase(BaseModel):
name: str
category: str
category_id: Optional[int] = None
type: Optional[str] = None
barcode: str
part_number: Optional[str] = None
color: Optional[str] = None
description: Optional[str] = None
connector: Optional[str] = None
size: Optional[str] = None
ocr_text: Optional[str] = None
specs: Optional[str] = None
quantity: float = 0.0
min_quantity: float = 1.0
image_url: Optional[str] = None
box_label: Optional[str] = None
labels_data: Optional[str] = None
photo_path: Optional[str] = None
photo_thumbnail_path: Optional[str] = None
photo_upload_date: Optional[datetime] = None
class ItemCreate(ItemBase):
extracted_image_bytes: Optional[str] = None # Base64-encoded image data from AI extraction
image_processing: Optional[Dict[str, Any]] = None # {crop_bounds, rotation_degrees, confidence} from AI
class Item(ItemBase):
id: int
photo_path: Optional[str] = None
photo_thumbnail_path: Optional[str] = None
photo_upload_date: Optional[datetime] = None
photo: Optional[PhotoResponse] = None
class Config:
from_attributes = True
@field_serializer('photo_upload_date', when_used='json')
def serialize_photo_upload_date(self, value: Optional[datetime]) -> Optional[str]:
"""Serialize datetime to ISO format string for JSON."""
return value.isoformat() if value else None