from datetime import datetime from sqlalchemy import DateTime, ForeignKey, Integer, String, Text, UniqueConstraint, func from sqlalchemy.orm import Mapped, mapped_column, relationship from pgvector.sqlalchemy import Vector from app.config import get_settings from app.database import Base settings = get_settings() class User(Base): __tablename__ = "users" id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True) email: Mapped[str] = mapped_column(String(255), unique=True, nullable=False, index=True) password_hash: Mapped[str] = mapped_column(String(255), nullable=False) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), nullable=False) documents: Mapped[list["UserDocument"]] = relationship(back_populates="user", cascade="all, delete-orphan") class Document(Base): __tablename__ = "documents" id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True) filename: Mapped[str] = mapped_column(String(512), nullable=False) file_hash: Mapped[str] = mapped_column(String(64), unique=True, nullable=False, index=True) file_path: Mapped[str] = mapped_column(String(1024), nullable=False) page_count: Mapped[int] = mapped_column(Integer, default=0, nullable=False) summary: Mapped[str] = mapped_column(Text, default="", nullable=False) extracted_preview: Mapped[str] = mapped_column(Text, default="", nullable=False) processing_status: Mapped[str] = mapped_column(String(32), default="completed", nullable=False) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), nullable=False) users: Mapped[list["UserDocument"]] = relationship(back_populates="document", cascade="all, delete-orphan") chunks: Mapped[list["DocumentChunk"]] = relationship(back_populates="document", cascade="all, delete-orphan") class UserDocument(Base): __tablename__ = "user_documents" __table_args__ = (UniqueConstraint("user_id", "document_id", name="uq_user_document"),) id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True) user_id: Mapped[int] = mapped_column(ForeignKey("users.id"), nullable=False, index=True) document_id: Mapped[int] = mapped_column(ForeignKey("documents.id"), nullable=False, index=True) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), nullable=False) user: Mapped["User"] = relationship(back_populates="documents") document: Mapped["Document"] = relationship(back_populates="users") class DocumentChunk(Base): __tablename__ = "document_chunks" __table_args__ = (UniqueConstraint("document_id", "chunk_index", name="uq_document_chunk"),) id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True) document_id: Mapped[int] = mapped_column(ForeignKey("documents.id"), nullable=False, index=True) file_hash: Mapped[str] = mapped_column(String(64), nullable=False, index=True) filename: Mapped[str] = mapped_column(String(512), nullable=False) chunk_index: Mapped[int] = mapped_column(Integer, nullable=False) page_number: Mapped[int | None] = mapped_column(Integer, nullable=True, index=True) content: Mapped[str] = mapped_column(Text, nullable=False) embedding: Mapped[list[float]] = mapped_column(Vector(settings.embedding_dimensions), nullable=False) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), nullable=False) document: Mapped["Document"] = relationship(back_populates="chunks")