LinkDesk/backend/models/shot.py

66 lines
2.5 KiB
Python

from sqlalchemy import Column, Integer, String, DateTime, ForeignKey, Enum, UniqueConstraint
from sqlalchemy.orm import relationship, Query
from sqlalchemy.sql import func
from database import Base
import enum
class ShotStatus(str, enum.Enum):
NOT_STARTED = "not_started"
IN_PROGRESS = "in_progress"
ON_HOLD = "on_hold"
COMPLETED = "completed"
APPROVED = "approved"
class Shot(Base):
__tablename__ = "shots"
id = Column(Integer, primary_key=True, index=True)
project_id = Column(Integer, ForeignKey("projects.id"), nullable=False, index=True)
episode_id = Column(Integer, ForeignKey("episodes.id"), nullable=False)
name = Column(String, nullable=False, index=True)
description = Column(String)
frame_start = Column(Integer, nullable=False, default=1001)
frame_end = Column(Integer, nullable=False, default=1001)
status = Column(Enum(ShotStatus), nullable=False, default=ShotStatus.NOT_STARTED)
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
# Soft deletion columns
deleted_at = Column(DateTime(timezone=True), nullable=True)
deleted_by = Column(Integer, ForeignKey("users.id"), nullable=True)
# Relationships
project = relationship("Project", back_populates="shots")
episode = relationship("Episode", back_populates="shots")
tasks = relationship("Task", back_populates="shot", cascade="all, delete-orphan")
deleted_by_user = relationship("User", foreign_keys=[deleted_by])
# Constraints
__table_args__ = (
UniqueConstraint('project_id', 'name', name='uq_shot_project_name'),
)
@property
def is_deleted(self) -> bool:
"""Check if the shot is soft deleted."""
return self.deleted_at is not None
@classmethod
def query_active(cls, query: Query) -> Query:
"""Filter query to exclude soft deleted shots."""
return query.filter(cls.deleted_at.is_(None))
@classmethod
def query_deleted(cls, query: Query) -> Query:
"""Filter query to include only soft deleted shots."""
return query.filter(cls.deleted_at.isnot(None))
@classmethod
def query_all_including_deleted(cls, query: Query) -> Query:
"""Return query without soft deletion filtering (for admin use)."""
return query
def __repr__(self):
return f"<Shot(id={self.id}, name='{self.name}', frames={self.frame_start}-{self.frame_end})>"