from __future__ import annotations import uuid from datetime import datetime from typing import Any from sqlalchemy import ( Boolean, DateTime, ForeignKey, Index, Integer, JSON, String, Text, UniqueConstraint, ) from sqlalchemy.orm import Mapped, mapped_column, relationship from govoplan_core.db.base import Base, TimestampMixin def new_uuid() -> str: return str(uuid.uuid4()) class ViewDefinition(Base, TimestampMixin): __tablename__ = "view_definitions" __table_args__ = ( UniqueConstraint( "scope_key", "definition_key", name="uq_view_definition_scope_key", ), Index("ix_view_definitions_tenant_status", "tenant_id", "status"), Index("ix_view_definitions_scope", "scope_type", "scope_id"), ) id: Mapped[str] = mapped_column(String(36), primary_key=True, default=new_uuid) tenant_id: Mapped[str | None] = mapped_column(String(36), nullable=True, index=True) scope_type: Mapped[str] = mapped_column(String(20), nullable=False, index=True) scope_id: Mapped[str | None] = mapped_column(String(255), nullable=True, index=True) scope_key: Mapped[str] = mapped_column(String(300), nullable=False, index=True) definition_key: Mapped[str] = mapped_column(String(120), nullable=False) name: Mapped[str] = mapped_column(String(200), nullable=False) description: Mapped[str | None] = mapped_column(Text, nullable=True) status: Mapped[str] = mapped_column( String(24), default="draft", nullable=False, index=True ) current_revision: Mapped[int] = mapped_column(Integer, default=1, nullable=False) published_revision_id: Mapped[str | None] = mapped_column( String(36), nullable=True, index=True, ) created_by: Mapped[str | None] = mapped_column( String(255), nullable=True, index=True ) updated_by: Mapped[str | None] = mapped_column( String(255), nullable=True, index=True ) deleted_at: Mapped[datetime | None] = mapped_column( DateTime(timezone=True), nullable=True, index=True, ) revisions: Mapped[list["ViewRevision"]] = relationship( back_populates="definition", cascade="all, delete-orphan", order_by="ViewRevision.revision", ) assignments: Mapped[list["ViewAssignment"]] = relationship( back_populates="definition", cascade="all, delete-orphan", ) class ViewRevision(Base, TimestampMixin): __tablename__ = "view_revisions" __table_args__ = ( UniqueConstraint( "definition_id", "revision", name="uq_view_revision_number", ), Index("ix_view_revisions_definition", "definition_id", "revision"), Index("ix_view_revisions_contract", "surface_contract_version"), ) id: Mapped[str] = mapped_column(String(36), primary_key=True, default=new_uuid) tenant_id: Mapped[str | None] = mapped_column(String(36), nullable=True, index=True) definition_id: Mapped[str] = mapped_column( ForeignKey("view_definitions.id", ondelete="CASCADE"), nullable=False, index=True, ) revision: Mapped[int] = mapped_column(Integer, nullable=False) surface_contract_version: Mapped[str] = mapped_column(String(20), nullable=False) visible_surface_ids: Mapped[list[str]] = mapped_column( JSON, default=list, nullable=False ) presentation: Mapped[dict[str, Any]] = mapped_column( JSON, default=dict, nullable=False ) content_hash: Mapped[str] = mapped_column(String(64), nullable=False, index=True) created_by: Mapped[str | None] = mapped_column( String(255), nullable=True, index=True ) definition: Mapped[ViewDefinition] = relationship(back_populates="revisions") class ViewAssignment(Base, TimestampMixin): __tablename__ = "view_assignments" __table_args__ = ( UniqueConstraint( "target_key", "definition_id", "mode", name="uq_view_assignment_target_definition_mode", ), Index("ix_view_assignments_tenant_active", "tenant_id", "is_active"), Index("ix_view_assignments_target", "scope_type", "scope_id"), ) id: Mapped[str] = mapped_column(String(36), primary_key=True, default=new_uuid) tenant_id: Mapped[str | None] = mapped_column(String(36), nullable=True, index=True) scope_type: Mapped[str] = mapped_column(String(20), nullable=False, index=True) scope_id: Mapped[str | None] = mapped_column(String(255), nullable=True, index=True) target_key: Mapped[str] = mapped_column(String(320), nullable=False, index=True) definition_id: Mapped[str] = mapped_column( ForeignKey("view_definitions.id", ondelete="CASCADE"), nullable=False, index=True, ) revision_id: Mapped[str | None] = mapped_column( ForeignKey("view_revisions.id", ondelete="SET NULL"), nullable=True, index=True, ) mode: Mapped[str] = mapped_column( String(20), default="available", nullable=False, index=True ) priority: Mapped[int] = mapped_column(Integer, default=0, nullable=False) is_active: Mapped[bool] = mapped_column( Boolean, default=True, nullable=False, index=True ) metadata_: Mapped[dict[str, Any]] = mapped_column( "metadata", JSON, default=dict, nullable=False, ) created_by: Mapped[str | None] = mapped_column( String(255), nullable=True, index=True ) updated_by: Mapped[str | None] = mapped_column( String(255), nullable=True, index=True ) definition: Mapped[ViewDefinition] = relationship(back_populates="assignments") revision: Mapped[ViewRevision | None] = relationship() class ViewPreference(Base, TimestampMixin): __tablename__ = "view_preferences" __table_args__ = ( UniqueConstraint( "tenant_id", "account_id", name="uq_view_preference_tenant_account", ), Index("ix_view_preferences_tenant_account", "tenant_id", "account_id"), ) id: Mapped[str] = mapped_column(String(36), primary_key=True, default=new_uuid) tenant_id: Mapped[str] = mapped_column(String(36), nullable=False, index=True) account_id: Mapped[str] = mapped_column(String(255), nullable=False, index=True) selection_kind: Mapped[str] = mapped_column( String(20), default="auto", nullable=False, index=True, ) view_id: Mapped[str | None] = mapped_column( ForeignKey("view_definitions.id", ondelete="SET NULL"), nullable=True, index=True, ) __all__ = [ "ViewAssignment", "ViewDefinition", "ViewPreference", "ViewRevision", "new_uuid", ]