LinkDesk/backend/models/project.py

88 lines
3.9 KiB
Python

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"<Project(id={self.id}, name='{self.name}', status='{self.status}')>"
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"<ProjectMember(user_id={self.user_id}, project_id={self.project_id}, role='{self.department_role}')>"