From f0da0c06849aaaac9b93d3305765b414a85e6cb8 Mon Sep 17 00:00:00 2001 From: Maxime Vergez <85738261+mvergez@users.noreply.github.com> Date: Fri, 13 Jan 2023 09:32:29 +0100 Subject: [PATCH] Fix/db migrations (#31) * feat(db): upgrade down_revision following rebase Since rebase with develop: changed the down_revision number * fix(db): fix bind params enabling downgrade Beforehand the downgrade was not possible... * refactor(db): removed cor_site_type_category * refactor(db): changed category into type in cor * refactor(db): create cor_type_site * fix(db): renamed column * refactor(api): update models to fit migrations * fix(db):change bib_categorie_site to bib_type_site Adding : cor_site_module cor_site_type revision alembic to create function and trigger in order to add bib_type_site but only with nomenclature 'TYPE_SITE' upgrade and downgrade works [Refs ticket]: #3 Reviewed-by: andriac * fix(api): updated models from migrations * fix(api): wip: fix admin following migrations * fix(api): update routes and tests To match migration changes * feat: flask admin bib_type_site Change bib_categories to bib_type_site into flask admin Adding filtering in list label_fr of type_site to secure the unique constraint Reviewed-by: andriac [Refs ticket]: #3 * fix(api): updated schema to match models * fix(api): module edition * style(api): uniformize type_site * style(api): change relationship name for type_site * feat(api): validator admin * fix(api): make unique BibTypeSite in admin * test(api): fix test when existing nomenclatures In database Co-authored-by: Andria Capai --- backend/gn_module_monitoring/blueprint.py | 4 +- .../config/generic/module.json | 12 ++-- ...remove_id_module_from_sites_complements.py | 4 +- ...=> a54bafb13ce8_create_cor_module_type.py} | 29 ++++---- .../b53bafb13ce8_create_bib_categorie_site.py | 60 ---------------- .../b53bafb13ce8_create_bib_type_site.py | 71 +++++++++++++++++++ .../ce54ba49ce5c_create_cor_type_site.py | 51 +++++++++++++ ...bafb13ce8_create_cor_site_type_category.py | 47 ------------ ...1f54_remove_id_module_from_sites_groups.py | 12 ++-- .../gn_module_monitoring/monitoring/admin.py | 42 +++++++---- .../gn_module_monitoring/monitoring/models.py | 50 ++++++++++--- .../monitoring/schemas.py | 15 ++-- .../gn_module_monitoring/routes/data_utils.py | 4 +- backend/gn_module_monitoring/routes/site.py | 27 ++++--- .../gn_module_monitoring/tests/conftest.py | 1 + .../tests/fixtures/module.py | 5 +- .../tests/fixtures/site.py | 38 ++-------- .../tests/fixtures/type_site.py | 40 +++++++++++ .../test_models/test_bib_type_site.py | 23 ++++++ .../test_models/test_module.py | 12 ++-- .../test_schemas/test_bib_site_type_schema.py | 14 ++++ .../tests/test_routes/test_site.py | 28 +++----- 22 files changed, 349 insertions(+), 240 deletions(-) rename backend/gn_module_monitoring/migrations/{a54bafb13ce8_create_cor_module_category.py => a54bafb13ce8_create_cor_module_type.py} (53%) delete mode 100644 backend/gn_module_monitoring/migrations/b53bafb13ce8_create_bib_categorie_site.py create mode 100644 backend/gn_module_monitoring/migrations/b53bafb13ce8_create_bib_type_site.py create mode 100644 backend/gn_module_monitoring/migrations/ce54ba49ce5c_create_cor_type_site.py delete mode 100644 backend/gn_module_monitoring/migrations/e64bafb13ce8_create_cor_site_type_category.py create mode 100644 backend/gn_module_monitoring/tests/fixtures/type_site.py create mode 100644 backend/gn_module_monitoring/tests/test_monitoring/test_models/test_bib_type_site.py create mode 100644 backend/gn_module_monitoring/tests/test_monitoring/test_schemas/test_bib_site_type_schema.py diff --git a/backend/gn_module_monitoring/blueprint.py b/backend/gn_module_monitoring/blueprint.py index 267b8fbba..116e2a03f 100644 --- a/backend/gn_module_monitoring/blueprint.py +++ b/backend/gn_module_monitoring/blueprint.py @@ -7,7 +7,7 @@ from geonature.core.admin.admin import admin as flask_admin from geonature.utils.env import DB -from gn_module_monitoring.monitoring.admin import BibCategorieSiteView +from gn_module_monitoring.monitoring.admin import BibTypeSiteView from .command.cmd import commands blueprint = Blueprint( @@ -19,4 +19,4 @@ for cmd in commands: blueprint.cli.add_command(cmd) -flask_admin.add_view(BibCategorieSiteView(DB.session, name="Catégories de sites", category="Monitorings")) +flask_admin.add_view(BibTypeSiteView(DB.session, name="Types de site", category="Monitorings")) diff --git a/backend/gn_module_monitoring/config/generic/module.json b/backend/gn_module_monitoring/config/generic/module.json index 66c97ab1d..ef3c5ddae 100644 --- a/backend/gn_module_monitoring/config/generic/module.json +++ b/backend/gn_module_monitoring/config/generic/module.json @@ -122,18 +122,18 @@ "definition": "Afficher le module dans le menu de GeoNature. (Recharger la page pour voir les modifications)." }, - "categories": { + "types_site": { "type_widget": "datalist", - "attribut_label": "Catégories de sites", - "type_util": "categorie", - "keyValue": "id_categorie", + "attribut_label": "Types de sites", + "type_util": "types_site", + "keyValue": "id_nomenclature", "keyLabel": "label", "multiple": true, - "api" : "__MONITORINGS_PATH/sites/categories", + "api" : "__MONITORINGS_PATH/sites/types", "application": "GeoNature", "required": true, "data_path": "items", - "definition": "Permet de paramétrer la compatibilité de ce module avec les catégories de sites" + "definition": "Permet de paramétrer la compatibilité de ce module avec les types de sites" }, "medias": { diff --git a/backend/gn_module_monitoring/migrations/6673266fb79c_remove_id_module_from_sites_complements.py b/backend/gn_module_monitoring/migrations/6673266fb79c_remove_id_module_from_sites_complements.py index ca6d04879..418234cdd 100644 --- a/backend/gn_module_monitoring/migrations/6673266fb79c_remove_id_module_from_sites_complements.py +++ b/backend/gn_module_monitoring/migrations/6673266fb79c_remove_id_module_from_sites_complements.py @@ -1,7 +1,7 @@ """remove_id_module_from_sites_complements Revision ID: 6673266fb79c -Revises: +Revises: a54bafb13ce8 Create Date: 2022-12-13 16:00:00.512562 """ @@ -12,7 +12,7 @@ # revision identifiers, used by Alembic. revision = "6673266fb79c" -down_revision = "e64bafb13ce8" +down_revision = "a54bafb13ce8" branch_labels = None depends_on = None diff --git a/backend/gn_module_monitoring/migrations/a54bafb13ce8_create_cor_module_category.py b/backend/gn_module_monitoring/migrations/a54bafb13ce8_create_cor_module_type.py similarity index 53% rename from backend/gn_module_monitoring/migrations/a54bafb13ce8_create_cor_module_category.py rename to backend/gn_module_monitoring/migrations/a54bafb13ce8_create_cor_module_type.py index 491198526..718194469 100644 --- a/backend/gn_module_monitoring/migrations/a54bafb13ce8_create_cor_module_category.py +++ b/backend/gn_module_monitoring/migrations/a54bafb13ce8_create_cor_module_type.py @@ -1,7 +1,7 @@ -"""create_cor_module_category +"""create_cor_module_type Revision ID: a54bafb13ce8 -Revises: +Revises: ce54ba49ce5c Create Date: 2022-12-06 16:18:24.512562 """ @@ -10,7 +10,7 @@ # revision identifiers, used by Alembic. revision = "a54bafb13ce8" -down_revision = "f24adb481f54" +down_revision = "ce54ba49ce5c" branch_labels = None depends_on = None @@ -20,28 +20,33 @@ def upgrade(): op.create_table( - "cor_module_categorie", + "cor_module_type", sa.Column( - "id_categorie", + "id_type_site", sa.Integer(), sa.ForeignKey( - f"{monitorings_schema}.bib_categorie_site.id_categorie", - name="fk_cor_module_categorie_id_categorie", + f"{monitorings_schema}.bib_type_site.id_nomenclature", + name="fk_cor_module_type_id_nomenclature", ondelete="CASCADE", onupdate="CASCADE", ), nullable=False, ), - sa.Column("id_module", sa.Integer(),sa.ForeignKey( + sa.Column( + "id_module", + sa.Integer(), + sa.ForeignKey( f"{referent_schema}.t_modules.id_module", - name="fk_cor_module_categorie_id_module", + name="fk_cor_module_type_id_module", ondelete="CASCADE", onupdate="CASCADE", - ), nullable=False), - sa.PrimaryKeyConstraint("id_categorie", "id_module", name="pk_cor_module_categorie"), + ), + nullable=False, + ), + sa.PrimaryKeyConstraint("id_type_site", "id_module", name="pk_cor_module_type"), schema=monitorings_schema, ) def downgrade(): - op.drop_table("cor_module_categorie", schema=monitorings_schema) + op.drop_table("cor_module_type", schema=monitorings_schema) diff --git a/backend/gn_module_monitoring/migrations/b53bafb13ce8_create_bib_categorie_site.py b/backend/gn_module_monitoring/migrations/b53bafb13ce8_create_bib_categorie_site.py deleted file mode 100644 index 610269dd5..000000000 --- a/backend/gn_module_monitoring/migrations/b53bafb13ce8_create_bib_categorie_site.py +++ /dev/null @@ -1,60 +0,0 @@ -"""create_bib_categorie_site - -Revision ID: b53bafb13ce8 -Revises: -Create Date: 2022-12-06 16:18:24.512562 - -""" -from alembic import op -import sqlalchemy as sa - -# revision identifiers, used by Alembic. -revision = "b53bafb13ce8" -down_revision = "362cf9d504ec" -branch_labels = None -depends_on = None - -monitorings_schema = "gn_monitoring" - - -def upgrade(): - op.create_table( - "bib_categorie_site", - sa.Column("id_categorie", sa.Integer(), nullable=False), - sa.Column("label", sa.String(), nullable=False), - sa.Column("config", sa.JSON(), nullable=True), - sa.PrimaryKeyConstraint("id_categorie"), - schema=monitorings_schema, - ) - op.create_index( - op.f("ix_bib_categorie_site_id"), - "bib_categorie_site", - ["id_categorie"], - unique=False, - schema=monitorings_schema, - ) - op.add_column( - "t_base_sites", - sa.Column( - "id_categorie", - sa.Integer(), - sa.ForeignKey( - f"{monitorings_schema}.bib_categorie_site.id_categorie", - name="fk_t_base_sites_id_categorie", - ondelete="CASCADE", - ), - nullable=True, # TODO: see migration? nullable is conservative here - ), - schema=monitorings_schema, - ) - - -def downgrade(): - op.drop_constraint("fk_t_base_sites_id_categorie", "t_base_sites", schema=monitorings_schema) - op.drop_column("t_base_sites", "id_categorie", schema=monitorings_schema) - op.drop_index( - op.f("ix_bib_categorie_site_id"), - table_name="bib_categorie_site", - schema=monitorings_schema, - ) - op.drop_table("bib_categorie_site", schema=monitorings_schema) diff --git a/backend/gn_module_monitoring/migrations/b53bafb13ce8_create_bib_type_site.py b/backend/gn_module_monitoring/migrations/b53bafb13ce8_create_bib_type_site.py new file mode 100644 index 000000000..a064d26bb --- /dev/null +++ b/backend/gn_module_monitoring/migrations/b53bafb13ce8_create_bib_type_site.py @@ -0,0 +1,71 @@ +"""create_bib_type_site + +Revision ID: b53bafb13ce8 +Revises: e78003460441 +Create Date: 2022-12-06 16:18:24.512562 + +""" +from alembic import op +import sqlalchemy as sa + +# revision identifiers, used by Alembic. +revision = "b53bafb13ce8" +down_revision = "e78003460441" +branch_labels = None +depends_on = None + +monitorings_schema = "gn_monitoring" +nomenclature_schema = "ref_nomenclatures" + +TYPE_SITE = "TYPE_SITE" + + +def upgrade(): + op.create_table( + "bib_type_site", + sa.Column( + "id_nomenclature", + sa.Integer(), + sa.ForeignKey( + f"{nomenclature_schema}.t_nomenclatures.id_nomenclature", + name="fk_t_nomenclatures_id_nomenclature", + ), + nullable=False, + unique=True, + ), + sa.PrimaryKeyConstraint("id_nomenclature"), + sa.Column("config", sa.JSON(), nullable=True), + schema=monitorings_schema, + ) + + statement = sa.text( + f""" + CREATE OR REPLACE FUNCTION {monitorings_schema}.ck_bib_type_site_id_nomenclature() + RETURNS trigger + LANGUAGE plpgsql + AS $function$ + BEGIN + perform {nomenclature_schema}.check_nomenclature_type_by_mnemonique(NEW.id_nomenclature, :mnemonique ); + RETURN NEW; + END; + $function$ + ; + DROP TRIGGER IF EXISTS ck_bib_type_site_id_nomenclature on gn_monitoring.bib_type_site; + CREATE TRIGGER ck_bib_type_site_id_nomenclature BEFORE + INSERT + OR + UPDATE ON {monitorings_schema}.bib_type_site FOR EACH ROW EXECUTE PROCEDURE {monitorings_schema}.ck_bib_type_site_id_nomenclature(); + """ + ).bindparams(mnemonique=TYPE_SITE) + op.execute(statement) + + +def downgrade(): + + op.drop_table("bib_type_site", schema=monitorings_schema) + statement = sa.text( + f""" + DROP FUNCTION IF EXISTS {monitorings_schema}.ck_bib_type_site_id_nomenclature; + """ + ) + op.execute(statement) diff --git a/backend/gn_module_monitoring/migrations/ce54ba49ce5c_create_cor_type_site.py b/backend/gn_module_monitoring/migrations/ce54ba49ce5c_create_cor_type_site.py new file mode 100644 index 000000000..8befc1f68 --- /dev/null +++ b/backend/gn_module_monitoring/migrations/ce54ba49ce5c_create_cor_type_site.py @@ -0,0 +1,51 @@ +"""create_cor_type_site + +Revision ID: ce54ba49ce5c +Revises: b53bafb13ce8 +Create Date: 2022-12-06 16:18:24.512562 + +""" +from alembic import op +import sqlalchemy as sa + +# revision identifiers, used by Alembic. +revision = "ce54ba49ce5c" +down_revision = "b53bafb13ce8" +branch_labels = None +depends_on = None + +monitorings_schema = "gn_monitoring" + + +def upgrade(): + op.create_table( + "cor_type_site", + sa.Column( + "id_type_site", + sa.Integer(), + sa.ForeignKey( + f"{monitorings_schema}.bib_type_site.id_nomenclature", + name="fk_cor_type_site_id_nomenclature", + ondelete="CASCADE", + onupdate="CASCADE", + ), + nullable=False, + ), + sa.Column( + "id_base_site", + sa.Integer(), + sa.ForeignKey( + f"{monitorings_schema}.t_base_sites.id_base_site", + name="fk_cor_type_site_id_base_site", + ondelete="CASCADE", + onupdate="CASCADE", + ), + nullable=False, + ), + sa.PrimaryKeyConstraint("id_type_site", "id_base_site", name="pk_cor_type_site"), + schema=monitorings_schema, + ) + + +def downgrade(): + op.drop_table("cor_type_site", schema=monitorings_schema) diff --git a/backend/gn_module_monitoring/migrations/e64bafb13ce8_create_cor_site_type_category.py b/backend/gn_module_monitoring/migrations/e64bafb13ce8_create_cor_site_type_category.py deleted file mode 100644 index 2dfcf3412..000000000 --- a/backend/gn_module_monitoring/migrations/e64bafb13ce8_create_cor_site_type_category.py +++ /dev/null @@ -1,47 +0,0 @@ -"""create_cor_site_type_category - -Revision ID: e64bafb13ce8 -Revises: -Create Date: 2022-12-06 16:18:24.512562 - -""" -from alembic import op -import sqlalchemy as sa - -# revision identifiers, used by Alembic. -revision = "e64bafb13ce8" -down_revision = "a54bafb13ce8" -branch_labels = None -depends_on = None - -monitorings_schema = "gn_monitoring" -referent_schema = "ref_nomenclatures" - - -def upgrade(): - op.create_table( - "cor_site_type_categorie", - sa.Column( - "id_categorie", - sa.Integer(), - sa.ForeignKey( - f"{monitorings_schema}.bib_categorie_site.id_categorie", - name="fk_cor_site_type_categorie_id_categorie", - ondelete="CASCADE", - onupdate="CASCADE", - ), - nullable=False, - ), - sa.Column("id_nomenclature", sa.Integer(),sa.ForeignKey( - f"{referent_schema}.t_nomenclatures.id_nomenclature", - name="fk_cor_site_type_categorie_id_type", - ondelete="CASCADE", - onupdate="CASCADE", - ), nullable=False), - sa.PrimaryKeyConstraint("id_categorie", "id_nomenclature", name="pk_cor_site_type_categorie"), - schema=monitorings_schema, - ) - - -def downgrade(): - op.drop_table("cor_site_type_categorie", schema=monitorings_schema) diff --git a/backend/gn_module_monitoring/migrations/f24adb481f54_remove_id_module_from_sites_groups.py b/backend/gn_module_monitoring/migrations/f24adb481f54_remove_id_module_from_sites_groups.py index 52c45d8e7..9280f92ed 100644 --- a/backend/gn_module_monitoring/migrations/f24adb481f54_remove_id_module_from_sites_groups.py +++ b/backend/gn_module_monitoring/migrations/f24adb481f54_remove_id_module_from_sites_groups.py @@ -1,7 +1,7 @@ """remove_id_module_from_sites_groups Revision ID: f24adb481f54 -Revises: +Revises: 6673266fb79c Create Date: 2022-12-13 16:00:00.512562 """ @@ -12,7 +12,7 @@ # revision identifiers, used by Alembic. revision = "f24adb481f54" -down_revision = "b53bafb13ce8" +down_revision = "6673266fb79c" branch_labels = None depends_on = None @@ -47,8 +47,8 @@ def downgrade(): update {monitorings_schema}.t_sites_groups set id_module = (select id_module from gn_commons.t_modules tm - where module_code = '\:module_code'); + where module_code = :module_code); """ - ) - op.execute(statement, module_code=MODULE_CODE) - op.alter_column("t_sites_groups", "id_module", nullable=False) + ).bindparams(module_code=MODULE_CODE) + op.execute(statement) + op.alter_column("t_sites_groups", "id_module", nullable=False, schema=monitorings_schema) diff --git a/backend/gn_module_monitoring/monitoring/admin.py b/backend/gn_module_monitoring/monitoring/admin.py index e7184b8ce..5406513ff 100644 --- a/backend/gn_module_monitoring/monitoring/admin.py +++ b/backend/gn_module_monitoring/monitoring/admin.py @@ -2,16 +2,33 @@ from geonature.core.admin.admin import CruvedProtectedMixin from geonature.utils.env import DB from pypnnomenclature.models import TNomenclatures, BibNomenclaturesTypes +from wtforms.validators import ValidationError -from gn_module_monitoring.monitoring.models import BibCategorieSite +from gn_module_monitoring.monitoring.models import BibTypeSite SITE_TYPE = "TYPE_SITE" -class BibCategorieSiteView(CruvedProtectedMixin, ModelView): +class Unique: + """ validator that checks field uniqueness """ + def __init__(self, model, field, message=None): + self.model = model + self.field = field + if not message: + message = u'A type is already created with this nomenclature' + self.message = message + + def __call__(self, form, field): + if field.object_data == field.data: + return + if self.model.query.filter(getattr(self.model, self.field) == getattr(field.data, self.field)).first(): + raise ValidationError(self.message) + + +class BibTypeSiteView(CruvedProtectedMixin, ModelView): """ - Surcharge de l'administration des catégories de sites + Surcharge de l'administration des types de sites """ module_code = "MONITORINGS" @@ -19,9 +36,9 @@ class BibCategorieSiteView(CruvedProtectedMixin, ModelView): def __init__(self, session, **kwargs): # Référence au model utilisé - super(BibCategorieSiteView, self).__init__(BibCategorieSite, session, **kwargs) + super(BibTypeSiteView, self).__init__(BibTypeSite, session, **kwargs) - def get_only_type_site_asc(): + def get_only_nomenclature_asc(): return ( DB.session.query(TNomenclatures) .join(TNomenclatures.nomenclature_type) @@ -32,19 +49,20 @@ def get_only_type_site_asc(): def get_label_fr_nomenclature(x): return x.label_fr - def list_label_site_type_formatter(view, _context, model, _name): - return [item.label_fr for item in model.site_type] + def list_label_nomenclature_formatter(view, _context, model, _name): + return model.nomenclature.label_fr # Nom de colonne user friendly - column_labels = dict(site_type="Type de site") + column_labels = dict(nomenclature="Types de site") # Description des colonnes - column_descriptions = dict(site_type="Type de site à choisir en lien avec la catégorie") + column_descriptions = dict(nomenclature="Nomenclature de type de site à choisir") column_hide_backrefs = False form_args = dict( - site_type=dict(query_factory=get_only_type_site_asc, get_label=get_label_fr_nomenclature) + nomenclature=dict(query_factory=get_only_nomenclature_asc, get_label=get_label_fr_nomenclature, + validators=[Unique(BibTypeSite, "id_nomenclature")]) ) - column_list = ("label", "config", "site_type") - column_formatters = dict(site_type=list_label_site_type_formatter) + column_list = ("nomenclature","config") + column_formatters = dict(nomenclature=list_label_nomenclature_formatter) diff --git a/backend/gn_module_monitoring/monitoring/models.py b/backend/gn_module_monitoring/monitoring/models.py index 99d082d7e..936e9c418 100644 --- a/backend/gn_module_monitoring/monitoring/models.py +++ b/backend/gn_module_monitoring/monitoring/models.py @@ -19,11 +19,10 @@ from geonature.core.gn_commons.models import TModules, cor_module_dataset from pypnusershub.db.models import User from geonature.core.gn_monitoring.models import corVisitObserver - from gn_module_monitoring.monitoring.queries import Query as MonitoringQuery -cor_module_categorie = DB.Table( - "cor_module_categorie", +cor_module_type = DB.Table( + "cor_module_type", DB.Column( "id_module", DB.Integer, @@ -31,27 +30,44 @@ primary_key=True, ), DB.Column( - "id_categorie", + "id_type_site", DB.Integer, - DB.ForeignKey("gn_monitoring.bib_categorie_site.id_categorie"), + DB.ForeignKey("gn_monitoring.bib_type_site.id_nomenclature"), primary_key=True, ), schema="gn_monitoring") -cor_site_type_categorie = DB.Table( - "cor_site_type_categorie", +cor_type_site = DB.Table( + "cor_type_site", DB.Column( - "id_nomenclature", + "id_base_site", DB.Integer, - DB.ForeignKey("ref_nomenclatures.t_nomenclatures.id_nomenclature"), + DB.ForeignKey("gn_monitoring.t_base_sites.id_base_site"), primary_key=True, ), DB.Column( - "id_categorie", + "id_type_site", DB.Integer, - DB.ForeignKey("gn_monitoring.bib_categorie_site.id_categorie"), + DB.ForeignKey("gn_monitoring.bib_type_site.id_nomenclature"), primary_key=True, ), schema="gn_monitoring") + +@serializable +class BibTypeSite(DB.Model): + __tablename__ = "bib_type_site" + __table_args__ = {"schema": "gn_monitoring"} + query_class = MonitoringQuery + + id_nomenclature = DB.Column(DB.ForeignKey("ref_nomenclatures.t_nomenclatures.id_nomenclature"), + nullable=False, + primary_key=True) + config = DB.Column(JSONB) + nomenclature = DB.relationship( + TNomenclatures, + uselist=False, + backref=DB.backref('bib_type_site', uselist=False) + ) + @serializable class TMonitoringObservationDetails(DB.Model): __tablename__ = "t_observation_details" @@ -214,6 +230,11 @@ class TMonitoringSites(TBaseSites): .where(TBaseSites.id_base_site == id_base_site) .correlate_except(TBaseSites) ) + types_site = DB.relationship( + "BibTypeSite", + secondary=cor_type_site, + lazy="joined" + ) @serializable @@ -320,6 +341,13 @@ class TMonitoringModules(TModules): lazy="joined", ) + types_site = DB.relationship( + "BibTypeSite", + secondary=cor_module_type, + lazy="joined" + ) + + data = DB.Column(JSONB) # visits = DB.relationship( diff --git a/backend/gn_module_monitoring/monitoring/schemas.py b/backend/gn_module_monitoring/monitoring/schemas.py index 2721de0e8..085af2751 100644 --- a/backend/gn_module_monitoring/monitoring/schemas.py +++ b/backend/gn_module_monitoring/monitoring/schemas.py @@ -6,7 +6,7 @@ from pypnnomenclature.schemas import NomenclatureSchema from gn_module_monitoring.monitoring.models import ( - BibCategorieSite, + BibTypeSite, TMonitoringSites, TMonitoringSitesGroups, ) @@ -46,12 +46,15 @@ def serialize_geojson(self, obj): return geojson.dumps(obj.as_geofeature().get("geometry")) -class BibCategorieSiteSchema(SQLAlchemyAutoSchema): - site_type = fields.Nested( - NomenclatureSchema(only=("id_nomenclature", "label_fr")), many=True, dump_only=True - ) +class BibTypeSiteSchema(SQLAlchemyAutoSchema): + label = fields.Method("get_label_from_type_site") + # See if useful in the future: + # type_site = fields.Nested(NomenclatureSchema(only=("label_fr",)), dump_only=True) + + def get_label_from_type_site(self, obj): + return obj.nomenclature.label_fr class Meta: - model = BibCategorieSite + model = BibTypeSite include_fk = True load_instance = True diff --git a/backend/gn_module_monitoring/routes/data_utils.py b/backend/gn_module_monitoring/routes/data_utils.py index 2038f5f34..37e850231 100644 --- a/backend/gn_module_monitoring/routes/data_utils.py +++ b/backend/gn_module_monitoring/routes/data_utils.py @@ -32,7 +32,7 @@ from ..blueprint import blueprint from ..config.repositories import get_config -from gn_module_monitoring.monitoring.models import TMonitoringSitesGroups, TMonitoringSites, BibCategorieSite +from gn_module_monitoring.monitoring.models import TMonitoringSitesGroups, TMonitoringSites, BibTypeSite model_dict = { "habitat": Habref, @@ -40,7 +40,7 @@ "user": User, "taxonomy": Taxref, "dataset": TDatasets, - "categorie": BibCategorieSite, + "types_site": BibTypeSite, "observer_list": UserList, "taxonomy_list": BibListes, "sites_group": TMonitoringSitesGroups, diff --git a/backend/gn_module_monitoring/routes/site.py b/backend/gn_module_monitoring/routes/site.py index 1a53ca316..989858feb 100644 --- a/backend/gn_module_monitoring/routes/site.py +++ b/backend/gn_module_monitoring/routes/site.py @@ -1,10 +1,9 @@ from flask import request from flask.json import jsonify -from geonature.core.gn_monitoring.models import TBaseSites from werkzeug.datastructures import MultiDict from gn_module_monitoring.blueprint import blueprint -from gn_module_monitoring.monitoring.models import BibCategorieSite, TMonitoringSites +from gn_module_monitoring.monitoring.models import BibTypeSite, TMonitoringSites from gn_module_monitoring.utils.routes import ( filter_params, get_limit_offset, @@ -12,33 +11,33 @@ paginate, sort, ) -from gn_module_monitoring.monitoring.schemas import MonitoringSitesSchema,BibCategorieSiteSchema +from gn_module_monitoring.monitoring.schemas import MonitoringSitesSchema,BibTypeSiteSchema -@blueprint.route("/sites/categories", methods=["GET"]) -def get_categories(): +@blueprint.route("/sites/types", methods=["GET"]) +def get_types_site(): params = MultiDict(request.args) limit, page = get_limit_offset(params=params) sort_label, sort_dir = get_sort( - params=params, default_sort="id_categorie", default_direction="desc" + params=params, default_sort="id_nomenclature", default_direction="desc" ) - query = filter_params(query=BibCategorieSite.query, params=params) + query = filter_params(query=BibTypeSite.query, params=params) query = sort(query=query, sort=sort_label, sort_dir=sort_dir) return paginate( query=query, - schema=BibCategorieSiteSchema, + schema=BibTypeSiteSchema, limit=limit, page=page, ) -@blueprint.route("/sites/categories/", methods=["GET"]) -def get_categories_by_id(id_categorie): - query = BibCategorieSite.query.filter_by(id_categorie=id_categorie) +@blueprint.route("/sites/types/", methods=["GET"]) +def get_type_site_by_id(id_type_site): + query = BibTypeSite.query.filter_by(id_nomenclature=id_type_site) res = query.first() - schema = BibCategorieSiteSchema() + schema = BibTypeSiteSchema() return schema.dump(res) @@ -50,9 +49,7 @@ def get_sites(): sort_label, sort_dir = get_sort( params=params, default_sort="id_base_site", default_direction="desc" ) - query = TMonitoringSites.query.join( - BibCategorieSite, TMonitoringSites.id_categorie == BibCategorieSite.id_categorie - ) + query = TMonitoringSites.query query = filter_params(query=query, params=params) query = sort(query=query, sort=sort_label, sort_dir=sort_dir) return paginate( diff --git a/backend/gn_module_monitoring/tests/conftest.py b/backend/gn_module_monitoring/tests/conftest.py index 6b1107a1d..422312d22 100644 --- a/backend/gn_module_monitoring/tests/conftest.py +++ b/backend/gn_module_monitoring/tests/conftest.py @@ -5,4 +5,5 @@ "gn_module_monitoring.tests.fixtures.module", "gn_module_monitoring.tests.fixtures.site", "gn_module_monitoring.tests.fixtures.sites_groups", + "gn_module_monitoring.tests.fixtures.type_site", ] diff --git a/backend/gn_module_monitoring/tests/fixtures/module.py b/backend/gn_module_monitoring/tests/fixtures/module.py index 41d946e37..29f678f25 100644 --- a/backend/gn_module_monitoring/tests/fixtures/module.py +++ b/backend/gn_module_monitoring/tests/fixtures/module.py @@ -4,18 +4,17 @@ from geonature.utils.env import db from gn_module_monitoring.monitoring.models import TMonitoringModules -from gn_module_monitoring.tests.fixtures.site import categories @pytest.fixture -def monitoring_module(categories): +def monitoring_module(types_site): t_monitoring_module = TMonitoringModules( module_code=uuid4(), module_label="test", active_frontend=True, active_backend=False, module_path="test", - categories=list(categories.values()), + types_site=list(types_site.values()), ) with db.session.begin_nested(): diff --git a/backend/gn_module_monitoring/tests/fixtures/site.py b/backend/gn_module_monitoring/tests/fixtures/site.py index 415ffe321..397b36703 100644 --- a/backend/gn_module_monitoring/tests/fixtures/site.py +++ b/backend/gn_module_monitoring/tests/fixtures/site.py @@ -1,45 +1,17 @@ import pytest from geoalchemy2.shape import from_shape from geonature.utils.env import db -from pypnnomenclature.models import BibNomenclaturesTypes, TNomenclatures from shapely.geometry import Point -from gn_module_monitoring.monitoring.models import BibCategorieSite, TMonitoringSites -from gn_module_monitoring.tests.fixtures.sites_groups import sites_groups +from gn_module_monitoring.monitoring.models import TMonitoringSites @pytest.fixture() -def site_type(): - return TNomenclatures.query.filter( - BibNomenclaturesTypes.mnemonique == "TYPE_SITE", TNomenclatures.mnemonique == "Grotte" - ).one() - - -@pytest.fixture() -def categories(site_type): - categories = [ - {"label": "gite", "config": {}, "site_type": [site_type]}, - {"label": "eolienne", "config": {}, "site_type": [site_type]}, - ] - - categories = {cat["label"]: BibCategorieSite(**cat) for cat in categories} - - with db.session.begin_nested(): - db.session.add_all(categories.values()) - - return categories - - -@pytest.fixture() -def sites(users, categories, sites_groups): +def sites(users, types_site, sites_groups): user = users["user"] geom_4326 = from_shape(Point(43, 24), srid=4326) sites = {} - # TODO: get_nomenclature from label - site_type = TNomenclatures.query.filter( - BibNomenclaturesTypes.mnemonique == "TYPE_SITE", TNomenclatures.mnemonique == "Grotte" - ).one() - for i, key in enumerate(categories.keys()): + for i, key in enumerate(types_site.keys()): sites[key] = TMonitoringSites( id_inventor=user.id_role, id_digitiser=user.id_role, @@ -47,8 +19,8 @@ def sites(users, categories, sites_groups): base_site_description=f"Description{i}", base_site_code=f"Code{i}", geom=geom_4326, - id_nomenclature_type_site=site_type.id_nomenclature, - id_categorie=categories[key].id_categorie, + id_nomenclature_type_site=types_site[key].id_nomenclature, + types_site=[types_site[key]], id_sites_group=sites_groups["Site_Groupe"].id_sites_group, ) with db.session.begin_nested(): diff --git a/backend/gn_module_monitoring/tests/fixtures/type_site.py b/backend/gn_module_monitoring/tests/fixtures/type_site.py new file mode 100644 index 000000000..36d419c86 --- /dev/null +++ b/backend/gn_module_monitoring/tests/fixtures/type_site.py @@ -0,0 +1,40 @@ +import pytest +from geonature.utils.env import db +from pypnnomenclature.models import BibNomenclaturesTypes, TNomenclatures + +from gn_module_monitoring.monitoring.models import BibTypeSite + + +@pytest.fixture +def nomenclature_types_site(): + mnemoniques = ("Test_Grotte", "Test_Mine") + nomenclatures = [] + type_site = BibNomenclaturesTypes.query.filter( + BibNomenclaturesTypes.mnemonique == "TYPE_SITE" + ).first() + for mnemo in mnemoniques: + nomenclatures.append( + TNomenclatures( + id_type=type_site.id_type, + cd_nomenclature=mnemo, + label_default=mnemo, + label_fr=mnemo, + active=True, + ) + ) + with db.session.begin_nested(): + db.session.add_all(nomenclatures) + return nomenclatures + + +@pytest.fixture +def types_site(nomenclature_types_site): + types_site = { + nomenc_type_site.mnemonique: BibTypeSite( + id_nomenclature=nomenc_type_site.id_nomenclature, config={} + ) + for nomenc_type_site in nomenclature_types_site + } + with db.session.begin_nested(): + db.session.add_all(types_site.values()) + return types_site diff --git a/backend/gn_module_monitoring/tests/test_monitoring/test_models/test_bib_type_site.py b/backend/gn_module_monitoring/tests/test_monitoring/test_models/test_bib_type_site.py new file mode 100644 index 000000000..ede321732 --- /dev/null +++ b/backend/gn_module_monitoring/tests/test_monitoring/test_models/test_bib_type_site.py @@ -0,0 +1,23 @@ +import pytest + +from gn_module_monitoring.monitoring.models import BibTypeSite + + +@pytest.mark.usefixtures("temporary_transaction") +class TestBibTypeSite: + def test_get_bib_type_site(self, types_site): + type_site = list(types_site.values())[0] + get_type_site = BibTypeSite.query.filter_by( + id_nomenclature=type_site.id_nomenclature + ).one() + + assert get_type_site.id_nomenclature == type_site.id_nomenclature + + def test_get_all_bib_type_site(self, types_site): + get_types_site = BibTypeSite.query.all() + + assert all( + type_site.id_nomenclature + in [get_type_site.id_nomenclature for get_type_site in get_types_site] + for type_site in types_site.values() + ) diff --git a/backend/gn_module_monitoring/tests/test_monitoring/test_models/test_module.py b/backend/gn_module_monitoring/tests/test_monitoring/test_models/test_module.py index fbf606816..978b9d1ca 100644 --- a/backend/gn_module_monitoring/tests/test_monitoring/test_models/test_module.py +++ b/backend/gn_module_monitoring/tests/test_monitoring/test_models/test_module.py @@ -6,14 +6,14 @@ @pytest.mark.usefixtures("temporary_transaction") class TestModule: - def test_module(self, monitoring_module, categories): - cats = monitoring_module.categories - assert cats == list(categories.values()) + def test_module(self, monitoring_module, types_site): + types = monitoring_module.types_site + assert types == list(types_site.values()) - def test_remove_categorie_from_module(self, monitoring_module, categories): + def test_remove_categorie_from_module(self, monitoring_module, types_site): with db.session.begin_nested(): - monitoring_module.categories.pop(0) + monitoring_module.types_site.pop(0) mon = TMonitoringModules.query.filter_by(id_module=monitoring_module.id_module).one() - assert len(mon.categories) == len(categories) - 1 + assert len(mon.types_site) == len(types_site) - 1 diff --git a/backend/gn_module_monitoring/tests/test_monitoring/test_schemas/test_bib_site_type_schema.py b/backend/gn_module_monitoring/tests/test_monitoring/test_schemas/test_bib_site_type_schema.py new file mode 100644 index 000000000..e3852cdb1 --- /dev/null +++ b/backend/gn_module_monitoring/tests/test_monitoring/test_schemas/test_bib_site_type_schema.py @@ -0,0 +1,14 @@ +import pytest + +from gn_module_monitoring.monitoring.models import BibTypeSite +from gn_module_monitoring.monitoring.schemas import BibTypeSiteSchema + + +@pytest.mark.usefixtures("temporary_transaction") +class TestBibSiteTypeSchema: + def test_dump(self, types_site): + one_type_site = BibTypeSite.query.first() + schema = BibTypeSiteSchema() + type_site = schema.dump(one_type_site) + + assert type_site["id_nomenclature"] == one_type_site.id_nomenclature diff --git a/backend/gn_module_monitoring/tests/test_routes/test_site.py b/backend/gn_module_monitoring/tests/test_routes/test_site.py index ecbbb3b68..59733d2c9 100644 --- a/backend/gn_module_monitoring/tests/test_routes/test_site.py +++ b/backend/gn_module_monitoring/tests/test_routes/test_site.py @@ -1,34 +1,28 @@ import pytest from flask import url_for -from gn_module_monitoring.monitoring.schemas import BibCategorieSiteSchema, MonitoringSitesSchema +from gn_module_monitoring.monitoring.schemas import BibTypeSiteSchema, MonitoringSitesSchema @pytest.mark.usefixtures("client_class", "temporary_transaction") class TestSite: - def test_get_categories_by_id(self, categories): - for cat in categories.values(): + def test_get_type_site_by_id(self, types_site): + for type_site in types_site.values(): r = self.client.get( url_for( - "monitorings.get_categories_by_id", - id_categorie=cat.id_categorie, + "monitorings.get_type_site_by_id", + id_type_site=type_site.id_nomenclature, ) ) - assert r.json["label"] == cat.label + assert r.json["id_nomenclature"] == type_site.id_nomenclature - def test_get_categories(self, categories): - schema = BibCategorieSiteSchema() + def test_get_types_site(self, types_site): + schema = BibTypeSiteSchema() - r = self.client.get(url_for("monitorings.get_categories")) + r = self.client.get(url_for("monitorings.get_types_site")) - assert r.json["count"] >= len(categories) - assert all([schema.dump(cat) in r.json["items"] for cat in categories.values()]) - - def test_get_categories_label(self, categories): - label = list(categories.keys())[0] - schema = BibCategorieSiteSchema() - r = self.client.get(url_for("monitorings.get_categories"), query_string={"label": label}) - assert schema.dump(categories[label]) in r.json["items"] + assert r.json["count"] >= len(types_site) + assert all([schema.dump(cat) in r.json["items"] for cat in types_site.values()]) def test_get_sites(self, sites): schema = MonitoringSitesSchema()