from sqlalchemy import ( CheckConstraint, DateTime, Integer, ForeignKey, Index, String, UniqueConstraint, func, ) from sqlalchemy.orm import Mapped, mapped_column, relationship from datetime import datetime from typing import Optional from app.database import Base class ProjectMember(Base): __tablename__ = "project_members" __table_args__ = ( UniqueConstraint("project_id", "user_id", name="uq_project_members_proj_user"), CheckConstraint( "permission IN ('read_only', 'create_modify')", name="ck_project_members_permission", ), CheckConstraint( "status IN ('pending', 'accepted', 'rejected')", name="ck_project_members_status", ), CheckConstraint( "source IN ('invited', 'auto_assigned')", name="ck_project_members_source", ), Index("ix_project_members_user_id", "user_id"), Index("ix_project_members_project_id", "project_id"), Index("ix_project_members_status", "status"), ) id: Mapped[int] = mapped_column(primary_key=True, index=True) project_id: Mapped[int] = mapped_column( Integer, ForeignKey("projects.id", ondelete="CASCADE"), nullable=False ) user_id: Mapped[int] = mapped_column( Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False ) invited_by: Mapped[int] = mapped_column( Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False ) permission: Mapped[str] = mapped_column(String(20), nullable=False) status: Mapped[str] = mapped_column(String(20), nullable=False, default="pending") source: Mapped[str] = mapped_column(String(20), nullable=False, default="invited") created_at: Mapped[datetime] = mapped_column( DateTime, default=func.now(), server_default=func.now() ) updated_at: Mapped[datetime] = mapped_column( DateTime, default=func.now(), server_default=func.now(), onupdate=func.now() ) accepted_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True) # Relationships — lazy="raise" to prevent N+1 (mirrors CalendarMember) project: Mapped["Project"] = relationship(back_populates="members", lazy="raise") user: Mapped["User"] = relationship(foreign_keys=[user_id], lazy="raise") inviter: Mapped[Optional["User"]] = relationship( foreign_keys=[invited_by], lazy="raise" )