from datetime import datetime, timezone from sqlalchemy import ForeignKey, Integer, Text, UniqueConstraint from sqlalchemy.orm import Mapped, mapped_column, relationship from fabledassistant.models import Base from fabledassistant.models.base import CreatedAtMixin, TimestampMixin class Group(Base, TimestampMixin): __tablename__ = "groups" id: Mapped[int] = mapped_column(Integer, primary_key=True) name: Mapped[str] = mapped_column(Text, nullable=False, unique=True) description: Mapped[str | None] = mapped_column(Text) created_by: Mapped[int | None] = mapped_column( Integer, ForeignKey("users.id", ondelete="SET NULL") ) memberships: Mapped[list["GroupMembership"]] = relationship( "GroupMembership", back_populates="group", cascade="all, delete-orphan" ) def to_dict(self) -> dict: return { "id": self.id, "name": self.name, "description": self.description, "created_by": self.created_by, "created_at": self.created_at.isoformat(), "updated_at": self.updated_at.isoformat(), } class GroupMembership(Base, CreatedAtMixin): __tablename__ = "group_memberships" __table_args__ = (UniqueConstraint("group_id", "user_id", name="uq_gm_group_user"),) id: Mapped[int] = mapped_column(Integer, primary_key=True) group_id: Mapped[int] = mapped_column( Integer, ForeignKey("groups.id", ondelete="CASCADE"), nullable=False ) user_id: Mapped[int] = mapped_column( Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False ) role: Mapped[str] = mapped_column(Text, nullable=False, default="member") group: Mapped["Group"] = relationship("Group", back_populates="memberships") def to_dict(self) -> dict: return { "id": self.id, "group_id": self.group_id, "user_id": self.user_id, "role": self.role, "created_at": self.created_at.isoformat(), }