bifroest/app/documents/models.py
2026-10-27 08:34:17 +01:00

52 lines
2.7 KiB
Python

"""Abgeglichene Ordner: Dokumente vom Rechner einer Person, die sich regelmäßig ändern.
Der Browser gleicht einen lokalen Ordner mit Bifröst ab (nur Neues und Geändertes); gespeichert wird der
ausgelesene Text, nicht das Original. Office-Dokumente, PDF und Bilder liest ein zentraler MCP-Server
(Rolle „documents“) aus, Textdateien Bifröst selbst. Sichtbar je Ordner: nur für die Person, die abgleicht
(nur ihre eigenen Runs in ihren eigenen Chats), oder für den ganzen Workspace (mit Bestätigung).
"""
from datetime import datetime
from sqlalchemy import BigInteger, Computed, ForeignKey, Identity, Index, String, Text, UniqueConstraint
from sqlalchemy.dialects.postgresql import TSVECTOR
from sqlalchemy.orm import Mapped, mapped_column
from app.core.database import Base, now
PRIVATE, WORKSPACE = "private", "workspace"
# Datei: wartet auf Auslese | ausgelesen | Fehler | Format ohne Auslese
PENDING, OK, ERROR, UNSUPPORTED = "pending", "ok", "error", "unsupported"
class DocFolder(Base):
__tablename__ = "doc_folders"
id: Mapped[int] = mapped_column(primary_key=True)
workspace_id: Mapped[int] = mapped_column(ForeignKey("workspaces.id", ondelete="CASCADE"), index=True)
owner_id: Mapped[int] = mapped_column(ForeignKey("users.id", ondelete="CASCADE"), index=True)
name: Mapped[str] = mapped_column(String(100))
scope: Mapped[str] = mapped_column(String(20), default=PRIVATE) # private | workspace
exposure_confirmed: Mapped[bool] = mapped_column(default=False)
synced: Mapped[datetime | None] # letzter vollständiger Abgleich
created: Mapped[datetime] = mapped_column(default=now)
class DocFile(Base):
__tablename__ = "doc_files"
__table_args__ = (UniqueConstraint("folder_id", "path"),
Index("ix_doc_files_search", "search", postgresql_using="gin"))
id: Mapped[int] = mapped_column(BigInteger, Identity(), primary_key=True)
folder_id: Mapped[int] = mapped_column(ForeignKey("doc_folders.id", ondelete="CASCADE"), index=True)
path: Mapped[str] = mapped_column(String(500)) # relativ zum Ordner
size: Mapped[int] = mapped_column(BigInteger)
modified: Mapped[int] = mapped_column(BigInteger) # lastModified im Browser (ms); erkennt Änderungen
sha256: Mapped[str] = mapped_column(String(64))
status: Mapped[str] = mapped_column(String(20), default=PENDING)
error: Mapped[str] = mapped_column(Text, default="")
text: Mapped[str] = mapped_column(Text, default="")
# Volltextsuche über Pfad und Inhalt (deutsche Stammformen)
search = mapped_column(TSVECTOR, Computed("to_tsvector('german', coalesce(path, '') || ' ' || coalesce(text, ''))",
persisted=True))
updated: Mapped[datetime] = mapped_column(default=now)