102 lines
4.0 KiB
Python
102 lines
4.0 KiB
Python
"""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)
|