from __future__ import annotations import enum import uuid from datetime import datetime from sqlalchemy import ( DateTime, Enum, Float, ForeignKey, UniqueConstraint, func, ) from sqlalchemy.dialects.postgresql import UUID from sqlalchemy.orm import Mapped, mapped_column, relationship from app.database.database import Base class RelationshipType(str, enum.Enum): REFERENCES = "REFERENCES" SUPPORTS = "SUPPORTS" CONTRADICTS = "CONTRADICTS" USES = "USES" IMPROVES = "IMPROVES" COMPARES = "COMPARES" DERIVED_FROM = "DERIVED_FROM" RELATED_TO = "RELATED_TO" class KnowledgeLink(Base): """ Represents a semantic relationship between two KnowledgeItems. Together, KnowledgeItems and KnowledgeLinks form the Knowledge Register graph. """ __tablename__ = "knowledge_links" __table_args__ = ( UniqueConstraint( "source_item_id", "target_item_id", "relationship_type", name="uq_knowledge_link", ), ) id: Mapped[uuid.UUID] = mapped_column( UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, ) source_item_id: Mapped[uuid.UUID] = mapped_column( UUID(as_uuid=True), ForeignKey("knowledge_items.id", ondelete="CASCADE"), nullable=False, index=True, ) target_item_id: Mapped[uuid.UUID] = mapped_column( UUID(as_uuid=True), ForeignKey("knowledge_items.id", ondelete="CASCADE"), nullable=False, index=True, ) relationship_type: Mapped[RelationshipType] = mapped_column( Enum(RelationshipType, name="relationship_type"), nullable=False, index=True, ) confidence: Mapped[float] = mapped_column( Float, nullable=False, default=1.0, ) created_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), nullable=False, server_default=func.now(), ) # ------------------------------------------------------------------ # Relationships # ------------------------------------------------------------------ source_item: Mapped["KnowledgeItem"] = relationship( foreign_keys=[source_item_id], back_populates="outgoing_links", ) target_item: Mapped["KnowledgeItem"] = relationship( foreign_keys=[target_item_id], back_populates="incoming_links", ) def __repr__(self) -> str: return ( f" {self.target_item_id})>" )