"""Wiki plugin models — articles, categories, versions (H-WIKI, H-VER).""" from __future__ import annotations import uuid from datetime import datetime from typing import Any from sqlalchemy import ForeignKey, Index, Integer, String, Text from sqlalchemy.dialects.postgresql import JSONB 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 class WikiCategory(Base, TenantMixin, OwnedMixin): """Wiki category for organizing articles.""" __tablename__ = "wiki_categories" __table_args__ = ( Index("ix_wiki_cat_tenant", "tenant_id"), Index("ix_wiki_cat_tenant_slug", "tenant_id", "slug"), ) id: Mapped[uuid.UUID] = mapped_column( PGUUID(as_uuid=True), primary_key=True, default=uuid.uuid4 ) name: Mapped[str] = mapped_column(String(200), nullable=False) slug: Mapped[str] = mapped_column(String(200), nullable=False) description: Mapped[str | None] = mapped_column(Text, nullable=True) parent_id: Mapped[uuid.UUID | None] = mapped_column( PGUUID(as_uuid=True), ForeignKey("wiki_categories.id", ondelete="SET NULL"), nullable=True, ) sort_order: Mapped[int] = mapped_column(Integer, nullable=False, default=0) class WikiArticle(Base, TenantMixin, OwnedMixin): """Wiki article with Markdown content, tags, and entity links (H-WIKI, H-LINK).""" __tablename__ = "wiki_articles" __table_args__ = ( Index("ix_wiki_art_tenant", "tenant_id"), Index("ix_wiki_art_tenant_category", "tenant_id", "category_id"), Index("ix_wiki_art_tenant_slug", "tenant_id", "slug"), Index("ix_wiki_art_tenant_status", "tenant_id", "status"), ) id: Mapped[uuid.UUID] = mapped_column( PGUUID(as_uuid=True), primary_key=True, default=uuid.uuid4 ) title: Mapped[str] = mapped_column(String(300), nullable=False) slug: Mapped[str] = mapped_column(String(300), nullable=False) content: Mapped[str] = mapped_column(Text, nullable=False, default="") content_html: Mapped[str | None] = mapped_column(Text, nullable=True) summary: Mapped[str | None] = mapped_column(Text, nullable=True) category_id: Mapped[uuid.UUID | None] = mapped_column( PGUUID(as_uuid=True), ForeignKey("wiki_categories.id", ondelete="SET NULL"), nullable=True, ) tags: Mapped[list[str]] = mapped_column(JSONB, nullable=False, default=list) status: Mapped[str] = mapped_column( String(20), nullable=False, default="draft" ) # draft, published, archived entity_links: Mapped[list[dict[str, Any]]] = mapped_column( JSONB, nullable=False, default=list ) # [{"entity_type": "contact", "entity_id": "..."}] version: Mapped[int] = mapped_column(Integer, nullable=False, default=1) published_at: Mapped[datetime | None] = mapped_column( nullable=True ) class WikiArticleVersion(Base, TenantMixin): """Article version history for diff and restore (H-VER).""" __tablename__ = "wiki_article_versions" __table_args__ = ( Index("ix_wiki_ver_tenant_article", "tenant_id", "article_id"), Index("ix_wiki_ver_tenant_version", "tenant_id", "article_id", "version"), ) id: Mapped[uuid.UUID] = mapped_column( PGUUID(as_uuid=True), primary_key=True, default=uuid.uuid4 ) article_id: Mapped[uuid.UUID] = mapped_column( PGUUID(as_uuid=True), ForeignKey("wiki_articles.id", ondelete="CASCADE"), nullable=False, ) version: Mapped[int] = mapped_column(Integer, nullable=False) title: Mapped[str] = mapped_column(String(300), nullable=False) content: Mapped[str] = mapped_column(Text, nullable=False) edited_by: Mapped[uuid.UUID | None] = mapped_column( PGUUID(as_uuid=True), ForeignKey("users.id", ondelete="SET NULL"), nullable=True ) edit_comment: Mapped[str | None] = mapped_column(Text, nullable=True)