from datetime import datetime from typing import Optional, Any from uuid import UUID from pydantic import BaseModel, Field class CategoryInfo(BaseModel): """Embedded category info for tree responses.""" id: UUID name: str slug: str class Config: from_attributes = True class TreeBase(BaseModel): name: str = Field(..., min_length=1, max_length=255) description: Optional[str] = None # Legacy category field - kept for backward compatibility category: Optional[str] = Field(None, max_length=100) class TreeCreate(TreeBase): tree_structure: dict[str, Any] = Field(..., description="The decision tree structure in JSON format") is_public: bool = Field(False, description="Make tree visible to all users") is_default: bool = Field(False, description="Mark as a default/system tree (admin only)") category_id: Optional[UUID] = Field(None, description="Category ID from tree_categories table") tags: Optional[list[str]] = Field(None, max_length=10, description="List of tag names to assign") class TreeUpdate(BaseModel): name: Optional[str] = Field(None, min_length=1, max_length=255) description: Optional[str] = None category: Optional[str] = Field(None, max_length=100) category_id: Optional[UUID] = None tree_structure: Optional[dict[str, Any]] = None is_public: Optional[bool] = None is_active: Optional[bool] = None tags: Optional[list[str]] = Field(None, max_length=10, description="List of tag names to assign (replaces existing)") class ForkCreate(BaseModel): fork_reason: Optional[str] = Field(None, max_length=255, description="Brief reason for forking") name: Optional[str] = Field(None, min_length=1, max_length=255, description="Name for the fork (defaults to 'Fork of {original name}')") class ForkInfo(BaseModel): """Fork metadata included in tree responses.""" parent_tree_id: Optional[UUID] = None root_tree_id: Optional[UUID] = None fork_reason: Optional[str] = None fork_depth: int = 0 parent_updated_at: Optional[datetime] = None has_parent_updates: bool = False class Config: from_attributes = True class TreeResponse(TreeBase): id: UUID tree_structure: dict[str, Any] author_id: Optional[UUID] = None account_id: Optional[UUID] = None category_id: Optional[UUID] = None category_info: Optional[CategoryInfo] = None tags: list[str] = [] # List of tag names fork_info: Optional[ForkInfo] = None is_active: bool is_public: bool is_default: bool version: int created_at: datetime updated_at: datetime usage_count: int class Config: from_attributes = True class TreeListResponse(BaseModel): id: UUID name: str description: Optional[str] = None category: Optional[str] = None category_id: Optional[UUID] = None category_info: Optional[CategoryInfo] = None tags: list[str] = [] # List of tag names author_id: Optional[UUID] = None account_id: Optional[UUID] = None is_active: bool is_public: bool is_default: bool version: int usage_count: int created_at: datetime updated_at: datetime class Config: from_attributes = True