31d17780a4
Departments now carry a type (shot or asset) and own a list of task types (e.g. Animation: blocking/primary_pass/second_pass, Composite: first_pass/second_pass, plus a new Simulation department), additive to the existing flat Custom Task Type system. When a task's type belongs to a department, its department is derived and kept in sync server-side across create/update paths; Task Type is now editable in the Task Detail panel and Department options are filtered to the task's shot/asset scope.
105 lines
4.3 KiB
Python
105 lines
4.3 KiB
Python
"""
|
|
Pydantic schemas for department management
|
|
"""
|
|
from pydantic import BaseModel, Field, validator
|
|
from typing import List, Literal
|
|
import re
|
|
|
|
DEPARTMENT_NAME_PATTERN = r'^[a-z0-9_]{2,50}$'
|
|
|
|
|
|
class CustomDepartmentCreate(BaseModel):
|
|
"""Schema for creating a new custom department"""
|
|
department: str = Field(..., min_length=2, max_length=50, description="Department name")
|
|
department_type: Literal["shot", "asset"] = Field(..., description="Whether this department applies to shots or assets")
|
|
task_types: List[str] = Field(default_factory=list, description="Task types owned by this department")
|
|
|
|
@validator('department')
|
|
def validate_department_name(cls, v):
|
|
"""Validate department name format"""
|
|
if not re.match(DEPARTMENT_NAME_PATTERN, v):
|
|
raise ValueError(
|
|
'Department name must be 2-50 characters, lowercase alphanumeric with underscores only'
|
|
)
|
|
return v
|
|
|
|
@validator('task_types', each_item=True)
|
|
def validate_task_type_name(cls, v):
|
|
if not re.match(DEPARTMENT_NAME_PATTERN, v):
|
|
raise ValueError(
|
|
'Task type name must be 2-50 characters, lowercase alphanumeric with underscores only'
|
|
)
|
|
return v
|
|
|
|
|
|
class CustomDepartmentUpdate(BaseModel):
|
|
"""Schema for updating a custom department name"""
|
|
old_name: str = Field(..., description="Current department name")
|
|
new_name: str = Field(..., min_length=2, max_length=50, description="New department name")
|
|
|
|
@validator('new_name')
|
|
def validate_department_name(cls, v):
|
|
"""Validate department name format"""
|
|
if not re.match(DEPARTMENT_NAME_PATTERN, v):
|
|
raise ValueError(
|
|
'Department name must be 2-50 characters, lowercase alphanumeric with underscores only'
|
|
)
|
|
return v
|
|
|
|
|
|
class DepartmentTaskTypeCreate(BaseModel):
|
|
"""Schema for adding a task type to a custom department"""
|
|
task_type: str = Field(..., min_length=2, max_length=50, description="Task type name")
|
|
|
|
@validator('task_type')
|
|
def validate_task_type_name(cls, v):
|
|
if not re.match(DEPARTMENT_NAME_PATTERN, v):
|
|
raise ValueError(
|
|
'Task type name must be 2-50 characters, lowercase alphanumeric with underscores only'
|
|
)
|
|
return v
|
|
|
|
|
|
class DepartmentTaskTypeUpdate(BaseModel):
|
|
"""Schema for renaming a task type within a custom department"""
|
|
old_name: str = Field(..., description="Current task type name")
|
|
new_name: str = Field(..., min_length=2, max_length=50, description="New task type name")
|
|
|
|
@validator('new_name')
|
|
def validate_task_type_name(cls, v):
|
|
if not re.match(DEPARTMENT_NAME_PATTERN, v):
|
|
raise ValueError(
|
|
'Task type name must be 2-50 characters, lowercase alphanumeric with underscores only'
|
|
)
|
|
return v
|
|
|
|
|
|
class DepartmentInfo(BaseModel):
|
|
"""A single department: its name, whether it applies to shots or assets, and its owned task types"""
|
|
name: str
|
|
type: Literal["shot", "asset"]
|
|
task_types: List[str]
|
|
|
|
|
|
class AllDepartmentsResponse(BaseModel):
|
|
"""Schema for response containing all departments (standard + custom)"""
|
|
departments: List[DepartmentInfo] = Field(..., description="All departments")
|
|
standard_departments: List[DepartmentInfo] = Field(..., description="Standard departments (read-only)")
|
|
custom_departments: List[DepartmentInfo] = Field(..., description="Custom departments")
|
|
|
|
|
|
class DepartmentInUseError(BaseModel):
|
|
"""Schema for error when trying to delete a department in use"""
|
|
error: str = Field(..., description="Error message")
|
|
department: str = Field(..., description="Department that is in use")
|
|
member_count: int = Field(..., description="Number of project members using this department")
|
|
task_count: int = Field(..., description="Number of tasks using this department")
|
|
|
|
|
|
class DepartmentTaskTypeInUseError(BaseModel):
|
|
"""Schema for error when trying to delete a department task type in use"""
|
|
error: str = Field(..., description="Error message")
|
|
department: str = Field(..., description="Department the task type belongs to")
|
|
task_type: str = Field(..., description="Task type that is in use")
|
|
task_count: int = Field(..., description="Number of tasks using this task type")
|