from pydantic import BaseModel, Field from typing import Optional, List from datetime import date, datetime from enum import Enum from models.task import TaskType, TaskStatus, ReviewDecision, AttachmentType, NoteType class TaskBase(BaseModel): name: str = Field(..., min_length=1, max_length=255) description: Optional[str] = None task_type: str # Changed from TaskType enum to str to support custom task types start_date: Optional[date] = None deadline: Optional[date] = None status: str = "not_started" # Changed from TaskStatus enum to str to support custom statuses department: Optional[str] = None # Standard or project-custom department, independent of assignee class TaskCreate(TaskBase): project_id: int episode_id: Optional[int] = None shot_id: Optional[int] = None asset_id: Optional[int] = None assigned_user_id: Optional[int] = None class TaskUpdate(BaseModel): name: Optional[str] = Field(None, min_length=1, max_length=255) description: Optional[str] = None task_type: Optional[str] = None # Changed from TaskType enum to str to support custom task types start_date: Optional[date] = None deadline: Optional[date] = None status: Optional[str] = None # Changed from TaskStatus enum to str to support custom statuses department: Optional[str] = None # Standard or project-custom department, independent of assignee assigned_user_id: Optional[int] = None class TaskStatusUpdate(BaseModel): status: str # Changed from TaskStatus enum to str to support custom statuses class TaskAssignment(BaseModel): assigned_user_id: int class TaskResponse(TaskBase): id: int project_id: int episode_id: Optional[int] = None shot_id: Optional[int] = None asset_id: Optional[int] = None assigned_user_id: Optional[int] = None created_at: datetime updated_at: datetime # Related entity names for display project_name: Optional[str] = None episode_name: Optional[str] = None shot_name: Optional[str] = None asset_name: Optional[str] = None assigned_user_name: Optional[str] = None assigned_user_email: Optional[str] = None assigned_user_avatar_url: Optional[str] = None class Config: from_attributes = True class TaskListResponse(BaseModel): id: int name: str task_type: str # Changed from TaskType enum to str to support custom task types status: str # Changed from TaskStatus enum to str to support custom statuses department: Optional[str] = None # Standard or project-custom department, independent of assignee start_date: Optional[date] = None deadline: Optional[date] = None project_id: int project_name: str episode_id: Optional[int] = None episode_name: Optional[str] = None shot_id: Optional[int] = None shot_name: Optional[str] = None asset_id: Optional[int] = None asset_name: Optional[str] = None assigned_user_id: Optional[int] = None assigned_user_name: Optional[str] = None assigned_user_avatar_url: Optional[str] = None created_at: datetime updated_at: datetime class Config: from_attributes = True # Production Notes schemas class ProductionNoteBase(BaseModel): content: str = Field(..., min_length=1) parent_note_id: Optional[int] = None class ProductionNoteCreate(ProductionNoteBase): note_type: NoteType = NoteType.INTERNAL class ProductionNoteUpdate(BaseModel): content: str = Field(..., min_length=1) class ProductionNoteResponse(ProductionNoteBase): id: int task_id: int user_id: int note_type: NoteType created_at: datetime updated_at: datetime # User information user_first_name: str user_last_name: str user_email: str user_avatar_url: Optional[str] = None # Child notes for threading child_notes: Optional[List['ProductionNoteResponse']] = None class Config: from_attributes = True # Task Attachment schemas class TaskAttachmentBase(BaseModel): file_name: str attachment_type: AttachmentType = AttachmentType.REFERENCE description: Optional[str] = None class TaskAttachmentCreate(TaskAttachmentBase): pass class TaskAttachmentResponse(TaskAttachmentBase): id: int task_id: int user_id: int file_path: str file_type: str file_size: int uploaded_at: datetime # User information user_first_name: str user_last_name: str # File serving URLs (computed properties) download_url: Optional[str] = None thumbnail_url: Optional[str] = None class Config: from_attributes = True # Submission schemas class SubmissionBase(BaseModel): notes: Optional[str] = None class SubmissionCreate(SubmissionBase): pass class SubmissionUpdate(BaseModel): notes: Optional[str] = None class SubmissionResponse(SubmissionBase): id: int task_id: int user_id: int file_path: str file_name: str version_number: int submitted_at: datetime # User information user_first_name: str user_last_name: str # Latest review if any latest_review: Optional['ReviewResponse'] = None # File serving URLs (computed properties) download_url: Optional[str] = None thumbnail_url: Optional[str] = None stream_url: Optional[str] = None class Config: from_attributes = True class SubmissionDateInfo(BaseModel): """Minimal per-task submission date, for lightweight bulk lookups (e.g. Gantt markers).""" task_id: int submitted_at: datetime class Config: from_attributes = True # Review schemas class ReviewBase(BaseModel): decision: ReviewDecision feedback: Optional[str] = None class ReviewCreate(ReviewBase): pass class ReviewResponse(ReviewBase): id: int submission_id: int reviewer_id: int reviewed_at: datetime # Reviewer information reviewer_first_name: str reviewer_last_name: str class Config: from_attributes = True # Bulk action schemas class BulkStatusUpdate(BaseModel): task_ids: List[int] = Field(..., min_length=1) status: str # Changed from TaskStatus enum to str to support custom statuses class BulkAssignment(BaseModel): task_ids: List[int] = Field(..., min_length=1) assigned_user_id: int class BulkActionResult(BaseModel): success_count: int failed_count: int errors: Optional[List[dict]] = None # Update forward references ProductionNoteResponse.model_rebuild() SubmissionResponse.model_rebuild()