"""Folder and File models for the DMS plugin.""" from __future__ import annotations import uuid from datetime import datetime from sqlalchemy import DateTime, ForeignKey, Index, Integer, String, UniqueConstraint, Text from sqlalchemy.dialects.postgresql import UUID as PGUUID from sqlalchemy.orm import Mapped, mapped_column from app.core.db import Base, TenantMixin from app.models.owned_mixin import OwnedMixin from pgvector.sqlalchemy import Vector from sqlalchemy.dialects.postgresql import TSVECTOR from typing import Any class Folder(Base, TenantMixin, OwnedMixin): """Folder entity — hierarchical, tenant-scoped, soft-deletable.""" __tablename__ = "folders" __table_args__ = ( UniqueConstraint( "tenant_id", "name", "parent_id", name="uq_folders_tenant_name_parent", # Partial uniqueness only for non-deleted folders is handled at app level ), Index("ix_folders_parent", "parent_id"), Index("ix_folders_tenant", "tenant_id"), ) id: Mapped[uuid.UUID] = mapped_column( PGUUID(as_uuid=True), primary_key=True, default=uuid.uuid4 ) name: Mapped[str] = mapped_column(String(255), nullable=False) parent_id: Mapped[uuid.UUID | None] = mapped_column( PGUUID(as_uuid=True), ForeignKey("folders.id", ondelete="CASCADE"), nullable=True, ) created_by: Mapped[uuid.UUID] = mapped_column(PGUUID(as_uuid=True), nullable=False) deleted_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) class File(Base, TenantMixin, OwnedMixin): """File entity — stored on disk, tenant-scoped, soft-deletable.""" __tablename__ = "files" indexed_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) content_tsv: Mapped[Any] = mapped_column(TSVECTOR, nullable=True) content_text: Mapped[str | None] = mapped_column(Text, nullable=True) embedding: Mapped[Any] = mapped_column(Vector(768), nullable=True) __table_args__ = ( Index("ix_files_folder", "folder_id"), Index("ix_files_tenant", "tenant_id"), Index("ix_files_name", "name"), ) id: Mapped[uuid.UUID] = mapped_column( PGUUID(as_uuid=True), primary_key=True, default=uuid.uuid4 ) name: Mapped[str] = mapped_column(String(255), nullable=False) folder_id: Mapped[uuid.UUID | None] = mapped_column( PGUUID(as_uuid=True), ForeignKey("folders.id", ondelete="SET NULL"), nullable=True, ) uploaded_by: Mapped[uuid.UUID] = mapped_column(PGUUID(as_uuid=True), nullable=False) mime_type: Mapped[str] = mapped_column(String(255), nullable=False) size_bytes: Mapped[int] = mapped_column(Integer, nullable=False) storage_path: Mapped[str] = mapped_column(String(1024), nullable=False) content_hash: Mapped[str | None] = mapped_column(String(64), nullable=True) deleted_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)