diff --git a/memos/config.py b/memos/config.py new file mode 100644 index 0000000..3b9c04a --- /dev/null +++ b/memos/config.py @@ -0,0 +1,11 @@ +import os + +# Define the default database path +DEFAULT_DB_PATH = os.path.expanduser("~/.memos/database.db") + +# Function to get the database path from environment variable or default +def get_database_path(): + return os.getenv("MEMOS_DATABASE_PATH", DEFAULT_DB_PATH) + +# Ensure the directory exists +os.makedirs(os.path.dirname(DEFAULT_DB_PATH), exist_ok=True) \ No newline at end of file diff --git a/memos/models.py b/memos/models.py new file mode 100644 index 0000000..563287f --- /dev/null +++ b/memos/models.py @@ -0,0 +1,112 @@ +from sqlalchemy import ( + create_engine, + Integer, + String, + Text, + DateTime, + Enum +) +from datetime import datetime +from sqlalchemy.orm import relationship, DeclarativeBase, Mapped, mapped_column +from enum import Enum as PyEnum +from typing import List +from .config import get_database_path + + +class MetadataSource(PyEnum): + USER_GENERATED = "user_generated" + SYSTEM_GENERATED = "system_generated" + PLUGIN_GENERATED = "plugin_generated" + + +class MetadataType(PyEnum): + EXTRACONTENT = "extra_content" + ATTRIBUTE = "attribute" + + +class Base(DeclarativeBase): + pass + + +class Library(Base): + __tablename__ = "libraries" + id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True) + name: Mapped[str] = mapped_column(String, nullable=False) + folders: Mapped[List["Folder"]] = relationship("Folder", back_populates="library") + plugins: Mapped[List["Plugin"]] = relationship("LibraryPlugin", back_populates="library") + + +class Folder(Base): + __tablename__ = "folders" + id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True) + path: Mapped[str] = mapped_column(String, nullable=False) + library_id: Mapped[int] = mapped_column(Integer, nullable=False) + library: Mapped["Library"] = relationship("Library", back_populates="folders") + entities: Mapped[List["Entity"]] = relationship("Entity", back_populates="folder") + + +class Entity(Base): + __tablename__ = "entities" + id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True) + path: Mapped[str] = mapped_column(String, nullable=False) + filename: Mapped[str] = mapped_column(String, nullable=False) + size: Mapped[int] = mapped_column(Integer, nullable=False) + created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False) + last_modified_at: Mapped[datetime] = mapped_column(DateTime, nullable=False) + filetype: Mapped[str] = mapped_column(String, nullable=False) + last_scan_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True) + folder_id: Mapped[int] = mapped_column(Integer, nullable=False) + folder: Mapped["Folder"] = relationship("Folder", back_populates="entities") + metadata_entries: Mapped[List["EntityMetadata"]] = relationship("EntityMetadata", back_populates="entity") + + +class Tag(Base): + __tablename__ = "tags" + id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True) + name: Mapped[str] = mapped_column(String, nullable=False) + description: Mapped[str | None] = mapped_column(Text, nullable=True) + color: Mapped[str | None] = mapped_column(String, nullable=True) + created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False) + + +class EntityTag(Base): + __tablename__ = "entity_tags" + id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True) + entity_id: Mapped[int] = mapped_column(Integer, nullable=False) + tag_id: Mapped[int] = mapped_column(Integer, nullable=False) + source: Mapped[MetadataSource] = mapped_column(Enum(MetadataSource), nullable=False) + created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False) + + +class EntityMetadata(Base): + __tablename__ = "metadata_entries" + id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True) + entity_id: Mapped[int] = mapped_column(Integer, nullable=False) + key: Mapped[str] = mapped_column(String, nullable=False) + value: Mapped[str] = mapped_column(Text, nullable=False) + source: Mapped[MetadataSource] = mapped_column(Enum(MetadataSource), nullable=False) + datetype: Mapped[MetadataType] = mapped_column(Enum(MetadataType), nullable=False) + entity = relationship("Entity", back_populates="metadata_entries") + + +class Plugin(Base): + __tablename__ = "plugins" + id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True) + name: Mapped[str] = mapped_column(String, nullable=False) + description: Mapped[str | None] = mapped_column(Text, nullable=True) + webhook_url: Mapped[str] = mapped_column(String, nullable=False) + libraries = relationship("LibraryPlugin", back_populates="plugin") + + +class LibraryPlugin(Base): + __tablename__ = "library_plugins" + id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True) + library_id: Mapped[int] = mapped_column(Integer, nullable=False) + plugin_id: Mapped[int] = mapped_column(Integer, nullable=False) + library = relationship("Library", back_populates="plugins") + plugin = relationship("Plugin", back_populates="libraries") + + +# Create the database engine with the path from config +engine = create_engine(f"sqlite:///{get_database_path()}", echo=True) +Base.metadata.create_all(engine)