diff --git a/modelbaker/dbconnector/db_connector.py b/modelbaker/dbconnector/db_connector.py index 45dc00c..08acd86 100644 --- a/modelbaker/dbconnector/db_connector.py +++ b/modelbaker/dbconnector/db_connector.py @@ -343,12 +343,29 @@ def get_classes_relevance(self): """ return [] - def create_basket(self, dataset_tid, topic, tilitid_value=None): + def create_basket( + self, dataset_tid, topic, tilitid_value=None, attachment_key="modelbaker" + ): """ Returns the state and the errormessage """ return False, None + def edit_basket(self, basket_config: dict) -> tuple[bool, str]: + """ + Returns the state and the errormessage + + The basket_config must have the following keys: + + dataset_t_id + datasetname + topic + bid_value + attachmentkey + basket_t_id + """ + return False, None + def get_tid_handling(self): """ Returns `True` if a tid handling is enabled according to the settings table (when the database has been created with `--createTidCol`). diff --git a/modelbaker/dbconnector/gpkg_connector.py b/modelbaker/dbconnector/gpkg_connector.py index 7654471..c549c8c 100644 --- a/modelbaker/dbconnector/gpkg_connector.py +++ b/modelbaker/dbconnector/gpkg_connector.py @@ -54,6 +54,7 @@ def __init__(self, uri, schema): self.tid = "T_Id" self.tilitid = "T_Ili_Tid" self.dispName = "dispName" + self.attachmentKey = "attachmentKey" self.basket_table_name = GPKG_BASKET_TABLE self.dataset_table_name = GPKG_DATASET_TABLE @@ -915,7 +916,9 @@ def get_classes_relevance(self): return contents return [] - def create_basket(self, dataset_tid, topic, tilitid_value=None): + def create_basket( + self, dataset_tid, topic, tilitid_value=None, attachment_key="modelbaker" + ): if self._table_exists(GPKG_BASKET_TABLE): cursor = self.conn.cursor() cursor.execute( @@ -947,7 +950,7 @@ def create_basket(self, dataset_tid, topic, tilitid_value=None): cursor.execute( """ INSERT INTO "{basket_table}" ("{tid_name}", dataset, topic, "{tilitid_name}", attachmentkey ) - VALUES (?, ?, ?, ?, 'modelbaker') + VALUES (?, ?, ?, ?, ?) """.format( tid_name=self.tid, tilitid_name=self.tilitid, @@ -958,6 +961,7 @@ def create_basket(self, dataset_tid, topic, tilitid_value=None): dataset_tid, topic, tilitid_value, + attachment_key, ), ) self.conn.commit() @@ -973,6 +977,47 @@ def create_basket(self, dataset_tid, topic, tilitid_value=None): ).format(topic, error_message) return False, self.tr('Could not create basket for topic "{}".').format(topic) + def edit_basket(self, basket_config: dict) -> tuple[bool, str]: + if self._table_exists(GPKG_BASKET_TABLE): + cursor = self.conn.cursor() + try: + cursor.execute( + """ + UPDATE {basket_table} + SET dataset = ?, + {t_ili_tid} = ?, + {attachment_key} = ? + WHERE {tid_name} = ? + """.format( + basket_table=GPKG_BASKET_TABLE, + t_ili_tid=self.tilitid, + attachment_key=self.attachmentKey, + tid_name=self.tid, + ), + ( + basket_config["dataset_t_id"], + basket_config["bid_value"], + basket_config["attachmentkey"], + basket_config["basket_t_id"], + ), + ) + self.conn.commit() + cursor.close() + return True, self.tr( + 'Successfully edited basket for topic "{}" and dataset "{}".' + ).format(basket_config["topic"], basket_config["datasetname"]) + except sqlite3.Error as e: + cursor.close() + error_message = " ".join(e.args) + return False, self.tr( + 'Could not edit basket for topic "{}" and dataset "{}": {}' + ).format( + basket_config["topic"], basket_config["datasetname"], error_message + ) + return False, self.tr( + 'Could not edit basket for topic "{}" and dataset "{}"' + ).format(basket_config["topic"], basket_config["datasetname"]) + def get_tid_handling(self): if self._table_exists(GPKG_SETTINGS_TABLE): cursor = self.conn.cursor() diff --git a/modelbaker/dbconnector/mssql_connector.py b/modelbaker/dbconnector/mssql_connector.py index fa7bbb6..85808bf 100644 --- a/modelbaker/dbconnector/mssql_connector.py +++ b/modelbaker/dbconnector/mssql_connector.py @@ -51,6 +51,7 @@ def __init__(self, uri, schema): self.iliCodeName = "iliCode" self.tid = "T_Id" self.tilitid = "T_Ili_Tid" + self.attachmentKey = "attachmentkey" self.dispName = "dispName" self.basket_table_name = BASKET_TABLE self.dataset_table_name = DATASET_TABLE @@ -1053,7 +1054,9 @@ def get_classes_relevance(self): result = self._get_dict_result(cur) return result - def create_basket(self, dataset_tid, topic, tilitid_value=None): + def create_basket( + self, dataset_tid, topic, tilitid_value=None, attachment_key="modelbaker" + ): if self.schema and self._table_exists(BASKET_TABLE): cur = self.conn.cursor() cur.execute( @@ -1080,7 +1083,7 @@ def create_basket(self, dataset_tid, topic, tilitid_value=None): cur.execute( """ INSERT INTO {schema}.{basket_table} ({tid_name}, dataset, topic, {tilitid_name}, attachmentkey ) - VALUES (NEXT VALUE FOR {schema}.{sequence}, {dataset_tid}, '{topic}', {tilitid}, 'modelbaker') + VALUES (NEXT VALUE FOR {schema}.{sequence}, {dataset_tid}, '{topic}', {tilitid}, {attachment_key}) """.format( schema=self.schema, sequence="t_ili2db_seq", @@ -1090,6 +1093,7 @@ def create_basket(self, dataset_tid, topic, tilitid_value=None): dataset_tid=dataset_tid, topic=topic, tilitid=tilitid_value, + attachment_key=attachment_key, ) ) self.conn.commit() @@ -1103,6 +1107,46 @@ def create_basket(self, dataset_tid, topic, tilitid_value=None): ).format(topic, error_message) return False, self.tr('Could not create basket for topic "{}".').format(topic) + def edit_basket(self, basket_config: dict) -> tuple[bool, str]: + if self.schema and self._table_exists(BASKET_TABLE): + cur = self.conn.cursor() + try: + cur.execute( + """ + UPDATE {schema}.{basket_table} + SET dataset = ?, + {t_ili_tid} = ?, + {attachment_key} = ? + WHERE {tid_name} = ? + """.format( + schema=self.schema, + basket_table=BASKET_TABLE, + t_ili_tid=self.tilitid, + attachment_key=self.attachmentKey, + tid_name=self.tid, + ), + ( + basket_config["dataset_t_id"], + basket_config["bid_value"], + basket_config["attachmentkey"], + basket_config["basket_t_id"], + ), + ) + self.conn.commit() + return True, self.tr( + 'Successfully edited basket for topic "{}" and dataset "{}".' + ).format(basket_config["topic"], basket_config["datasetname"]) + except pyodbc.errors.Error as e: + error_message = " ".join(e.args) + return False, self.tr( + 'Could not edit basket for topic "{}" and dataset "{}": {}' + ).format( + basket_config["topic"], basket_config["datasetname"], error_message + ) + return False, self.tr( + 'Could not edit basket for topic "{}" and dataset "{}"' + ).format(basket_config["topic"], basket_config["datasetname"]) + def get_tid_handling(self): if self.schema and self._table_exists(SETTINGS_TABLE): cur = self.conn.cursor() diff --git a/modelbaker/dbconnector/pg_connector.py b/modelbaker/dbconnector/pg_connector.py index 2b72824..78d0517 100644 --- a/modelbaker/dbconnector/pg_connector.py +++ b/modelbaker/dbconnector/pg_connector.py @@ -49,6 +49,7 @@ def __init__(self, uri, schema): self.iliCodeName = "ilicode" self.tid = "t_id" self.tilitid = "t_ili_tid" + self.attachmentKey = "attachmentkey" self.dispName = "dispname" self.basket_table_name = PG_BASKET_TABLE self.dataset_table_name = PG_DATASET_TABLE @@ -1100,7 +1101,9 @@ def get_classes_relevance(self): return cur.fetchall() return [] - def create_basket(self, dataset_tid, topic, tilitid_value=None): + def create_basket( + self, dataset_tid, topic, tilitid_value=None, attachment_key="modelbaker" + ): if self.schema and self._table_exists(PG_BASKET_TABLE): cur = self.conn.cursor() cur.execute( @@ -1128,7 +1131,7 @@ def create_basket(self, dataset_tid, topic, tilitid_value=None): sql.SQL( """ INSERT INTO {schema}.{basket_table} ({tid_name}, dataset, topic, {tilitid_name}, attachmentkey) - VALUES (nextval(%s), %s, %s, %s, 'modelbaker') + VALUES (nextval(%s), %s, %s, %s, %s) """ ).format( schema=sql.Identifier(self.schema), @@ -1141,6 +1144,7 @@ def create_basket(self, dataset_tid, topic, tilitid_value=None): dataset_tid, topic, tilitid_value, + attachment_key, ), ) self.conn.commit() @@ -1154,6 +1158,48 @@ def create_basket(self, dataset_tid, topic, tilitid_value=None): ).format(topic, error_message) return False, self.tr('Could not create basket for topic "{}".').format(topic) + def edit_basket(self, basket_config: dict) -> tuple[bool, str]: + if self.schema and self._table_exists(PG_BASKET_TABLE): + cur = self.conn.cursor() + try: + cur.execute( + sql.SQL( + """ + UPDATE {schema}.{basket_table} + SET dataset = %s, + {t_ili_tid} = %s, + {attachment_key} = %s + WHERE {tid_name} = %s + """ + ).format( + schema=sql.Identifier(self.schema), + basket_table=sql.Identifier(PG_BASKET_TABLE), + t_ili_tid=sql.Identifier(self.tilitid), + attachment_key=sql.Identifier(self.attachmentKey), + tid_name=sql.Identifier(self.tid), + ), + ( + basket_config["dataset_t_id"], + basket_config["bid_value"], + basket_config["attachmentkey"], + basket_config["basket_t_id"], + ), + ) + self.conn.commit() + return True, self.tr( + 'Successfully edited basket for topic "{}" and dataset "{}".' + ).format(basket_config["topic"], basket_config["datasetname"]) + except psycopg2.errors.Error as e: + error_message = " ".join(e.args) + return False, self.tr( + 'Could not edit basket for topic "{}" and dataset "{}": {}' + ).format( + basket_config["topic"], basket_config["datasetname"], error_message + ) + return False, self.tr( + 'Could not edit basket for topic "{}" and dataset "{}"' + ).format(basket_config["topic"], basket_config["datasetname"]) + def get_tid_handling(self): if self.schema and self._table_exists(PG_SETTINGS_TABLE): cur = self.conn.cursor() diff --git a/modelbaker/iliwrapper/ili2dbconfig.py b/modelbaker/iliwrapper/ili2dbconfig.py index 2dde42e..00c6b3b 100644 --- a/modelbaker/iliwrapper/ili2dbconfig.py +++ b/modelbaker/iliwrapper/ili2dbconfig.py @@ -111,31 +111,35 @@ def ilidata_directories(self): class Ili2DbCommandConfiguration: - def __init__(self): - self.base_configuration = BaseConfiguration() - - self.dbport = "" - self.dbhost = "" - self.dbpwd = "" - self.dbusr = "" - self.dbauthid = "" - self.db_use_super_login = False - self.database = "" - self.dbschema = "" - self.dbfile = "" - self.dbservice = None - self.sslmode = None - self.tool = None - self.ilifile = "" - self.ilimodels = "" - self.tomlfile = "" - self.dbinstance = "" - self.db_odbc_driver = "" - self.disable_validation = False - self.metaconfig = None - self.metaconfig_id = None - self.metaconfig_params_only = False - self.db_ili_version = None + def __init__(self, other=None): + if not isinstance(other, Ili2DbCommandConfiguration): + self.base_configuration = BaseConfiguration() + + self.dbport = "" + self.dbhost = "" + self.dbpwd = "" + self.dbusr = "" + self.dbauthid = "" + self.db_use_super_login = False + self.database = "" + self.dbschema = "" + self.dbfile = "" + self.dbservice = None + self.sslmode = None + self.tool = None + self.ilifile = "" + self.ilimodels = "" + self.tomlfile = "" + self.dbinstance = "" + self.db_odbc_driver = "" + self.disable_validation = False + self.metaconfig = None + self.metaconfig_id = None + self.metaconfig_params_only = False + self.db_ili_version = None + else: + # We got an 'other' object from which we'll get parameters + self.__dict__ = other.__dict__.copy() def append_args(self, args, values, consider_metaconfig=False, force_append=False): @@ -186,8 +190,8 @@ def to_ili2db_args(self): class ExportConfiguration(Ili2DbCommandConfiguration): - def __init__(self): - super().__init__() + def __init__(self, other: Ili2DbCommandConfiguration = None): + super().__init__(other) self.xtffile = "" self.with_exporttid = False self.iliexportmodels = "" @@ -228,8 +232,8 @@ def to_ili2db_args(self, extra_args=[], with_action=True): class SchemaImportConfiguration(Ili2DbCommandConfiguration): - def __init__(self): - super().__init__() + def __init__(self, other: Ili2DbCommandConfiguration = None): + super().__init__(other) self.inheritance = "smart1" self.create_basket_col = False self.create_import_tid = True @@ -323,8 +327,8 @@ def to_ili2db_args(self, extra_args=[], with_action=True): class ImportDataConfiguration(SchemaImportConfiguration): - def __init__(self): - super().__init__() + def __init__(self, other: Ili2DbCommandConfiguration = None): + super().__init__(other) self.xtffile = "" self.delete_data = False self.with_importtid = False @@ -376,8 +380,8 @@ def to_ili2db_args(self, extra_args=[], with_action=True): class UpdateDataConfiguration(Ili2DbCommandConfiguration): - def __init__(self): - super().__init__() + def __init__(self, other: Ili2DbCommandConfiguration = None): + super().__init__(other) self.xtffile = "" self.dataset = "" self.delete_data = False @@ -414,8 +418,8 @@ def to_ili2db_args(self, extra_args=[], with_action=True): class ValidateConfiguration(Ili2DbCommandConfiguration): - def __init__(self): - super().__init__() + def __init__(self, other: Ili2DbCommandConfiguration = None): + super().__init__(other) self.ilimodels = "" self.topics = "" self.dataset = "" @@ -472,3 +476,23 @@ def to_ili2db_args(self, extra_args=[], with_action=True): self.append_args(args, Ili2DbCommandConfiguration.to_ili2db_args(self)) return args + + +class DeleteConfiguration(Ili2DbCommandConfiguration): + def __init__(self, other: Ili2DbCommandConfiguration = None): + super().__init__(other) + self.dataset = "" + + def to_ili2db_args(self, extra_args=[], with_action=True): + args = list() + + if with_action: + self.append_args(args, ["--delete"]) + + self.append_args(args, extra_args) + + self.append_args(args, ["--dataset", self.dataset]) + + self.append_args(args, Ili2DbCommandConfiguration.to_ili2db_args(self)) + + return args diff --git a/modelbaker/iliwrapper/ilideleter.py b/modelbaker/iliwrapper/ilideleter.py new file mode 100644 index 0000000..1f10bfa --- /dev/null +++ b/modelbaker/iliwrapper/ilideleter.py @@ -0,0 +1,28 @@ +""" +/*************************************************************************** + ------------------- + begin : 27/09/24 + git sha : :%H$ + copyright : (C) 2024 by Germán Carrillo + email : german@opengis.ch + ***************************************************************************/ + +/*************************************************************************** + * * + * This program 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 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +""" +from .ili2dbconfig import DeleteConfiguration, Ili2DbCommandConfiguration +from .iliexecutable import IliExecutable + + +class Deleter(IliExecutable): + def __init__(self, parent=None): + super().__init__(parent) + + def _create_config(self) -> Ili2DbCommandConfiguration: + return DeleteConfiguration() diff --git a/modelbaker/utils/ili2db_utils.py b/modelbaker/utils/ili2db_utils.py new file mode 100644 index 0000000..f6eb1ac --- /dev/null +++ b/modelbaker/utils/ili2db_utils.py @@ -0,0 +1,94 @@ +""" +/*************************************************************************** + ------------------- + begin : 27.09.2024 + git sha : :%H$ + copyright : (C) 2024 by Germán Carrillo + email : german at opengis ch + ***************************************************************************/ + +/*************************************************************************** + * * + * This program 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 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +""" +from qgis.PyQt.QtCore import QObject, Qt, pyqtSignal + +from ..iliwrapper import ilideleter +from ..iliwrapper.ili2dbconfig import DeleteConfiguration, Ili2DbCommandConfiguration +from ..iliwrapper.ili2dbutils import JavaNotFoundError +from ..utils.qt_utils import OverrideCursor + + +class Ili2DbUtils(QObject): + """ + Execute ili2db operations via Model Baker Library + """ + + stdout = pyqtSignal(str) + stderr = pyqtSignal(str) + process_started = pyqtSignal(str) + process_finished = pyqtSignal(int, int) + log_on_error = pyqtSignal(str) + + def __init__(self): + QObject.__init__(self) + + self._log = "" + + def delete_dataset( + self, dataset: str, configuration: Ili2DbCommandConfiguration = None + ): + deleter = ilideleter.Deleter() + deleter.tool = configuration.tool + deleter.configuration = DeleteConfiguration(configuration) + deleter.configuration.dataset = dataset + + with OverrideCursor(Qt.WaitCursor): + self._connect_ili_executable_signals(deleter) + self._log = "" + + res = True + msg = self.tr("Dataset '{}' successfully deleted!").format(dataset) + try: + if deleter.run() != ilideleter.Deleter.SUCCESS: + msg = self.tr( + "An error occurred when deleting the dataset '{}' from the DB (check the QGIS log panel)." + ).format(dataset) + res = False + self.log_on_error.emit(self._log) + except JavaNotFoundError as e: + msg = e.error_string + res = False + + self._disconnect_ili_executable_signals(deleter) + + return res, msg + + def _connect_ili_executable_signals(self, ili_executable): + ili_executable.process_started.connect(self.process_started) + ili_executable.stderr.connect(self.stderr) + ili_executable.stdout.connect(self.stdout) + ili_executable.process_finished.connect(self.process_finished) + + ili_executable.process_started.connect(self._log_on_process_started) + ili_executable.stderr.connect(self._log_on_stderr) + + def _disconnect_ili_executable_signals(self, ili_executable): + ili_executable.process_started.disconnect(self.process_started) + ili_executable.stderr.disconnect(self.stderr) + ili_executable.stdout.disconnect(self.stdout) + ili_executable.process_finished.disconnect(self.process_finished) + + ili_executable.process_started.disconnect(self._log_on_process_started) + ili_executable.stderr.disconnect(self._log_on_stderr) + + def _log_on_process_started(self, command): + self._log += command + "\n" + + def _log_on_stderr(self, text): + self._log += text diff --git a/tests/README.md b/tests/README.md index 78f7065..081d9ae 100644 --- a/tests/README.md +++ b/tests/README.md @@ -28,7 +28,7 @@ These are dirty notes for the quickest way to test mssql queries manually in the 1. Create a new dir. E.g. `.local_docker_test` 2. Copy the original docker-compose file from directory `.docker` and remove everything except the qgis and the mssql container. -3. Copy the original Dockerfile as well. Leaf it like it is... +3. Copy the original Dockerfile as well. Leave it like it is... 4. Copy the original `run-docker-tests.sh` and remove everything except: ```bash set -e diff --git a/tests/test_dataset_handling.py b/tests/test_dataset_handling.py index e2a7cc2..505fec0 100644 --- a/tests/test_dataset_handling.py +++ b/tests/test_dataset_handling.py @@ -281,7 +281,7 @@ def check_dataset_mutations(self, db_connector): result = db_connector.create_basket( glarus_nord_tid, f"{topics[0]['model']}.{topics[0]['topic']}" ) - # Generate the basketsfor 'Glarus Nord' and the second topic + # Generate the baskets for 'Glarus Nord' and the second topic result = db_connector.create_basket( glarus_nord_tid, f"{topics[1]['model']}.{topics[1]['topic']}" ) @@ -296,6 +296,46 @@ def check_dataset_mutations(self, db_connector): record["datasetname"] for record in db_connector.get_datasets_info() } == {"Winti", "Seuzach", "Glarus West"} + # Edit basket for topic PipeBasketTest.Infrastructure and dataset Glarus West + baskets_info = db_connector.get_baskets_info() + for record in baskets_info: + if ( + record["topic"] == "PipeBasketTest.Infrastructure" + and record["datasetname"] == "Glarus West" + ): + basket_info = record + break + basket_t_id = basket_info["basket_t_id"] + dataset_t_id = basket_info["dataset_t_id"] + + # Info to be set to existing basket + basket_config = { + "topic": "PipeBasketTest.Infrastructure", + "basket_t_id": basket_t_id, + "bid_value": "3aa70ca6-13c6-482f-a415-a59694cfd658", + "attachmentkey": "my own attachment key", + "dataset_t_id": dataset_t_id, + "datasetname": "Glarus West", + } + res, msg = db_connector.edit_basket(basket_config) + assert res, msg + + baskets_info = db_connector.get_baskets_info() + assert len(baskets_info) == 6 + for record in baskets_info: + if record["basket_t_id"] == basket_t_id: + edited_basket_info = record + break + assert edited_basket_info["basket_t_id"] == basket_t_id + assert edited_basket_info["dataset_t_id"] == dataset_t_id + assert ( + edited_basket_info["basket_t_ili_tid"] + == "3aa70ca6-13c6-482f-a415-a59694cfd658" + ) + assert edited_basket_info["attachmentkey"] == "my own attachment key" + assert edited_basket_info["datasetname"] == "Glarus West" + assert edited_basket_info["topic"] == "PipeBasketTest.Infrastructure" + def print_info(self, text): logging.info(text) diff --git a/tests/test_delete.py b/tests/test_delete.py new file mode 100644 index 0000000..db43673 --- /dev/null +++ b/tests/test_delete.py @@ -0,0 +1,307 @@ +""" +/*************************************************************************** + ------------------- + begin : 27.09.2024 + git sha : :%H$ + copyright : (C) 2024 by Germán Carrillo + email : german at opengis ch + ***************************************************************************/ + +/*************************************************************************** + * * + * This program 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 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +""" + +import datetime +import logging +import os +import shutil +import tempfile + +from qgis.testing import start_app, unittest + +import modelbaker.utils.db_utils as db_utils +from modelbaker.iliwrapper import ilideleter, iliimporter +from modelbaker.iliwrapper.globals import DbIliMode +from tests.utils import ( + ilidataimporter_config, + ilideleter_config, + iliimporter_config, + testdata_path, +) + +start_app() + + +class TestDelete(unittest.TestCase): + @classmethod + def setUpClass(cls): + """Run before all tests.""" + cls.basetestpath = tempfile.mkdtemp() + + def test_delete_postgis(self): + # Schema Import + importer = iliimporter.Importer() + importer.tool = DbIliMode.ili2pg + importer.configuration = iliimporter_config(importer.tool) + importer.configuration.ilifile = testdata_path( + "ilimodels/PipeBasketTest_V1.ili" + ) + importer.configuration.ilimodels = "PipeBasketTest" + importer.configuration.dbschema = "any_{:%Y%m%d%H%M%S%f}".format( + datetime.datetime.now() + ) + importer.configuration.inheritance = "smart2" + importer.configuration.create_basket_col = True + importer.stdout.connect(self.print_info) + importer.stderr.connect(self.print_error) + assert importer.run() == iliimporter.Importer.SUCCESS + + # Import data + dataImporter = iliimporter.Importer(dataImport=True) + dataImporter.tool = DbIliMode.ili2pg + dataImporter.configuration = ilidataimporter_config(importer.tool) + dataImporter.configuration.dbschema = importer.configuration.dbschema + dataImporter.configuration.xtffile = testdata_path( + "xtf/test_pipebaskettest_v1_winti.xtf" + ) + dataImporter.configuration.dataset = "Winti" + dataImporter.stdout.connect(self.print_info) + dataImporter.stderr.connect(self.print_error) + assert dataImporter.run() == iliimporter.Importer.SUCCESS + + # Expected datasets and baskets + db_connector = db_utils.get_db_connector(importer.configuration) + + # Basket handling is active + assert db_connector.get_basket_handling() + + # Two topics are created (by schema import) + assert len(db_connector.get_topics_info()) == 2 + # One dataset is created (by schema import) + assert len(db_connector.get_datasets_info()) == 1 + # Means we have two baskets created (by schema import) + assert len(db_connector.get_baskets_info()) == 2 + + # Import data + dataImporter = iliimporter.Importer(dataImport=True) + dataImporter.tool = DbIliMode.ili2pg + dataImporter.configuration = ilidataimporter_config(importer.tool) + dataImporter.configuration.dbschema = importer.configuration.dbschema + dataImporter.configuration.xtffile = testdata_path( + "xtf/test_pipebaskettest_v1_seuzach.xtf" + ) + dataImporter.configuration.dataset = "Seuzach" + dataImporter.stdout.connect(self.print_info) + dataImporter.stderr.connect(self.print_error) + assert dataImporter.run() == iliimporter.Importer.SUCCESS + + # Two topics are created (by schema import) + assert len(db_connector.get_topics_info()) == 2 + # Two datasets are created (by schema import) + assert len(db_connector.get_datasets_info()) == 2 + # Means we have four baskets created (by schema import) + assert len(db_connector.get_baskets_info()) == 4 + + # Delete dataset + datasetDeleter = ilideleter.Deleter() + datasetDeleter.tool = DbIliMode.ili2pg + datasetDeleter.configuration = ilideleter_config(importer.tool) + datasetDeleter.configuration.dbschema = importer.configuration.dbschema + datasetDeleter.configuration.dataset = "Winti" + datasetDeleter.stdout.connect(self.print_info) + datasetDeleter.stderr.connect(self.print_error) + assert datasetDeleter.run() == ilideleter.Deleter.SUCCESS + + # One remaining dataset + datasets_info = db_connector.get_datasets_info() + assert len(db_connector.get_datasets_info()) == 1 + # Means only two remaining baskets + assert len(db_connector.get_baskets_info()) == 2 + + # Check existent dataset name + assert datasets_info[0]["datasetname"] == "Seuzach" + + def test_delete_geopackage(self): + # Schema Import + importer = iliimporter.Importer() + importer.tool = DbIliMode.ili2gpkg + importer.configuration = iliimporter_config(importer.tool) + importer.configuration.ilifile = testdata_path( + "ilimodels/PipeBasketTest_V1.ili" + ) + importer.configuration.ilimodels = "PipeBasketTest" + importer.configuration.dbfile = os.path.join( + self.basetestpath, "tmp_delete_dataset_gpkg.gpkg" + ) + importer.configuration.inheritance = "smart2" + importer.configuration.create_basket_col = True + importer.stdout.connect(self.print_info) + importer.stderr.connect(self.print_error) + assert importer.run() == iliimporter.Importer.SUCCESS + + # Import data + dataImporter = iliimporter.Importer(dataImport=True) + dataImporter.tool = DbIliMode.ili2gpkg + dataImporter.configuration = ilidataimporter_config(importer.tool) + dataImporter.configuration.dbfile = importer.configuration.dbfile + dataImporter.configuration.xtffile = testdata_path( + "xtf/test_pipebaskettest_v1_winti.xtf" + ) + dataImporter.configuration.dataset = "Winti" + dataImporter.stdout.connect(self.print_info) + dataImporter.stderr.connect(self.print_error) + assert dataImporter.run() == iliimporter.Importer.SUCCESS + + # Expected datasets and baskets + db_connector = db_utils.get_db_connector(importer.configuration) + + # Basket handling is active + assert db_connector.get_basket_handling() + + # Two topics are created (by schema import) + assert len(db_connector.get_topics_info()) == 2 + # One dataset is created (by schema import) + assert len(db_connector.get_datasets_info()) == 1 + # Means we have two baskets created (by schema import) + assert len(db_connector.get_baskets_info()) == 2 + + # Import data + dataImporter = iliimporter.Importer(dataImport=True) + dataImporter.tool = DbIliMode.ili2gpkg + dataImporter.configuration = ilidataimporter_config(importer.tool) + dataImporter.configuration.dbfile = importer.configuration.dbfile + dataImporter.configuration.xtffile = testdata_path( + "xtf/test_pipebaskettest_v1_seuzach.xtf" + ) + dataImporter.configuration.dataset = "Seuzach" + dataImporter.stdout.connect(self.print_info) + dataImporter.stderr.connect(self.print_error) + assert dataImporter.run() == iliimporter.Importer.SUCCESS + + # Two topics are created (by schema import) + assert len(db_connector.get_topics_info()) == 2 + # Two datasets are created (by schema import) + assert len(db_connector.get_datasets_info()) == 2 + # Means we have four baskets created (by schema import) + assert len(db_connector.get_baskets_info()) == 4 + + # Delete dataset + datasetDeleter = ilideleter.Deleter() + datasetDeleter.tool = DbIliMode.ili2gpkg + datasetDeleter.configuration = ilideleter_config(importer.tool) + datasetDeleter.configuration.dbfile = importer.configuration.dbfile + datasetDeleter.configuration.dataset = "Winti" + datasetDeleter.stdout.connect(self.print_info) + datasetDeleter.stderr.connect(self.print_error) + assert datasetDeleter.run() == ilideleter.Deleter.SUCCESS + + # One remaining dataset + datasets_info = db_connector.get_datasets_info() + assert len(db_connector.get_datasets_info()) == 1 + # Means only two remaining baskets + assert len(db_connector.get_baskets_info()) == 2 + + # Check existent dataset name + assert datasets_info[0]["datasetname"] == "Seuzach" + + def _test_delete_mssql(self): + + # Schema Import + importer = iliimporter.Importer() + importer.tool = DbIliMode.ili2mssql + importer.configuration = iliimporter_config(importer.tool) + importer.configuration.ilifile = testdata_path( + "ilimodels/PipeBasketTest_V1.ili" + ) + importer.configuration.ilimodels = "PipeBasketTest" + importer.configuration.dbschema = "baskets_{:%Y%m%d%H%M%S%f}".format( + datetime.datetime.now() + ) + importer.configuration.create_basket_col = True + importer.configuration.inheritance = "smart2" + importer.stdout.connect(self.print_info) + importer.stderr.connect(self.print_error) + + assert importer.run() == iliimporter.Importer.SUCCESS + + # Import data + dataImporter = iliimporter.Importer(dataImport=True) + dataImporter.tool = DbIliMode.ili2mssql + dataImporter.configuration = ilidataimporter_config(importer.tool) + dataImporter.configuration.dbschema = importer.configuration.dbschema + dataImporter.configuration.xtffile = testdata_path( + "xtf/test_pipebaskettest_v1_winti.xtf" + ) + dataImporter.configuration.dataset = "Winti" + dataImporter.stdout.connect(self.print_info) + dataImporter.stderr.connect(self.print_error) + assert dataImporter.run() == iliimporter.Importer.SUCCESS + + # Expected datasets and baskets + db_connector = db_utils.get_db_connector(importer.configuration) + + # Basket handling is active + assert db_connector.get_basket_handling() + + # Two topics are created (by schema import) + assert len(db_connector.get_topics_info()) == 2 + # One dataset is created (by schema import) + assert len(db_connector.get_datasets_info()) == 1 + # Means we have two baskets created (by schema import) + assert len(db_connector.get_baskets_info()) == 2 + + # Import data + dataImporter = iliimporter.Importer(dataImport=True) + dataImporter.tool = DbIliMode.ili2mssql + dataImporter.configuration = ilidataimporter_config(importer.tool) + dataImporter.configuration.dbschema = importer.configuration.dbschema + dataImporter.configuration.xtffile = testdata_path( + "xtf/test_pipebaskettest_v1_seuzach.xtf" + ) + dataImporter.configuration.dataset = "Seuzach" + dataImporter.stdout.connect(self.print_info) + dataImporter.stderr.connect(self.print_error) + assert dataImporter.run() == iliimporter.Importer.SUCCESS + + # Two topics are created (by schema import) + assert len(db_connector.get_topics_info()) == 2 + # One dataset is created (by schema import) + assert len(db_connector.get_datasets_info()) == 2 + # Means we have two baskets created (by schema import) + assert len(db_connector.get_baskets_info()) == 4 + + # Delete dataset + datasetDeleter = ilideleter.Deleter() + datasetDeleter.tool = DbIliMode.ili2mssql + datasetDeleter.configuration = ilideleter_config(importer.tool) + datasetDeleter.configuration.dbschema = importer.configuration.dbschema + datasetDeleter.configuration.dataset = "Winti" + datasetDeleter.stdout.connect(self.print_info) + datasetDeleter.stderr.connect(self.print_error) + assert datasetDeleter.run() == ilideleter.Deleter.SUCCESS + + # One remaining dataset + datasets_info = db_connector.get_datasets_info() + assert len(db_connector.get_datasets_info()) == 1 + # Means only two remaining baskets + assert len(db_connector.get_baskets_info()) == 2 + + # Check existent dataset name + assert datasets_info[0]["datasetname"] == "Seuzach" + + def print_info(self, text): + logging.info(text) + + def print_error(self, text): + logging.error(text) + + @classmethod + def tearDownClass(cls): + """Run after all tests.""" + shutil.rmtree(cls.basetestpath, True) diff --git a/tests/test_ili2dbcommandconfiguration.py b/tests/test_ili2dbcommandconfiguration.py new file mode 100644 index 0000000..e5befd6 --- /dev/null +++ b/tests/test_ili2dbcommandconfiguration.py @@ -0,0 +1,72 @@ +""" +/*************************************************************************** + ------------------- + begin : 27.09.2024 + git sha : :%H$ + copyright : (C) 2024 by Germán Carrillo + email : german at opengis ch + ***************************************************************************/ + +/*************************************************************************** + * * + * This program 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 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +""" +from qgis.testing import start_app, unittest + +from modelbaker.iliwrapper.ili2dbconfig import ( + BaseConfiguration, + DeleteConfiguration, + ExportConfiguration, + Ili2DbCommandConfiguration, + ImportDataConfiguration, + SchemaImportConfiguration, + UpdateDataConfiguration, + ValidateConfiguration, +) + +start_app() + + +class TestIli2DbCommandConfiguration(unittest.TestCase): + GPKG_PATH = "/tmp/data.gpkg" + MODELS_DIR = "/tmp/models/" + + def test_ili2db_command_configuration_from_an_other(self): + base_config = BaseConfiguration() + base_config.custom_model_directories_enabled = True + base_config.custom_model_directories = self.MODELS_DIR + + configuration = Ili2DbCommandConfiguration() + configuration.base_configuration = base_config + configuration.dbfile = self.GPKG_PATH + + delete_config = DeleteConfiguration() + assert delete_config.dbfile == "" + + delete_config = DeleteConfiguration(configuration) + self.check_members(delete_config) + + import_config = ImportDataConfiguration(configuration) + self.check_members(import_config) + + import_schema_config = SchemaImportConfiguration(configuration) + self.check_members(import_schema_config) + + export_config = ExportConfiguration(configuration) + self.check_members(export_config) + + update_config = UpdateDataConfiguration(configuration) + self.check_members(update_config) + + validate_config = ValidateConfiguration(configuration) + self.check_members(validate_config) + + def check_members(self, config): + assert config.base_configuration.custom_model_directories_enabled == True + assert config.base_configuration.custom_model_directories == self.MODELS_DIR + assert config.dbfile == self.GPKG_PATH diff --git a/tests/utils.py b/tests/utils.py index 60539e5..fc53132 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -26,6 +26,7 @@ from modelbaker.iliwrapper.globals import DbIliMode from modelbaker.iliwrapper.ili2dbconfig import ( BaseConfiguration, + DeleteConfiguration, ExportConfiguration, ImportDataConfiguration, SchemaImportConfiguration, @@ -166,6 +167,31 @@ def ilivalidator_config( return configuration +def ilideleter_config(tool=DbIliMode.ili2pg, modeldir=None): + base_config = BaseConfiguration() + if modeldir is None: + base_config.custom_model_directories_enabled = False + else: + base_config.custom_model_directories = testdata_path(modeldir) + base_config.custom_model_directories_enabled = True + + configuration = DeleteConfiguration() + if tool == DbIliMode.ili2pg: + configuration.dbhost = os.environ["PGHOST"] + configuration.dbusr = "docker" + configuration.dbpwd = "docker" + configuration.database = "gis" + elif tool == DbIliMode.ili2mssql: + configuration.dbhost = "mssql" + configuration.dbusr = "sa" + configuration.dbpwd = "" + configuration.database = "gis" + + configuration.base_configuration = base_config + + return configuration + + @pytest.mark.skip("This is a utility function, not a test function") def testdata_path(path): basepath = os.path.dirname(os.path.abspath(__file__))