Add shot/asset typing and owned task types to departments
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.
This commit is contained in:
@@ -2,23 +2,35 @@
|
||||
Pydantic schemas for department management
|
||||
"""
|
||||
from pydantic import BaseModel, Field, validator
|
||||
from typing import List
|
||||
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(r'^[a-z0-9_]{2,50}$', v):
|
||||
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"""
|
||||
@@ -28,18 +40,52 @@ class CustomDepartmentUpdate(BaseModel):
|
||||
@validator('new_name')
|
||||
def validate_department_name(cls, v):
|
||||
"""Validate department name format"""
|
||||
if not re.match(r'^[a-z0-9_]{2,50}$', v):
|
||||
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[str] = Field(..., description="All departments")
|
||||
standard_departments: List[str] = Field(..., description="Standard departments (read-only)")
|
||||
custom_departments: List[str] = Field(..., description="Custom departments")
|
||||
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):
|
||||
@@ -48,3 +94,11 @@ class DepartmentInUseError(BaseModel):
|
||||
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")
|
||||
|
||||
Reference in New Issue
Block a user