feat: add concept of (non-)default importers for handler

i.e. when telling handler to "import all" which should be included
vs. excluded by default?
This commit is contained in:
Lance Edgar 2026-03-17 18:24:42 -05:00
parent ae282ab468
commit d7d0768a9c
7 changed files with 148 additions and 15 deletions

View file

@ -177,7 +177,7 @@ class ImportCommandHandler(GenericHandler):
# sort out which models to process # sort out which models to process
models = kw.pop("models", None) models = kw.pop("models", None)
if not models: if not models:
models = list(self.import_handler.importers) models = self.import_handler.get_default_importer_keys()
log.debug( log.debug(
"%s %s for models: %s", "%s %s for models: %s",
self.import_handler.actioning, self.import_handler.actioning,
@ -196,13 +196,31 @@ class ImportCommandHandler(GenericHandler):
This is what happens when command line has ``--list-models``. This is what happens when command line has ``--list-models``.
""" """
sys.stdout.write("\nALL MODELS:\n") all_keys = list(self.import_handler.importers)
default_keys = [k for k in all_keys if self.import_handler.is_default(k)]
extra_keys = [k for k in all_keys if k not in default_keys]
sys.stdout.write("\n")
sys.stdout.write("==============================\n") sys.stdout.write("==============================\n")
for key in self.import_handler.importers: sys.stdout.write(" DEFAULT MODELS:\n")
sys.stdout.write(key)
sys.stdout.write("\n")
sys.stdout.write("==============================\n") sys.stdout.write("==============================\n")
sys.stdout.write(f"for {self.import_handler.get_title()}\n\n") if default_keys:
for key in default_keys:
sys.stdout.write(f"{key}\n")
else:
sys.stdout.write("(none)\n")
sys.stdout.write("==============================\n")
sys.stdout.write(" EXTRA MODELS:\n")
sys.stdout.write("==============================\n")
if extra_keys:
for key in extra_keys:
sys.stdout.write(f"{key}\n")
else:
sys.stdout.write("(none)\n")
sys.stdout.write("==============================\n")
sys.stdout.write(f" for {self.import_handler.get_title()}\n\n")
def import_command_template( # pylint: disable=unused-argument,too-many-arguments,too-many-positional-arguments,too-many-locals def import_command_template( # pylint: disable=unused-argument,too-many-arguments,too-many-positional-arguments,too-many-locals

View file

@ -150,6 +150,14 @@ class ImportExportWarning(EmailSetting):
} }
class export_to_wutta_from_wutta_warning( # pylint: disable=invalid-name
ImportExportWarning
):
"""
Diff warning for Wutta Wutta export.
"""
class import_to_versions_from_wutta_warning( # pylint: disable=invalid-name class import_to_versions_from_wutta_warning( # pylint: disable=invalid-name
ImportExportWarning ImportExportWarning
): ):

View file

@ -371,7 +371,7 @@ class ImportHandler( # pylint: disable=too-many-public-methods,too-many-instanc
changes = OrderedDict() changes = OrderedDict()
if not keys: if not keys:
keys = list(self.importers) keys = self.get_default_importer_keys()
success = False success = False
try: try:
@ -655,6 +655,36 @@ class ImportHandler( # pylint: disable=too-many-public-methods,too-many-instanc
""" """
return kwargs return kwargs
def is_default(self, key):
"""
Return a boolean indicating whether the importer corresponding
to ``key`` should be considered "default" - i.e. included as
part of a typical "import all" job.
The default logic here returns ``True`` in all cases; subclass can
override as needed.
:param key: Key indicating the importer.
:rtype: bool
"""
return True
def get_default_importer_keys(self):
"""
Return the list of importer keys which should be considered
"default" - i.e. which should be included as part of a typical
"import all" job.
This inspects :attr:`importers` and calls :meth:`is_default()`
for each, to determine the result.
:returns: List of importer keys (strings).
"""
keys = list(self.importers)
keys = [k for k in keys if self.is_default(k)]
return keys
def process_changes(self, changes): def process_changes(self, changes):
""" """
Run post-processing operations on the given changes, if Run post-processing operations on the given changes, if

View file

@ -126,6 +126,22 @@ class FromWuttaToWuttaBase(FromWuttaHandler, ToWuttaHandler):
}, },
) )
def is_default(self, key):
""" """
special = [
"Setting",
"Role",
"Permission",
"User",
"UserRole",
"UserAPIToken",
"Upgrade",
]
if key in special:
return False
return True
class FromWuttaToWuttaImport(FromWuttaToWuttaBase): class FromWuttaToWuttaImport(FromWuttaToWuttaBase):
""" """

View file

@ -3,7 +3,7 @@
import inspect import inspect
import sys import sys
from unittest import TestCase from unittest import TestCase
from unittest.mock import patch, Mock from unittest.mock import patch, Mock, call
from wuttasync.cli import base as mod from wuttasync.cli import base as mod
from wuttjamaican.testing import DataTestCase from wuttjamaican.testing import DataTestCase
@ -169,22 +169,60 @@ class TestImportCommandHandler(DataTestCase):
params={}, params={},
), ),
) )
# self.assertRaises(FileNotFoundError, handler.run, ctx)
handler.run(ctx) handler.run(ctx)
exit_.assert_not_called() exit_.assert_not_called()
def test_list_models(self): def test_list_models(self):
# CSV -> Wutta (all importers are default)
handler = self.make_handler( handler = self.make_handler(
import_handler="wuttasync.importing.csv:FromCsvToWutta" import_handler="wuttasync.importing.csv:FromCsvToWutta"
) )
with patch.object(mod, "sys") as sys: with patch.object(mod, "sys") as sys:
handler.list_models({}) handler.list_models({})
# just test a few random things we expect to see sys.stdout.write.assert_has_calls(
self.assertTrue(sys.stdout.write.has_call("ALL MODELS:\n")) [
self.assertTrue(sys.stdout.write.has_call("Person")) call("==============================\n"),
self.assertTrue(sys.stdout.write.has_call("User")) call(" EXTRA MODELS:\n"),
self.assertTrue(sys.stdout.write.has_call("Upgrade")) call("==============================\n"),
call("(none)\n"),
call("==============================\n"),
]
)
# Wutta -> Wutta (only Person importer is default)
handler = self.make_handler(
import_handler="wuttasync.importing.wutta:FromWuttaToWuttaImport"
)
with patch.object(mod, "sys") as sys:
handler.list_models({})
sys.stdout.write.assert_has_calls(
[
call("==============================\n"),
call(" DEFAULT MODELS:\n"),
call("==============================\n"),
call("Person\n"),
call("==============================\n"),
call(" EXTRA MODELS:\n"),
call("==============================\n"),
]
)
# again, but pretend there are no default importers
with patch.object(handler.import_handler, "is_default", return_value=False):
with patch.object(mod, "sys") as sys:
handler.list_models({})
sys.stdout.write.assert_has_calls(
[
call("==============================\n"),
call(" DEFAULT MODELS:\n"),
call("==============================\n"),
call("(none)\n"),
call("==============================\n"),
call(" EXTRA MODELS:\n"),
call("==============================\n"),
]
)
class TestImporterCommand(TestCase): class TestImporterCommand(TestCase):

View file

@ -7,6 +7,7 @@ from uuid import UUID
from wuttjamaican.testing import DataTestCase from wuttjamaican.testing import DataTestCase
from wuttasync.importing import handlers as mod, Importer, ToSqlalchemy from wuttasync.importing import handlers as mod, Importer, ToSqlalchemy
from wuttasync.importing.wutta import FromWuttaToWuttaImport
class FromFooToBar(mod.ImportHandler): class FromFooToBar(mod.ImportHandler):
@ -253,6 +254,25 @@ class TestImportHandler(DataTestCase):
KeyError, handler.get_importer, "BunchOfNonsense", model_class=model.Setting KeyError, handler.get_importer, "BunchOfNonsense", model_class=model.Setting
) )
def test_is_default(self):
handler = self.make_handler()
# nb. anything is considered default, by default
self.assertTrue(handler.is_default("there_is_no_way_this_is_valid"))
def test_get_default_importer_keys(self):
# use handler which already has some non/default keys
handler = FromWuttaToWuttaImport(self.config)
# it supports many importers
self.assertIn("Person", handler.importers)
self.assertIn("User", handler.importers)
self.assertIn("Setting", handler.importers)
# but only Person is default
keys = handler.get_default_importer_keys()
self.assertEqual(keys, ["Person"])
def test_get_warnings_email_key(self): def test_get_warnings_email_key(self):
handler = FromFooToBar(self.config) handler = FromFooToBar(self.config)

View file

@ -85,6 +85,9 @@ class TestEmailSettings(ImportExportWarningTestCase):
return config return config
def test_export_to_wutta_from_wutta_warning(self):
self.do_test_preview("export_to_wutta_from_wutta_warning")
def test_import_to_versions_from_wutta_warning(self): def test_import_to_versions_from_wutta_warning(self):
self.do_test_preview("import_to_versions_from_wutta_warning") self.do_test_preview("import_to_versions_from_wutta_warning")