"""Модель участника сеанса конференции — метки времени присутствия и identity (ADR-001, п.6).""" import uuid from datetime import datetime from sqlalchemy import CheckConstraint, DateTime, ForeignKey, Index, text from sqlalchemy.dialects.postgresql import UUID from sqlalchemy.orm import Mapped, mapped_column from models.base import Base class ConferenceParticipant(Base): """Временное окно присутствия участника (пользователя ИЛИ гостя) в сеансе. Ровно одно из `user_id`/`guest_id` заполнено (CHECK) — зарегистрированный участник и гость взаимоисключающие identity одной строки. """ __tablename__ = "conference_participants" __table_args__ = ( Index("ix_conference_participants_session_id", "session_id"), CheckConstraint( "(user_id IS NOT NULL)::int + (guest_id IS NOT NULL)::int = 1", name="ck_conference_participants_exactly_one_identity", ), ) id: Mapped[uuid.UUID] = mapped_column( UUID(as_uuid=True), primary_key=True, server_default=text("gen_random_uuid()") ) session_id: Mapped[uuid.UUID] = mapped_column( UUID(as_uuid=True), ForeignKey("conference_sessions.id", ondelete="CASCADE"), nullable=False, ) user_id: Mapped[uuid.UUID | None] = mapped_column( UUID(as_uuid=True), ForeignKey("users.id"), nullable=True ) guest_id: Mapped[uuid.UUID | None] = mapped_column( UUID(as_uuid=True), ForeignKey("guest_access.id"), nullable=True ) joined_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False) left_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)