from sqlalchemy import Column, Integer, String, DateTime, Date, Enum, ForeignKey, Float, JSON from sqlalchemy.orm import relationship from sqlalchemy.sql import func from database import Base from .user import DepartmentRole import enum class ProjectStatus(str, enum.Enum): PLANNING = "planning" IN_PROGRESS = "in_progress" ON_HOLD = "on_hold" COMPLETED = "completed" CANCELLED = "cancelled" class ProjectType(str, enum.Enum): TV = "tv" CINEMA = "cinema" GAME = "game" class Project(Base): __tablename__ = "projects" id = Column(Integer, primary_key=True, index=True) name = Column(String, nullable=False, index=True) code_name = Column(String, nullable=False, unique=True, index=True) # Unique project code client_name = Column(String, nullable=False, index=True) # Client/studio name project_type = Column(Enum(ProjectType), nullable=False) # TV, Cinema, Game description = Column(String) status = Column(Enum(ProjectStatus), nullable=False, default=ProjectStatus.PLANNING) start_date = Column(Date) end_date = Column(Date) # Technical specifications frame_rate = Column(Float, nullable=True) # Frames per second (1-120 fps) data_drive_path = Column(String, nullable=True) # Physical path for project data storage publish_storage_path = Column(String, nullable=True) # Path for approved work delivery delivery_image_resolution = Column(String, nullable=True) # Required image resolution delivery_movie_specs_by_department = Column(JSON, nullable=True) # Delivery movie specs per department # Project-specific settings upload_data_location = Column(String, nullable=True) # Custom upload storage path asset_task_templates = Column(JSON, nullable=True) # Default tasks per asset category shot_task_templates = Column(JSON, nullable=True) # Default tasks for shots enabled_asset_tasks = Column(JSON, nullable=True) # Enabled/disabled asset tasks per category enabled_shot_tasks = Column(JSON, nullable=True) # Enabled/disabled shot tasks # Custom task types custom_asset_task_types = Column(JSON, nullable=True) # Custom task types for assets custom_shot_task_types = Column(JSON, nullable=True) # Custom task types for shots # Custom task statuses custom_task_statuses = Column(JSON, nullable=True) # Custom task statuses for project # Project thumbnail thumbnail_path = Column(String, nullable=True) # Path to project thumbnail image created_at = Column(DateTime(timezone=True), server_default=func.now()) updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now()) # Relationships episodes = relationship("Episode", back_populates="project", cascade="all, delete-orphan") shots = relationship("Shot", back_populates="project", cascade="all, delete-orphan") assets = relationship("Asset", back_populates="project", cascade="all, delete-orphan") project_members = relationship("ProjectMember", back_populates="project", cascade="all, delete-orphan") tasks = relationship("Task", back_populates="project", cascade="all, delete-orphan") def __repr__(self): return f"" class ProjectMember(Base): __tablename__ = "project_members" id = Column(Integer, primary_key=True, index=True) user_id = Column(Integer, ForeignKey("users.id"), nullable=False) project_id = Column(Integer, ForeignKey("projects.id"), nullable=False) department_role = Column(Enum(DepartmentRole), nullable=True) joined_at = Column(DateTime(timezone=True), server_default=func.now()) # Relationships user = relationship("User", back_populates="project_memberships") project = relationship("Project", back_populates="project_members") def __repr__(self): return f""