Spaces:
Running
Running
| """ | |
| SQLAlchemy ORM models matching the Supabase PostgreSQL database schema. | |
| This file serves as the single source of truth for the database schema, | |
| used by SQLAlchemy for query ORM mappings and Alembic for migration version control. | |
| """ | |
| import uuid | |
| from datetime import datetime, timezone | |
| from typing import Any, List, Optional | |
| from sqlalchemy import ( | |
| ARRAY, | |
| Boolean, | |
| Column, | |
| DateTime, | |
| Float, | |
| ForeignKey, | |
| Integer, | |
| String, | |
| Text, | |
| func, | |
| text, | |
| ) | |
| from sqlalchemy.dialects.postgresql import JSONB, UUID | |
| from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship | |
| try: | |
| from pgvector.sqlalchemy import Vector | |
| except ImportError: | |
| # Fallback type if pgvector package is not available | |
| from sqlalchemy.types import UserDefinedType | |
| class Vector(UserDefinedType): # type: ignore | |
| def __init__(self, dim: Optional[int] = None, *args, **kwargs): | |
| self.dim = dim | |
| def get_col_spec(self, **kw): | |
| return f"VECTOR({self.dim})" if self.dim else "VECTOR" | |
| class Base(DeclarativeBase): | |
| """Base class for all SQLAlchemy ORM models.""" | |
| pass | |
| class Profile(Base): | |
| __tablename__ = "profiles" | |
| id: Mapped[uuid.UUID] = mapped_column( | |
| UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, server_default=text("gen_random_uuid()") | |
| ) | |
| email: Mapped[Optional[str]] = mapped_column(String, nullable=True) | |
| display_name: Mapped[Optional[str]] = mapped_column(String, nullable=True) | |
| avatar_url: Mapped[Optional[str]] = mapped_column(String, nullable=True) | |
| daily_requests: Mapped[int] = mapped_column(Integer, default=0, server_default=text("0")) | |
| last_request_date: Mapped[Optional[str]] = mapped_column(String, nullable=True) | |
| theme: Mapped[str] = mapped_column(String, default="system", server_default=text("'system'")) | |
| created_at: Mapped[datetime] = mapped_column( | |
| DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), server_default=func.now() | |
| ) | |
| # Relationships | |
| materials: Mapped[List["Material"]] = relationship("Material", back_populates="user", cascade="all, delete-orphan") | |
| quizzes: Mapped[List["Quiz"]] = relationship("Quiz", back_populates="user", cascade="all, delete-orphan") | |
| quiz_attempts: Mapped[List["QuizAttempt"]] = relationship("QuizAttempt", back_populates="user", cascade="all, delete-orphan") | |
| summaries: Mapped[List["Summary"]] = relationship("Summary", back_populates="user", cascade="all, delete-orphan") | |
| chat_sessions: Mapped[List["ChatSession"]] = relationship("ChatSession", back_populates="user", cascade="all, delete-orphan") | |
| class Material(Base): | |
| __tablename__ = "materials" | |
| id: Mapped[uuid.UUID] = mapped_column( | |
| UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, server_default=text("gen_random_uuid()") | |
| ) | |
| user_id: Mapped[uuid.UUID] = mapped_column( | |
| UUID(as_uuid=True), ForeignKey("profiles.id", ondelete="CASCADE"), nullable=False | |
| ) | |
| source_type: Mapped[str] = mapped_column(String, nullable=False) | |
| title: Mapped[str] = mapped_column(String, nullable=False) | |
| file_path: Mapped[Optional[str]] = mapped_column(String, nullable=True) | |
| url: Mapped[Optional[str]] = mapped_column(String, nullable=True) | |
| status: Mapped[str] = mapped_column(String, default="pending", server_default=text("'pending'")) | |
| error_message: Mapped[Optional[str]] = mapped_column(String, nullable=True) | |
| vector_store_path: Mapped[Optional[str]] = mapped_column(String, nullable=True) | |
| title_normalized: Mapped[Optional[str]] = mapped_column(String, nullable=True) | |
| created_at: Mapped[datetime] = mapped_column( | |
| DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), server_default=func.now() | |
| ) | |
| updated_at: Mapped[datetime] = mapped_column( | |
| DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), server_default=func.now() | |
| ) | |
| # Relationships | |
| user: Mapped["Profile"] = relationship("Profile", back_populates="materials") | |
| chunks: Mapped[List["MaterialChunk"]] = relationship("MaterialChunk", back_populates="material", cascade="all, delete-orphan") | |
| embeddings: Mapped[List["MaterialEmbedding"]] = relationship("MaterialEmbedding", back_populates="material", cascade="all, delete-orphan") | |
| summaries: Mapped[List["Summary"]] = relationship("Summary", back_populates="material", cascade="all, delete-orphan") | |
| quizzes: Mapped[List["Quiz"]] = relationship("Quiz", back_populates="material", cascade="all, delete-orphan") | |
| chat_sessions: Mapped[List["ChatSession"]] = relationship("ChatSession", back_populates="material", cascade="all, delete-orphan") | |
| class MaterialChunk(Base): | |
| __tablename__ = "material_chunks" | |
| id: Mapped[uuid.UUID] = mapped_column( | |
| UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, server_default=text("gen_random_uuid()") | |
| ) | |
| material_id: Mapped[uuid.UUID] = mapped_column( | |
| UUID(as_uuid=True), ForeignKey("materials.id", ondelete="CASCADE"), nullable=False | |
| ) | |
| chunk_index: Mapped[int] = mapped_column(Integer, nullable=False) | |
| content: Mapped[str] = mapped_column(Text, nullable=False) | |
| token_count: Mapped[Optional[int]] = mapped_column(Integer, nullable=True) | |
| created_at: Mapped[datetime] = mapped_column( | |
| DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), server_default=func.now() | |
| ) | |
| # Relationships | |
| material: Mapped["Material"] = relationship("Material", back_populates="chunks") | |
| embeddings: Mapped[List["MaterialEmbedding"]] = relationship("MaterialEmbedding", back_populates="chunk", cascade="all, delete-orphan") | |
| class MaterialEmbedding(Base): | |
| __tablename__ = "material_embeddings" | |
| id: Mapped[uuid.UUID] = mapped_column( | |
| UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, server_default=text("gen_random_uuid()") | |
| ) | |
| material_id: Mapped[uuid.UUID] = mapped_column( | |
| UUID(as_uuid=True), ForeignKey("materials.id", ondelete="CASCADE"), nullable=False | |
| ) | |
| chunk_id: Mapped[uuid.UUID] = mapped_column( | |
| UUID(as_uuid=True), ForeignKey("material_chunks.id", ondelete="CASCADE"), nullable=False | |
| ) | |
| embedding: Mapped[Any] = mapped_column(Vector(384), nullable=True) | |
| created_at: Mapped[datetime] = mapped_column( | |
| DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), server_default=func.now() | |
| ) | |
| # Relationships | |
| material: Mapped["Material"] = relationship("Material", back_populates="embeddings") | |
| chunk: Mapped["MaterialChunk"] = relationship("MaterialChunk", back_populates="embeddings") | |
| class Summary(Base): | |
| __tablename__ = "summaries" | |
| id: Mapped[uuid.UUID] = mapped_column( | |
| UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, server_default=text("gen_random_uuid()") | |
| ) | |
| material_id: Mapped[uuid.UUID] = mapped_column( | |
| UUID(as_uuid=True), ForeignKey("materials.id", ondelete="CASCADE"), nullable=False | |
| ) | |
| user_id: Mapped[uuid.UUID] = mapped_column( | |
| UUID(as_uuid=True), ForeignKey("profiles.id", ondelete="CASCADE"), nullable=False | |
| ) | |
| summary: Mapped[str] = mapped_column(Text, nullable=False) | |
| status: Mapped[str] = mapped_column(String, default="completed", server_default=text("'completed'")) | |
| model_name: Mapped[Optional[str]] = mapped_column(String, nullable=True) | |
| error_message: Mapped[Optional[str]] = mapped_column(String, nullable=True) | |
| time_taken: Mapped[Optional[float]] = mapped_column(Float, nullable=True) | |
| created_at: Mapped[datetime] = mapped_column( | |
| DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), server_default=func.now() | |
| ) | |
| # Relationships | |
| material: Mapped["Material"] = relationship("Material", back_populates="summaries") | |
| user: Mapped["Profile"] = relationship("Profile", back_populates="summaries") | |
| class Quiz(Base): | |
| __tablename__ = "quizzes" | |
| id: Mapped[uuid.UUID] = mapped_column( | |
| UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, server_default=text("gen_random_uuid()") | |
| ) | |
| user_id: Mapped[uuid.UUID] = mapped_column( | |
| UUID(as_uuid=True), ForeignKey("profiles.id", ondelete="CASCADE"), nullable=False | |
| ) | |
| material_id: Mapped[Optional[uuid.UUID]] = mapped_column( | |
| UUID(as_uuid=True), ForeignKey("materials.id", ondelete="CASCADE"), nullable=True | |
| ) | |
| source_type: Mapped[Optional[str]] = mapped_column(String, nullable=True) | |
| difficulty: Mapped[Optional[str]] = mapped_column(String, nullable=True) | |
| mcq_count: Mapped[Optional[int]] = mapped_column(Integer, nullable=True) | |
| tf_count: Mapped[Optional[int]] = mapped_column(Integer, nullable=True) | |
| quiz_data: Mapped[Optional[dict]] = mapped_column(JSONB, nullable=True) | |
| status: Mapped[str] = mapped_column(String, default="completed", server_default=text("'completed'")) | |
| model_name: Mapped[Optional[str]] = mapped_column(String, nullable=True) | |
| error_message: Mapped[Optional[str]] = mapped_column(String, nullable=True) | |
| created_at: Mapped[datetime] = mapped_column( | |
| DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), server_default=func.now() | |
| ) | |
| # Relationships | |
| user: Mapped["Profile"] = relationship("Profile", back_populates="quizzes") | |
| material: Mapped[Optional["Material"]] = relationship("Material", back_populates="quizzes") | |
| attempts: Mapped[List["QuizAttempt"]] = relationship("QuizAttempt", back_populates="quiz", cascade="all, delete-orphan") | |
| class QuizAttempt(Base): | |
| __tablename__ = "quiz_attempts" | |
| id: Mapped[uuid.UUID] = mapped_column( | |
| UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, server_default=text("gen_random_uuid()") | |
| ) | |
| quiz_id: Mapped[uuid.UUID] = mapped_column( | |
| UUID(as_uuid=True), ForeignKey("quizzes.id", ondelete="CASCADE"), nullable=False | |
| ) | |
| user_id: Mapped[uuid.UUID] = mapped_column( | |
| UUID(as_uuid=True), ForeignKey("profiles.id", ondelete="CASCADE"), nullable=False | |
| ) | |
| score: Mapped[Optional[int]] = mapped_column(Integer, nullable=True) | |
| total: Mapped[Optional[int]] = mapped_column(Integer, nullable=True) | |
| results: Mapped[Optional[dict]] = mapped_column(JSONB, nullable=True) | |
| created_at: Mapped[datetime] = mapped_column( | |
| DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), server_default=func.now() | |
| ) | |
| # Relationships | |
| quiz: Mapped["Quiz"] = relationship("Quiz", back_populates="attempts") | |
| user: Mapped["Profile"] = relationship("Profile", back_populates="quiz_attempts") | |
| class ChatSession(Base): | |
| __tablename__ = "chat_sessions" | |
| id: Mapped[uuid.UUID] = mapped_column( | |
| UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, server_default=text("gen_random_uuid()") | |
| ) | |
| user_id: Mapped[uuid.UUID] = mapped_column( | |
| UUID(as_uuid=True), ForeignKey("profiles.id", ondelete="CASCADE"), nullable=False | |
| ) | |
| material_id: Mapped[uuid.UUID] = mapped_column( | |
| UUID(as_uuid=True), ForeignKey("materials.id", ondelete="CASCADE"), nullable=False | |
| ) | |
| title: Mapped[Optional[str]] = mapped_column(String, nullable=True) | |
| created_at: Mapped[datetime] = mapped_column( | |
| DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), server_default=func.now() | |
| ) | |
| updated_at: Mapped[datetime] = mapped_column( | |
| DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), server_default=func.now() | |
| ) | |
| # Relationships | |
| user: Mapped["Profile"] = relationship("Profile", back_populates="chat_sessions") | |
| material: Mapped["Material"] = relationship("Material", back_populates="chat_sessions") | |
| messages: Mapped[List["ChatMessage"]] = relationship("ChatMessage", back_populates="session", cascade="all, delete-orphan") | |
| class ChatMessage(Base): | |
| __tablename__ = "chat_messages" | |
| id: Mapped[uuid.UUID] = mapped_column( | |
| UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, server_default=text("gen_random_uuid()") | |
| ) | |
| session_id: Mapped[uuid.UUID] = mapped_column( | |
| UUID(as_uuid=True), ForeignKey("chat_sessions.id", ondelete="CASCADE"), nullable=False | |
| ) | |
| role: Mapped[Optional[str]] = mapped_column(String, nullable=True) | |
| content: Mapped[Optional[str]] = mapped_column(Text, nullable=True) | |
| message_metadata: Mapped[Optional[dict]] = mapped_column("metadata", JSONB, nullable=True) | |
| retrieved_chunk_ids: Mapped[Optional[list]] = mapped_column(JSONB, nullable=True) | |
| created_at: Mapped[datetime] = mapped_column( | |
| DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), server_default=func.now() | |
| ) | |
| # Relationships | |
| session: Mapped["ChatSession"] = relationship("ChatSession", back_populates="messages") | |
| class ChatMessageChunk(Base): | |
| __tablename__ = "chat_message_chunks" | |
| message_id: Mapped[uuid.UUID] = mapped_column( | |
| UUID(as_uuid=True), ForeignKey("chat_messages.id", ondelete="CASCADE"), primary_key=True | |
| ) | |
| chunk_id: Mapped[uuid.UUID] = mapped_column( | |
| UUID(as_uuid=True), ForeignKey("material_chunks.id", ondelete="CASCADE"), primary_key=True | |
| ) | |
| relevance_score: Mapped[Optional[float]] = mapped_column(Float, nullable=True) | |