Quellcode für meipi.indexing.config

"""Konfiguration der App

Neben der hier definierten default-Konfiguration,
können die Werte auch über eine .env-Datei oder direkt über Umgebungsvariablen überschrieben werden.
Die .env-Datei sollte im Root-Verzeichnis der App liegen und den Namen "config.env" tragen.

Die globale Instanz ``appconf`` wird einmal beim Import von ``meipi.indexing`` erzeugt.
``CONFIG_PATH`` (``MEIPI_CONFIG_ENV`` oder ``config.env``) wird dabei einmalig festgelegt.
Einzelne Felder können zur Laufzeit gesetzt werden (z. B. ``--docroot`` in der CLI).
Änderungen an der Env-Datei auf dem Datenträger oder an ``MEIPI_CONFIG_ENV`` erfordern einen
Prozess-Neustart.

Beispiel für eine ``.env-Datei``::

    IND_PG_HOST=localhost                   #PostgreSQL Host
    IND_PG_PORT=5432                        #PostgreSQL Port
    IND_PG_USER=postgres                    #PostgreSQL Username
    IND_PG_DATABASE=postgres                #PostgreSQL Database Name
    IND_PG_API_KEY=pg-docker                #API-Key-Name für das DB-Passwort im Keyring
    IND_DOCROOT=/home/rslsync/folders/  #Dokumenten-Root-Verzeichnis
    IND_LOGGER_NAME=sqlalchemy.engine          #Name des Loggers für SQLAlchemy
    IND_DOCSUF='{
        ".pdf",
        ".txt",
        ".md",
        ".docx",
        ".doc",
        ".html",
        ".htm",
        ".epub",
        ".odt",
    }  #zulässige Dokumentenerweiterungen
    IND_PICSUF='{
        ".jpg",
        ".jpeg",
        ".bmp",
        ".png",
        ".heic",
        ".tiff",
        ".tif"
    }  #zulässige Bild-Dateiendungen
    IND_VIDSUF='{
        ".mov",
        ".vob",
        ".mkv",
        ".avi",
        ".mp4",
        ".mcf"
    }  #zulässige Video-Dateiendungen
    IND_LOGLEVEL=20  #Log-Level (z.B. 10=DEBUG, 20=INFO, 30=WARNING, 40=ERROR, 50=CRITICAL)

Das DB-Passwort wird aus dem Keyring geholt, der API-Key-Name
kann über die Umgebungsvariable IND_PG_API_KEY konfiguriert werden (Standard: "pg-docker").

Alternatives Env-File vor dem Start setzen::

    export MEIPI_CONFIG_ENV=/path/to/config.env
    meipi-index schema-info
"""

from typing import Set
import logging
import os
from pydantic import Field
from pydantic_settings import BaseSettings, SettingsConfigDict
from sqlalchemy import MetaData, URL as saURL
#from pillow_heif import register_heif_opener
#from PIL import ImageFile

[Doku] class FTYPE(): DOC:str = "doc" PIC:str = "pic" VID:str = "vid" UNK:str = "unk"
CONFIG_PATH = os.environ.get("MEIPI_CONFIG_ENV", "config.env")
[Doku] class Config(BaseSettings): """Enthält die Konfiguration der App Zur Laufzeit gilt ``appconf``; Felder können in-process geändert werden (z. B. CLI-Overrides). Das PostgreSQL-Schema wird über ``pg_schema`` (``search_path``) gesteuert, nicht über qualifizierte Tabellennamen in den ORM-``Table``-Objekten. """ config_path: str = Field( default=CONFIG_PATH, description="Path to the env file used when this config was loaded", ) model_config = SettingsConfigDict( env_file=CONFIG_PATH, env_file_encoding="utf-8", env_prefix="IND_", case_sensitive=False, ) pg_host: str = Field(default="localhost") pg_port: str = Field(default="5432") pg_user: str = Field(default="postgres") pg_passwd: str = Field(default="postgres") pg_database: str = Field(default="postgres") pg_schema: str = Field(default="public") pg_api_key: str = Field(default="pg-docker") tika_noocr_url: str = Field(default="http://localhost:9998") tika_ocrurl: str = Field(default="http://localhost:9997") docroot: str = Field( default=".", description="Filesystem root; file paths in the DB are relative to this directory", ) docsuf: Set[str] = Field(default={".pdf",".txt",".md",".docx",".doc", ".html",".htm",".epub",".odt", ".xlsx", ".xls", ".csv"}) picsuf: Set[str] = Field(default={".jpg", ".jpeg", ".bmp", ".png", ".heic", ".tiff", ".tif"}) vidsuf: Set[str] = Field(default={".mov", ".vob", ".mkv", ".avi", ".mp4", ".mcf"}) logger_name: str = "sqlalchemy.engine" loglevel: int = logging.INFO @property def logger(self): return logging.getLogger(self.logger_name) @property def metadata(self) -> MetaData: """Shared ORM ``MetaData`` (tables are unqualified; schema via ``pg_schema`` / search_path).""" from meipi.indexing.model import orm_metadata return orm_metadata
[Doku] def resolved_docroot(self) -> str: """Absolute path to the configured filesystem root.""" return os.path.abspath(self.docroot)
[Doku] def db_passwd_from_keyring(self) -> str: """DB password from SecretService keyring when D-Bus is available, else ``pg_passwd``.""" if not os.environ.get("DBUS_SESSION_BUS_ADDRESS"): return self.pg_passwd try: import keyring from keyring.backends.SecretService import Keyring as SecretServiceKeyring keyring.set_keyring(SecretServiceKeyring()) pwd = keyring.get_password("API-Keys", self.pg_api_key) if pwd is not None: return pwd self.logger.warning( "Kein Passwort für API-Key '%s' im Keyring gefunden, verwende pg_passwd aus Konfiguration", self.pg_api_key, ) except Exception as exc: self.logger.warning( "Keyring nicht verfügbar (%s), verwende pg_passwd aus Konfiguration", exc, ) return self.pg_passwd
@property def db_conn_URL(self) -> saURL: """DB-Verbindungsstring aus den Konfigurationswerten zusammensetzen""" pg_passwd = self.db_passwd_from_keyring() return saURL.create( "postgresql+psycopg", username=self.pg_user, password=pg_passwd, host=self.pg_host, port=int(self.pg_port), database=self.pg_database, )
[Doku] def get_ftype(self, suf: str) -> str: """Gibt den konfigurierten Dateityp zurück, basierend auf der Dateiendung""" _suf = suf.lower() if _suf in self.docsuf: return FTYPE.DOC elif _suf in self.picsuf: return FTYPE.PIC elif _suf in self.vidsuf: return FTYPE.VID else: return FTYPE.UNK
[Doku] def resolve_config(config: Config | None) -> Config: """Return *config* or the process-wide ``appconf``.""" if config is None: import meipi.indexing as indexing_pkg return indexing_pkg.appconf return config
[Doku] def install_appconf(config: Config) -> None: """Replace the process-wide ``appconf``.""" import sys import meipi.indexing as indexing_pkg indexing_pkg.appconf = config # type: ignore[misc, assignment] for mod in list(sys.modules.values()): if mod is None: continue name = getattr(mod, "__name__", "") if name.startswith("meipi.indexing") and hasattr(mod, "appconf"): mod.appconf = config #type: ignore[assignment]