from pydantic import BaseModel, Field, validator from typing import List, Optional import re class CustomTaskStatus(BaseModel): """Schema for a custom task status""" id: str = Field(..., description="Unique identifier for the status") name: str = Field(..., min_length=1, max_length=50, description="Display name") color: str = Field(..., pattern=r'^#[0-9A-Fa-f]{6}$', description="Hex color code") order: int = Field(..., ge=0, description="Display order") is_default: bool = Field(default=False, description="Whether this is the default status") class Config: from_attributes = True class CustomTaskStatusCreate(BaseModel): """Schema for creating a new custom task status""" name: str = Field(..., min_length=1, max_length=50, description="Status name") color: Optional[str] = Field(None, description="Hex color code (e.g., #FF5733)") @validator('name') def validate_name(cls, v): """Validate and normalize status name""" # Trim whitespace v = v.strip() if not v: raise ValueError('Status name cannot be empty') if len(v) > 50: raise ValueError('Status name cannot exceed 50 characters') return v @validator('color') def validate_color(cls, v): """Validate color format if provided""" if v is None: return v # Trim whitespace v = v.strip() # Check hex color format if not re.match(r'^#[0-9A-Fa-f]{6}$', v): raise ValueError('Color must be a valid hex code (e.g., #FF5733)') return v.upper() # Normalize to uppercase class CustomTaskStatusUpdate(BaseModel): """Schema for updating a custom task status""" name: Optional[str] = Field(None, min_length=1, max_length=50, description="Status name") color: Optional[str] = Field(None, description="Hex color code (e.g., #FF5733)") is_default: Optional[bool] = Field(None, description="Set as default status") @validator('name') def validate_name(cls, v): """Validate and normalize status name""" if v is None: return v # Trim whitespace v = v.strip() if not v: raise ValueError('Status name cannot be empty') if len(v) > 50: raise ValueError('Status name cannot exceed 50 characters') return v @validator('color') def validate_color(cls, v): """Validate color format if provided""" if v is None: return v # Trim whitespace v = v.strip() # Check hex color format if not re.match(r'^#[0-9A-Fa-f]{6}$', v): raise ValueError('Color must be a valid hex code (e.g., #FF5733)') return v.upper() # Normalize to uppercase class CustomTaskStatusReorder(BaseModel): """Schema for reordering statuses""" status_ids: List[str] = Field(..., min_items=1, description="Ordered list of status IDs") @validator('status_ids') def validate_status_ids(cls, v): """Validate status IDs list""" if not v: raise ValueError('Status IDs list cannot be empty') # Check for duplicates if len(v) != len(set(v)): raise ValueError('Status IDs list contains duplicates') return v class CustomTaskStatusDelete(BaseModel): """Schema for deleting a status with optional reassignment""" reassign_to_status_id: Optional[str] = Field( None, description="Status ID to reassign tasks to (required if status is in use)" ) class SystemTaskStatus(BaseModel): """Schema for system (built-in) task statuses""" id: str = Field(..., description="System status identifier") name: str = Field(..., description="Display name") color: str = Field(..., description="Hex color code") is_system: bool = Field(default=True, description="Indicates this is a system status") class Config: from_attributes = True class AllTaskStatusesResponse(BaseModel): """Schema for response containing all task statuses (system + custom)""" statuses: List[CustomTaskStatus] = Field( default_factory=list, description="Custom task statuses defined for the project" ) system_statuses: List[SystemTaskStatus] = Field( default_factory=list, description="Built-in system task statuses" ) default_status_id: str = Field( ..., description="ID of the default status for new tasks" ) class Config: from_attributes = True class TaskStatusInUseError(BaseModel): """Schema for error when trying to delete a status that is in use""" error: str = Field(..., description="Error message") status_id: str = Field(..., description="ID of the status that cannot be deleted") status_name: str = Field(..., description="Name of the status that cannot be deleted") task_count: int = Field(..., ge=0, description="Number of tasks using this status") task_ids: List[int] = Field( default_factory=list, description="IDs of tasks using this status" ) class Config: from_attributes = True class CustomTaskStatusResponse(BaseModel): """Schema for successful custom task status operation response""" message: str = Field(..., description="Success message") status: Optional[CustomTaskStatus] = Field(None, description="The created or updated status") all_statuses: Optional[AllTaskStatusesResponse] = Field( None, description="All statuses after the operation" ) class Config: from_attributes = True