diff --git a/modelbaker/dbconnector/db_connector.py b/modelbaker/dbconnector/db_connector.py index 1fef556..11259a8 100644 --- a/modelbaker/dbconnector/db_connector.py +++ b/modelbaker/dbconnector/db_connector.py @@ -449,9 +449,16 @@ def get_translation_handling(self) -> tuple[bool, str]: """ return False, "" - def get_available_languages(self, irrelevant_models: list[str]) -> list[str]: + def get_translation_models(self) -> list[str]: """ - Returns a list of available languages in the t_ili2db_nls table and ignores the values for the irrelevant models + Returns a list of models that are a TRANSLATION OF another model. + """ + return [] + + def get_available_languages(self, irrelevant_models: list[str], relevant_models: list[str]) -> list[str]: + """ + Returns a list of available languages in the t_ili2db_nls table and ignores the values for the irrelevant models. + If a list for relevant models is passed, only those are considered (otherwise all the others) """ return [] diff --git a/modelbaker/dbconnector/gpkg_connector.py b/modelbaker/dbconnector/gpkg_connector.py index 90781ff..f603a5c 100644 --- a/modelbaker/dbconnector/gpkg_connector.py +++ b/modelbaker/dbconnector/gpkg_connector.py @@ -1260,30 +1260,70 @@ def set_ili2db_sequence_value(self, value): def get_translation_handling(self) -> tuple[bool, str]: return self._table_exists(GPKG_NLS_TABLE) and self._lang != "", self._lang - def get_available_languages(self, irrelevant_models=[]): - if not self._table_exists(GPKG_NLS_TABLE): + def get_translation_models(self): + if not self._table_exists(GPKG_METAATTRS_TABLE): return [] cursor = self.conn.cursor() cursor.execute( - """SELECT DISTINCT - lang - FROM "{t_ili2db_nls}" - WHERE - lang IS NOT NULL - AND - substr(iliElement, 0, instr(iliElement, '.')) NOT IN ({model_list}) + """ + SELECT DISTINCT + ilielement + FROM "{t_ili2db_meta_attrs}" + WHERE + attr_name = 'ili2db.ili.translationOf' ; """.format( - t_ili2db_nls=GPKG_NLS_TABLE, - model_list=",".join( + t_ili2db_meta_attrs=GPKG_METAATTRS_TABLE, + ) + ) + records = cursor.fetchall() + cursor.close() + return [record["ilielement"] for record in records] + + def get_available_languages(self, irrelevant_models=[], relevant_models=[]): + if not self._table_exists(GPKG_METAATTRS_TABLE): + return [] + + white_list_restriction = '' + if len(relevant_models) > 0: + white_list_restriction = """ + AND + ilielement IN ({relevant_model_list}) + """.format( + relevant_model_list=",".join( + [f"'{modelname}'" for modelname in relevant_models] + ), + ) + black_list_restriction = '' + if len(irrelevant_models) > 0: + black_list_restriction = """ + AND + ilielement NOT IN ({irrelevant_model_list}) + """.format( + irrelevant_model_list=",".join( [f"'{modelname}'" for modelname in irrelevant_models] ), ) + cursor = self.conn.cursor() + cursor.execute( + """SELECT DISTINCT + attr_value + FROM "{t_ili2db_meta_attrs}" + WHERE + attr_name = 'ili2db.ili.lang' + {black_list_restriction} + {white_list_restriction} + ; + """.format( + t_ili2db_meta_attrs=GPKG_METAATTRS_TABLE, + black_list_restriction=black_list_restriction, + white_list_restriction=white_list_restriction, + ) ) records = cursor.fetchall() cursor.close() - return [record["lang"] for record in records] + return [record["attr_value"] for record in records] def get_domain_dispnames(self, tablename): if ( diff --git a/modelbaker/dbconnector/mssql_connector.py b/modelbaker/dbconnector/mssql_connector.py index cd61406..a4f15a1 100644 --- a/modelbaker/dbconnector/mssql_connector.py +++ b/modelbaker/dbconnector/mssql_connector.py @@ -1260,25 +1260,61 @@ def set_ili2db_sequence_value(self, value): def get_translation_handling(self) -> tuple[bool, str]: return self._table_exists(NLS_TABLE) and self._lang != "", self._lang - def get_available_languages(self, irrelevant_models=[]): - if self.schema and self._table_exists(NLS_TABLE): + def get_translation_models(self): + if self.schema and self._table_exists(METAATTRS_TABLE): cur = self.conn.cursor() cur.execute( """ SELECT DISTINCT - lang - FROM {schema}.t_ili2db_nls - WHERE - lang IS NOT NULL - AND - left(iliElement, charindex('.', iliElement)-1) NOT IN ({model_list}) + ilielement + FROM {schema}.t_ili2db_meta_attrs + WHERE + attr_name = 'ili2db.ili.translationOf' """ ).format( schema=self.schema, - model_list=",".join( - [f"'{modelname}'" for modelname in irrelevant_models] - ), ) + return [row.ilielement for row in cur.fetchall()] + return [] + + def get_available_languages(self, irrelevant_models=[], relevant_models=[]): + if self.schema and self._table_exists(METAATTRS_TABLE): - return [row.lang for row in cur.fetchall()] + white_list_restriction = '' + if len(relevant_models) > 0: + white_list_restriction = """ + AND + ilielement IN ({relevant_model_list}) + """.format( + relevant_model_list=",".join( + [f"'{modelname}'" for modelname in relevant_models] + ), + ) + black_list_restriction = '' + if len(irrelevant_models) > 0: + black_list_restriction = """ + AND + ilielement NOT IN ({irrelevant_model_list}) + """.format( + irrelevant_model_list=",".join( + [f"'{modelname}'" for modelname in irrelevant_models] + ), + ) + cur = self.conn.cursor() + cur.execute( + """ + SELECT DISTINCT + attr_value + FROM {schema}.t_ili2db_meta_attrs + WHERE + attr_name = 'ili2db.ili.lang' + {black_list_restriction} + {white_list_restriction} + """ + ).format( + schema=self.schema, + black_list_restriction=black_list_restriction, + white_list_restriction=white_list_restriction, + ) + return [row.attr_value for row in cur.fetchall()] return [] diff --git a/modelbaker/dbconnector/pg_connector.py b/modelbaker/dbconnector/pg_connector.py index b22207a..527dba9 100644 --- a/modelbaker/dbconnector/pg_connector.py +++ b/modelbaker/dbconnector/pg_connector.py @@ -1396,29 +1396,72 @@ def get_all_schemas(self): def get_translation_handling(self) -> tuple[bool, str]: return self._table_exists(PG_NLS_TABLE) and self._lang != "", self._lang - def get_available_languages(self, irrelevant_models=[]): - if self.schema and self._table_exists(PG_NLS_TABLE): + def get_translation_models(self): + if self.schema and self._table_exists(PG_METAATTRS_TABLE): cur = self.conn.cursor(cursor_factory=psycopg2.extras.DictCursor) cur.execute( sql.SQL( """ SELECT DISTINCT - lang - FROM {schema}.t_ili2db_nls - WHERE - lang IS NOT NULL - AND - split_part(iliElement,'.',1) NOT IN ({model_list}) + ilielement + FROM {schema}.t_ili2db_meta_attrs + WHERE + attr_name = 'ili2db.ili.translationOf' """ ).format( schema=sql.Identifier(self.schema), - model_list=sql.SQL(", ").join( - sql.Placeholder() * len(irrelevant_models) + ) + ) + return [row["ilielement"] for row in cur.fetchall()] + return [] + + + def get_available_languages(self, irrelevant_models=[], relevant_models=[]): + if self.schema and self._table_exists(PG_METAATTRS_TABLE): + + white_list_placeholders = sql.SQL('') + if len(relevant_models) > 0: + white_list_placeholders = sql.SQL(""" + AND + ilielement IN ({relevant_model_list}) + """ + ).format( + relevant_model_list=sql.SQL(", ").join( + sql.Placeholder() * len(relevant_models) ), + ) + black_list_placeholders = sql.SQL('') + if len(irrelevant_models) > 0: + black_list_placeholders = sql.SQL(""" + AND + ilielement NOT IN ({irrelevant_model_list}) + """ + ).format( + irrelevant_model_list=sql.SQL(", ").join( + sql.Placeholder() * len(irrelevant_models) + ) + ) + + cur = self.conn.cursor(cursor_factory=psycopg2.extras.DictCursor) + cur.execute( + sql.SQL( + """ + SELECT DISTINCT + attr_value + FROM {schema}.t_ili2db_meta_attrs + WHERE + attr_name = 'ili2db.ili.lang' + {white_list_placeholders} + {black_list_placeholders} + """ + ).format( + schema=sql.Identifier(self.schema), + white_list_placeholders=white_list_placeholders, + black_list_placeholders=black_list_placeholders, ), - irrelevant_models, + relevant_models+irrelevant_models ) - return [row["lang"] for row in cur.fetchall()] + return [row["attr_value"] for row in cur.fetchall()] return [] def get_domain_dispnames(self, tablename): diff --git a/tests/test_translations.py b/tests/test_translations.py index 53d1de1..432d26c 100644 --- a/tests/test_translations.py +++ b/tests/test_translations.py @@ -27,6 +27,7 @@ from qgis.core import QgsProject from qgis.testing import start_app, unittest +import modelbaker.utils.db_utils as db_utils from modelbaker.dataobjects.project import Project from modelbaker.db_factory.gpkg_command_config_manager import GpkgCommandConfigManager from modelbaker.generator.generator import Generator @@ -230,6 +231,82 @@ def test_translated_db_objects_pg(self): == "Geometrie_Document_(Geometrie)_AffectationPrimaire_SurfaceDeZones_(t_id)" ) + def test_available_langs_gpkg(self): + importer = iliimporter.Importer() + importer.tool = DbIliMode.ili2gpkg + importer.configuration = iliimporter_config(importer.tool) + importer.configuration.ilimodels = "PlansDAffectation_V1_2" + importer.configuration.dbfile = os.path.join( + self.basetestpath, "tmp_translated_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 + + db_connector = db_utils.get_db_connector(importer.configuration) + + # Translation handling is active + assert db_connector.get_translation_handling() + + # Get the translated models + assert {"PlansDAffectation_V1_2"} == set(db_connector.get_translation_models()) + + # Get all languages + assert {'en','de','fr'} == set(db_connector.get_available_languages()) + + # ... without irrelevant models + irrelevants = ["AdministrativeUnits_V1","AdministrativeUnitsCH_V1","Dictionaries_V1","DictionariesCH_V1"] + assert {'de','fr'} == set(db_connector.get_available_languages(irrelevants)) + + # ... and the language of the translated model only + assert {'fr'} == set(db_connector.get_available_languages([],["PlansDAffectation_V1_2"])) + + # --- and nonsense use case for the validation, get only of an english model + assert {'en'} == set(db_connector.get_available_languages([],["AdministrativeUnits_V1"])) + + # ... as well as ignoring the translated models and alowing it again and the english one + assert {'de','en'} == set(db_connector.get_available_languages(["PlansDAffectation_V1_2"])) + + def test_translated_db_objects_pg(self): + importer = iliimporter.Importer() + importer.tool = DbIliMode.ili2pg + importer.configuration = iliimporter_config(importer.tool) + importer.configuration.ilimodels = "PlansDAffectation_V1_2" + importer.configuration.dbschema = "tid_{:%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 + + db_connector = db_utils.get_db_connector(importer.configuration) + + # Translation handling is active + assert db_connector.get_translation_handling() + + # Get the translated models + assert {"PlansDAffectation_V1_2"} == set(db_connector.get_translation_models()) + + # Get all languages + assert {'en','de','fr'} == set(db_connector.get_available_languages()) + + # ... without irrelevant models + irrelevants = ["AdministrativeUnits_V1","AdministrativeUnitsCH_V1","Dictionaries_V1","DictionariesCH_V1"] + assert {'de','fr'} == set(db_connector.get_available_languages(irrelevants)) + + # ... and the language of the translated model only + assert {'fr'} == set(db_connector.get_available_languages([],["PlansDAffectation_V1_2"])) + + # --- and nonsense use case for the validation, get only of an english model + assert {'en'} == set(db_connector.get_available_languages([],["AdministrativeUnits_V1"])) + + # ... as well as ignoring the translated models and alowing it again and the english one + assert {'de','en'} == set(db_connector.get_available_languages(["PlansDAffectation_V1_2"])) + def print_info(self, text): logging.info(text)