Source code for wuttasync.importing.wutta

# -*- coding: utf-8; -*-
################################################################################
#
#  WuttaSync -- Wutta Framework for data import/export and real-time sync
#  Copyright © 2024-2026 Lance Edgar
#
#  This file is part of Wutta Framework.
#
#  Wutta Framework is free software: you can redistribute it and/or modify it
#  under the terms of the GNU General Public License as published by the Free
#  Software Foundation, either version 3 of the License, or (at your option) any
#  later version.
#
#  Wutta Framework is distributed in the hope that it will be useful, but
#  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
#  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
#  more details.
#
#  You should have received a copy of the GNU General Public License along with
#  Wutta Framework.  If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
Wutta → Wutta import/export
"""

from collections import OrderedDict

from sqlalchemy_utils.functions import get_primary_keys

from wuttjamaican.db.util import make_topo_sortkey

from .base import FromSqlalchemyMirror
from .model import ToWutta
from .handlers import FromWuttaHandler, ToWuttaHandler, Orientation


[docs] class FromWuttaMirror(FromSqlalchemyMirror): # pylint: disable=abstract-method """ Base class for Wutta → Wutta data :term:`importers/exporters <importer>`. This inherits from :class:`~wuttasync.importing.base.FromSqlalchemyMirror`. """
[docs] class FromWuttaToWuttaBase(FromWuttaHandler, ToWuttaHandler): """ Base class for Wutta → Wutta data :term:`import/export handlers <import handler>`. This inherits from :class:`~wuttasync.importing.handlers.FromWuttaHandler` and :class:`~wuttasync.importing.handlers.ToWuttaHandler`. """ dbkey = None """ Config key for the "other" (non-local) :term:`app database`. Depending on context this will represent either the source or target for import/export. """ def get_target_model(self): # pylint: disable=missing-function-docstring return self.app.model # TODO: pylint (correctly) flags this as duplicate code, matching # on the wuttasync.importing.csv/versions module - should fix?
[docs] def define_importers(self): """ This overrides typical (manual) importer definition, and instead dynamically generates a set of importers, e.g. one per table in the target DB. It does this by calling :meth:`make_importer_factory()` for each class found in the :term:`app model`. """ importers = {} model = self.get_target_model() # pylint: disable=duplicate-code # mostly try to make an importer for every data model for name in dir(model): cls = getattr(model, name) if ( isinstance(cls, type) and issubclass(cls, model.Base) and cls is not model.Base ): importers[name] = self.make_importer_factory(cls, name) # sort importers according to schema topography topo_sortkey = make_topo_sortkey(model) importers = OrderedDict( [(name, importers[name]) for name in sorted(importers, key=topo_sortkey)] ) return importers
[docs] def make_importer_factory(self, model_class, name): """ Generate and return a new :term:`importer` class, targeting the given :term:`data model` class. The newly-created class will inherit from: * :class:`FromWuttaMirror` * :class:`~wuttasync.importing.model.ToWutta` :param model_class: A data model class. :param name: The "model name" for the importer/exporter. New class name will be based on this, so e.g. ``Widget`` model name becomes ``WidgetImporter`` class name. :returns: The new class, meant to process import/export targeting the given data model. """ return type( f"{name}Importer", (FromWuttaMirror, ToWutta), { "model_class": model_class, "key": list(get_primary_keys(model_class)), }, )
def is_default(self, key): # pylint: disable=empty-docstring """ """ special = [ "Setting", "Role", "Permission", "User", "UserRole", "UserAPIToken", "Upgrade", ] if key in special: return False return True
[docs] class FromWuttaToWuttaImport(FromWuttaToWuttaBase): """ Handler for Wutta (other) → Wutta (local) data import. This inherits from :class:`FromWuttaToWuttaBase`. """ orientation = Orientation.IMPORT """ """ # nb. suppress docs
[docs] def make_source_session(self): """ This makes a "normal" :term:`db session`, but will use the engine corresponding to the :attr:`~FromWuttaToWuttaBase.dbkey`. """ if ( not self.dbkey or self.dbkey == "default" or self.dbkey not in self.config.appdb_engines ): raise ValueError(f"dbkey is not valid: {self.dbkey}") engine = self.config.appdb_engines[self.dbkey] return self.app.make_session(bind=engine)
[docs] class FromWuttaToWuttaExport(FromWuttaToWuttaBase): """ Handler for Wutta (local) → Wutta (other) data export. This inherits from :class:`FromWuttaToWuttaBase`. """ orientation = Orientation.EXPORT """ """ # nb. suppress docs
[docs] def make_target_session(self): """ This makes a "normal" :term:`db session`, but will use the engine corresponding to the :attr:`~FromWuttaToWuttaBase.dbkey`. """ if ( not self.dbkey or self.dbkey == "default" or self.dbkey not in self.config.appdb_engines ): raise ValueError(f"dbkey is not valid: {self.dbkey}") engine = self.config.appdb_engines[self.dbkey] return self.app.make_session(bind=engine)