""" Demo Session Models Tracks ephemeral demo sessions for prospect users """ from sqlalchemy import Column, String, Boolean, DateTime, Integer, Enum as SQLEnum from sqlalchemy.dialects.postgresql import UUID, JSONB from datetime import datetime, timezone import uuid import enum from shared.database.base import Base class DemoSessionStatus(enum.Enum): """Demo session status""" PENDING = "pending" # Data cloning in progress READY = "ready" # All data loaded, safe to use FAILED = "failed" # One or more services failed completely PARTIAL = "partial" # Some services failed, others succeeded ACTIVE = "active" # User is actively using the session (deprecated, use READY) EXPIRED = "expired" # Session TTL exceeded DESTROYED = "destroyed" # Session terminated class CloningStatus(enum.Enum): """Individual service cloning status""" NOT_STARTED = "not_started" IN_PROGRESS = "in_progress" COMPLETED = "completed" FAILED = "failed" class DemoSession(Base): """Demo Session tracking model""" __tablename__ = "demo_sessions" id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) session_id = Column(String(100), unique=True, nullable=False, index=True) # Session ownership user_id = Column(UUID(as_uuid=True), nullable=True) ip_address = Column(String(45), nullable=True) user_agent = Column(String(500), nullable=True) # Demo tenant linking base_demo_tenant_id = Column(UUID(as_uuid=True), nullable=False, index=True) virtual_tenant_id = Column(UUID(as_uuid=True), nullable=False, index=True) demo_account_type = Column(String(50), nullable=False) # 'professional', 'enterprise' # Session lifecycle status = Column(SQLEnum(DemoSessionStatus, values_callable=lambda obj: [e.value for e in obj]), default=DemoSessionStatus.PENDING, index=True) created_at = Column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), index=True) expires_at = Column(DateTime(timezone=True), nullable=False, index=True) last_activity_at = Column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc)) destroyed_at = Column(DateTime(timezone=True), nullable=True) # Cloning progress tracking cloning_started_at = Column(DateTime(timezone=True), nullable=True) cloning_completed_at = Column(DateTime(timezone=True), nullable=True) total_records_cloned = Column(Integer, default=0) # Per-service cloning status cloning_progress = Column(JSONB, default=dict) # {service_name: {status, records, started_at, completed_at, error}} # Session metrics request_count = Column(Integer, default=0) data_cloned = Column(Boolean, default=False) # Deprecated: use status instead redis_populated = Column(Boolean, default=False) # Deprecated: use status instead # Session metadata session_metadata = Column(JSONB, default=dict) def __repr__(self): return f"" def to_dict(self): """Convert to dictionary""" return { "id": str(self.id), "session_id": self.session_id, "user_id": str(self.user_id) if self.user_id else None, "virtual_tenant_id": str(self.virtual_tenant_id), "base_demo_tenant_id": str(self.base_demo_tenant_id), "demo_account_type": self.demo_account_type, "status": self.status.value, "created_at": self.created_at.isoformat() if self.created_at else None, "expires_at": self.expires_at.isoformat() if self.expires_at else None, "last_activity_at": self.last_activity_at.isoformat() if self.last_activity_at else None, "request_count": self.request_count, "metadata": self.session_metadata }