c09710f4e5
Departments are now a customizable per-project list (standard + custom), matching the existing custom task type/status pattern, instead of a fixed 7-value enum. They're usable directly on tasks (new field, independent of assignee) and continue to drive team member department roles.
93 lines
4.3 KiB
Python
93 lines
4.3 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
|
|
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
|
|
|
|
# Custom departments
|
|
custom_departments = Column(JSON, nullable=True) # Custom departments for project (in addition to standard ones)
|
|
|
|
# Submission configuration per task type
|
|
submission_config_by_task_type = Column(JSON, nullable=True) # Allowed file types, naming pattern, required flag per task type
|
|
|
|
# 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(String, nullable=True) # Free-form: standard department or a project's custom department
|
|
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}')>" |