Files
bakery-ia/services/demo_session/app/models/demo_session.py
2026-01-01 19:56:07 +01:00

97 lines
4.0 KiB
Python

"""
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
DESTROYING = "destroying" # Session in the process of being destroyed
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)
# Error tracking
error_details = Column(JSONB, default=list) # List of error objects for failed sessions
def __repr__(self):
return f"<DemoSession(session_id={self.session_id}, status={self.status.value})>"
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
}