from datetime import UTC, datetime from sqlalchemy.dialects.postgresql import JSONB from app import db class Epic(db.Model): """Epic model for tracking large features across multiple cards""" __tablename__ = "epics" id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(200), nullable=False, index=True) description = db.Column(db.Text) content = db.Column(JSONB) # Rich text content (Slate.js JSON) color = db.Column(db.String(7)) # Hex color for epic badge closed = db.Column(db.Boolean, default=False, index=True) pos = db.Column(db.Float) # Position for sorting in epic list depth_limit = db.Column(db.Integer, default=5) # Max nesting depth # Foreign keys board_id = db.Column( db.Integer, db.ForeignKey("boards.id", ondelete="CASCADE"), nullable=False, index=True, ) parent_epic_id = db.Column( db.Integer, db.ForeignKey("epics.id", ondelete="SET NULL") ) # Timestamps date_last_activity = db.Column(db.DateTime) created_at = db.Column(db.DateTime, default=lambda: datetime.now(UTC)) updated_at = db.Column( db.DateTime, default=lambda: datetime.now(UTC), onupdate=lambda: datetime.now(UTC), ) # JSON fields for metrics metrics = db.Column(JSONB) # {"card_count": 10} # Relationships board = db.relationship("Board", backref="epics") parent_epic = db.relationship("Epic", remote_side=[id], backref="child_epics") cards = db.relationship("Card", backref="epic", cascade="all, delete-orphan") attachments = db.relationship( "FileAttachment", foreign_keys="FileAttachment.attachable_id", primaryjoin="""and_(FileAttachment.attachable_id == Epic.id, FileAttachment.attachable_type == 'Epic')""", cascade="all, delete-orphan", lazy="dynamic", ) def to_dict(self): """Convert epic to dictionary""" return { "id": self.id, "name": self.name, "description": self.description, "content": self.content, "color": self.color, "closed": self.closed, "pos": self.pos, "depth_limit": self.depth_limit, "board_id": self.board_id, "parent_epic_id": self.parent_epic_id, "date_last_activity": self.date_last_activity.isoformat() if self.date_last_activity else None, "created_at": self.created_at.isoformat() if self.created_at else None, "updated_at": self.updated_at.isoformat() if self.updated_at else None, "metrics": self.metrics or {"card_count": 0}, } def __repr__(self): return f""