From b701c38fbbb51e2fa3ab3fab8e9c2e6f82c885ed Mon Sep 17 00:00:00 2001 From: Vincent Bourgeois Date: Thu, 24 Oct 2024 18:06:34 +0200 Subject: [PATCH] feat: wip multi langs --- backend/models.py | 101 +++++++++++++++++++++++++++++++++++++++++++--- backend/routes.py | 16 ++++---- backend/utils.py | 53 ++++++++++++++++-------- 3 files changed, 142 insertions(+), 28 deletions(-) diff --git a/backend/models.py b/backend/models.py index f100d487..7bae60c1 100644 --- a/backend/models.py +++ b/backend/models.py @@ -2,7 +2,7 @@ from geoalchemy2.types import Geometry import geoalchemy2.functions as geo_funcs from geoalchemy2.shape import to_shape -from marshmallow import fields +from marshmallow import fields, post_dump from marshmallow_enum import EnumField from shapely.geometry import mapping @@ -381,6 +381,16 @@ def _deserialize(self, value, attr, data): # schemas# +def get_translated_data(self, data): + if not self.lang_id: + return data + for field in self.translatable_fields: + for translation in data["translations"]: + if translation["lang_id"] == self.lang_id: + data[field] = translation[field] + return data + + class LangSchema(ma.SQLAlchemyAutoSchema): class Meta: model = Lang @@ -430,6 +440,16 @@ class Meta: class DicoThemeSchema(ma.SQLAlchemyAutoSchema): translations = ma.Nested(DicoThemeTranslationSchema, many=True) + translatable_fields = DicoThemeTranslationSchema.Meta.fields + + def __init__(self, *args, **kwargs): + self.lang_id = kwargs.pop("locale", None) + super().__init__(*args, **kwargs) + + @post_dump + def translate_fields(self, data, **kwargs): + return get_translated_data(self, data) + class Meta: model = DicoTheme fields = ("id_theme", "icon", "translations") @@ -438,6 +458,16 @@ class Meta: class DicoSthemeSchema(ma.SQLAlchemyAutoSchema): translations = ma.Nested(DicoSthemeTranslationSchema, many=True) + translatable_fields = DicoSthemeTranslationSchema.Meta.fields + + def __init__(self, *args, **kwargs): + self.lang_id = kwargs.pop("locale", None) + super().__init__(*args, **kwargs) + + @post_dump + def translate_fields(self, data, **kwargs): + return get_translated_data(self, data) + class Meta: model = DicoStheme include_relationships = True @@ -468,8 +498,8 @@ class Meta: class CorSthemeThemeSchema(ma.SQLAlchemyAutoSchema): - dico_theme = ma.Nested(DicoThemeSchema, only=["id_theme", "name_theme"]) - dico_stheme = ma.Nested(DicoSthemeSchema, only=["id_stheme", "name_stheme"]) + dico_theme = ma.Nested(DicoThemeSchema, only=["id_theme"]) + dico_stheme = ma.Nested(DicoSthemeSchema, only=["id_stheme"]) class Meta: fields = ("dico_theme", "dico_stheme") @@ -481,6 +511,16 @@ class ObservatorySchema(ma.SQLAlchemyAutoSchema): comparator = EnumField(ComparatorEnum, by_value=True) geom = fields.Method("geomSerialize") + translatable_fields = ObservatoryTranslationSchema.Meta.fields + + def __init__(self, *args, **kwargs): + self.lang_id = kwargs.pop("locale", None) + super().__init__(*args, **kwargs) + + @post_dump + def translate_fields(self, data, **kwargs): + return get_translated_data(self, data) + @staticmethod def geomSerialize(obj): if obj.geom is None: @@ -494,7 +534,21 @@ class Meta: include_relationships = True -class ObservatorySchemaFull(ObservatorySchema): +class ObservatorySchemaFull(ma.SQLAlchemyAutoSchema): + translations = ma.Nested(ObservatoryTranslationSchema, many=True) + comparator = EnumField(ComparatorEnum, by_value=True) + geom = fields.Method("geomSerialize") + + translatable_fields = ObservatoryTranslationSchema.Meta.fields + + def __init__(self, *args, **kwargs): + self.lang_id = kwargs.pop("locale", None) + super().__init__(*args, **kwargs) + + @post_dump + def translate_fields(self, data, **kwargs): + return get_translated_data(self, data) + @staticmethod def geomSerialize(obj): if obj.geom is None: @@ -503,8 +557,21 @@ def geomSerialize(obj): return p.wkt -class ObservatorySchemaLite(ObservatorySchema): +class ObservatorySchemaLite(ma.SQLAlchemyAutoSchema): + translations = ma.Nested(ObservatoryTranslationSchema, many=True) comparator = EnumField(ComparatorEnum, by_value=False) + geom = fields.Method("geomSerialize") + + translatable_fields = ObservatoryTranslationSchema.Meta.fields + + def __init__(self, *args, **kwargs): + self.lang_id = kwargs.pop("locale", None) + super().__init__(*args, **kwargs) + + @post_dump + def translate_fields(self, data, **kwargs): + print("translate_fields", self.lang_id) + return get_translated_data(self, data) @staticmethod def geomSerialize(obj): @@ -513,6 +580,10 @@ def geomSerialize(obj): p = to_shape(obj.geom) s = p.simplify(0.001, preserve_topology=True) return s.wkt + + class Meta: + model = Observatory + include_relationships = True class TSiteSchema(ma.SQLAlchemyAutoSchema): @@ -521,6 +592,16 @@ class TSiteSchema(ma.SQLAlchemyAutoSchema): observatory = ma.Nested(ObservatorySchema, only=["id", "ref", "color", "logo"]) main_theme = ma.Nested(DicoThemeSchema, only=["id_theme", "translations", "icon"]) + translatable_fields = TSiteTranslationSchema.Meta.fields + + def __init__(self, *args, **kwargs): + self.lang_id = kwargs.pop("locale", None) + super().__init__(*args, **kwargs) + + @post_dump + def translate_fields(self, data, **kwargs): + return get_translated_data(self, data) + class Meta: model = TSite include_fk = True @@ -530,6 +611,16 @@ class Meta: class CommunesSchema(ma.SQLAlchemyAutoSchema): translations = ma.Nested(CommunesTranslationSchema, many=True) + translatable_fields = CommunesTranslationSchema.Meta.fields + + def __init__(self, *args, **kwargs): + self.lang_id = kwargs.pop("locale", None) + super().__init__(*args, **kwargs) + + @post_dump + def translate_fields(self, data, **kwargs): + return get_translated_data(self, data) + class Meta: model = Communes include_relationships = True diff --git a/backend/routes.py b/backend/routes.py index 4954a70d..6b56374e 100644 --- a/backend/routes.py +++ b/backend/routes.py @@ -13,13 +13,8 @@ from env import db -dicotheme_schema = models.DicoThemeSchema(many=True) -dicostheme_schema = models.DicoSthemeSchema(many=True) photo_schema = models.TPhotoSchema(many=True) -observatory_schema_lite = models.ObservatorySchemaLite(many=True) -site_schema = models.TSiteSchema(many=True) themes_sthemes_schema = models.CorSthemeThemeSchema(many=True) -communes_schema = models.CommunesSchema(many=True) def localeGuard(f): @@ -50,6 +45,8 @@ def home(locale=None): if not utils.isMultiLangs() and locale is not None: return redirect("/") locale = utils.getLocale() + site_schema = models.TSiteSchema(many=True, locale=locale) + communes_schema = models.CommunesSchema(many=True, locale=locale) sql = text( f"""SELECT * FROM geopaysages.t_site p join geopaysages.t_site_translation pt on p.id_site=pt.row_id and pt.lang_id = '{locale}' @@ -154,6 +151,7 @@ def home(locale=None): .filter(models.ObservatoryTranslation.is_published == True) .order_by(models.ObservatoryTranslation.title) ) + observatory_schema_lite = models.ObservatorySchemaLite(many=True, locale=locale) dump_observatories = observatory_schema_lite.dump(observatories) col_max = 5 @@ -191,7 +189,11 @@ def gallery(): @main.route("/sites/") -def site(id_site): +@main.route("//sites/") +@localeGuard +def site(id_site, locale): + site_schema = models.TSiteSchema(many=True, locale=locale) + communes_schema = models.CommunesSchema(many=True, locale=locale) get_site_by_id = models.TSite.query.filter_by(id_site=id_site, publish_site=True) site = site_schema.dump(get_site_by_id) if len(site) == 0: @@ -249,6 +251,7 @@ def getPhoto(photo): @main.route("/sites//photos/latest") def site_photos_last(id_site): + site_schema = models.TSiteSchema(many=True) get_site_by_id = models.TSite.query.filter_by(id_site=id_site, publish_site=True) site = site_schema.dump(get_site_by_id) if len(site) == 0: @@ -279,7 +282,6 @@ def site_photos_last(id_site): @localeGuard def sites(locale=None): data = utils.getFiltersData() - print(locale) return render_template( "sites.jinja", diff --git a/backend/utils.py b/backend/utils.py index cd9dd3ee..5e8a711f 100644 --- a/backend/utils.py +++ b/backend/utils.py @@ -14,11 +14,7 @@ db = SQLAlchemy() -dicotheme_schema = models.DicoThemeSchema(many=True) -dicostheme_schema = models.DicoSthemeSchema(many=True) photo_schema = models.TPhotoSchema(many=True) -observatory_schema = models.ObservatorySchema(many=True) -site_schema = models.TSiteSchema(many=True) themes_sthemes_schema = models.CorSthemeThemeSchema(many=True) @@ -75,7 +71,9 @@ def getDbConf(): except Exception as exception: conf[row.get("key")] = row.get("value") - conf["default_sort_sites"] = conf.get("default_sort_sites", "name_site") + conf["default_sort_sites"] = conf.get( + "default_sort_sites", "geopaysages.t_site_translation.name_site" + ) return conf @@ -95,14 +93,37 @@ def isMultiObservatories(): return False +def getLocalizedSitesQuery(): + locale = getLocale() + return ( + models.TSite.query.join( + models.TSiteTranslation, + (models.TSite.id_site == models.TSiteTranslation.row_id) + & (models.TSiteTranslation.lang_id == locale), + ) + .join(models.Observatory) + .join( + models.ObservatoryTranslation, + (models.Observatory.id == models.ObservatoryTranslation.row_id) + & (models.ObservatoryTranslation.lang_id == locale), + ) + .filter( + models.TSiteTranslation.publish_site == True, + models.ObservatoryTranslation.is_published == True, + ) + ) + + def getFiltersData(): dbconf = getDbConf() + locale = getLocale() + observatory_schema = models.ObservatorySchema(many=True, locale=locale) + site_schema = models.TSiteSchema(many=True, locale=locale) + dicotheme_schema = models.DicoThemeSchema(many=True, locale=locale) + dicostheme_schema = models.DicoSthemeSchema(many=True, locale=locale) + sites = site_schema.dump( - models.TSite.query.join(models.Observatory) - .filter( - models.TSite.publish_site == True, models.Observatory.is_published == True - ) - .order_by(dbconf["default_sort_sites"]) + getLocalizedSitesQuery().order_by(text(dbconf["default_sort_sites"])) ) for site in sites: cor_sthemes_themes = site.get("cor_site_stheme_themes") @@ -204,11 +225,11 @@ def getFiltersData(): filter for filter in filters if filter.get("name") == "township" ][0] str_map_in = ["'" + township + "'" for township in filter_township.get("items")] - sql_map_str = ( - "SELECT code_commune AS id, nom_commune AS label FROM geopaysages.communes WHERE code_commune IN (" - + ",".join(str_map_in) - + ")" - ) + sql_map_str = f"""SELECT c.code_commune AS id, ct.nom_commune AS label + FROM geopaysages.communes c + JOIN geopaysages.communes_translation ct on ct.row_id = c.code_commune + WHERE code_commune IN ({",".join(str_map_in)}) + AND ct.lang_id = '{locale}'""" sql_map = text(sql_map_str) townships_result = db.engine.execute(sql_map).fetchall() townships = [dict(row) for row in townships_result] @@ -291,7 +312,7 @@ def getItem(name, id): observatories.append( { "id": site["id_observatory"], - "label": site["observatory"]["title"], + "label": observatory["title"], "data": { "geom": observatory["geom"], "color": observatory["color"],