"""Pydantic request/response models for auth and personal inventory.""" from datetime import datetime from typing import Any, Literal from pydantic import BaseModel, ConfigDict, Field from app import config class RegisterRequest(BaseModel): username: str = Field(min_length=3, max_length=32) password: str = Field(min_length=8, max_length=128) class LoginRequest(BaseModel): username: str = Field(min_length=1, max_length=32) password: str = Field(min_length=1, max_length=128) class UserOut(BaseModel): model_config = ConfigDict(from_attributes=True) id: int username: str created_at: datetime class AuthResponse(BaseModel): user: UserOut class InventoryAddRequest(BaseModel): class_name: str = Field(min_length=1, max_length=255) quantity: int = Field(default=1, ge=1, le=config.MAX_QUANTITY) notes: str = Field(default="", max_length=config.MAX_NOTES_LENGTH) quality: int | None = Field(default=None, ge=1, le=config.MAX_QUALITY) class InventoryPatchRequest(BaseModel): quantity: int | None = Field(default=None, ge=1, le=config.MAX_QUANTITY) notes: str | None = Field(default=None, max_length=config.MAX_NOTES_LENGTH) visibility: Literal["private", "available"] | None = None # Explicit null clears the quality; omitting the field leaves it unchanged. quality: int | None = Field(default=None, ge=1, le=config.MAX_QUALITY) class InventoryEntryOut(BaseModel): class_name: str name: str category: str | None type: str | None size: int | None grade: Any | None = None manufacturer: dict | None quantity: int notes: str # Mining/refining quality (1-1000); only meaningful when # `quality_applicable` is true. None means the owner has not set it. quality: int | None = None quality_applicable: bool = False # Owner-chosen classification. Unrelated to `available` below. visibility: Literal["private", "available"] # False when the catalog no longer contains the referenced item (e.g. after # a dataset refresh). The row is preserved rather than silently deleted. available: bool created_at: datetime updated_at: datetime class InventoryPage(BaseModel): total: int page: int page_size: int total_pages: int items: list[InventoryEntryOut] class InventorySummary(BaseModel): items: int quantity: int categories: dict[str, int] unavailable: int class AvailableItemOut(BaseModel): class_name: str name: str category: str | None type: str | None size: int | None grade: Any | None = None manufacturer: dict | None quantity: int quality: int | None = None quality_applicable: bool = False # Public listing: only the owner's display name, never notes. username: str updated_at: datetime class AvailablePage(BaseModel): total: int page: int page_size: int total_pages: int items: list[AvailableItemOut] class InventoryAddResponse(BaseModel): entry: InventoryEntryOut created: bool class OrderCreateRequest(BaseModel): class_name: str = Field(min_length=1, max_length=255) quantity: int = Field(default=1, ge=1, le=config.MAX_QUANTITY) notes: str = Field(default="", max_length=config.MAX_NOTES_LENGTH) quality: int | None = Field(default=None, ge=1, le=config.MAX_QUALITY) class OrderPatchRequest(BaseModel): quantity: int | None = Field(default=None, ge=1, le=config.MAX_QUANTITY) notes: str | None = Field(default=None, max_length=config.MAX_NOTES_LENGTH) status: Literal["open", "fulfilled"] | None = None # Explicit null clears the quality; omitting the field leaves it unchanged. quality: int | None = Field(default=None, ge=1, le=config.MAX_QUALITY) class OrderOut(BaseModel): id: int class_name: str name: str category: str | None type: str | None size: int | None grade: Any | None = None manufacturer: dict | None quantity: int notes: str quality: int | None = None quality_applicable: bool = False # Public listing: the requesting user's display name. username: str status: Literal["open", "fulfilled"] # False when the catalog no longer contains the referenced item. available: bool created_at: datetime updated_at: datetime class OrderPage(BaseModel): total: int page: int page_size: int total_pages: int items: list[OrderOut]