diff --git a/backend/gn_module_monitoring/blueprint.py b/backend/gn_module_monitoring/blueprint.py index 32c0d5ae5..354791bd4 100644 --- a/backend/gn_module_monitoring/blueprint.py +++ b/backend/gn_module_monitoring/blueprint.py @@ -4,6 +4,10 @@ """ from flask import Blueprint, current_app +from geonature.core.admin.admin import admin as flask_admin +from geonature.utils.env import DB + +from gn_module_monitoring.monitoring.admin import BibTypeSiteView from .command.cmd import commands blueprint = Blueprint("monitorings", __name__) @@ -12,3 +16,5 @@ blueprint.cli.short_help = "Commandes pour l" "administration du module MONITORINGS" for cmd in commands: blueprint.cli.add_command(cmd) + +flask_admin.add_view(BibTypeSiteView(DB.session, name="Types de site", category="Monitorings")) diff --git a/backend/gn_module_monitoring/conf_schema_toml.py b/backend/gn_module_monitoring/conf_schema_toml.py index e89328a69..0d9b3039a 100644 --- a/backend/gn_module_monitoring/conf_schema_toml.py +++ b/backend/gn_module_monitoring/conf_schema_toml.py @@ -4,11 +4,14 @@ Fichier à ne pas modifier. Paramètres surcouchables dans config/config_gn_module.tml """ -from marshmallow import Schema, fields, validates_schema, ValidationError +from marshmallow import Schema, fields class GnModuleSchemaConf(Schema): - pass + DESCRIPTION_MODULE = fields.String(missing="Vous trouverez ici la liste des modules") + TITLE_MODULE = fields.String(missing="Module de suivi") + + # AREA_TYPE = fields.List(fields.String(), missing=["COM", "M1", "M5", "M10"]) # BORNE_OBS = fields.List(fields.Integer(), missing=[1, 20, 40, 60, 80, 100, 120]) # BORNE_TAXON = fields.List(fields.Integer(), missing=[1, 5, 10, 15]) 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 new file mode 100644 index 000000000..418234cdd --- /dev/null +++ b/backend/gn_module_monitoring/migrations/6673266fb79c_remove_id_module_from_sites_complements.py @@ -0,0 +1,54 @@ +"""remove_id_module_from_sites_complements + +Revision ID: 6673266fb79c +Revises: a54bafb13ce8 +Create Date: 2022-12-13 16:00:00.512562 + +""" +import sqlalchemy as sa +from alembic import op + +from gn_module_monitoring import MODULE_CODE + +# revision identifiers, used by Alembic. +revision = "6673266fb79c" +down_revision = "a54bafb13ce8" +branch_labels = None +depends_on = None + +monitorings_schema = "gn_monitoring" + + +def upgrade(): + op.drop_column("t_site_complements", "id_module", schema=monitorings_schema) + + +def downgrade(): + op.add_column( + "t_site_complements", + sa.Column( + "id_module", + sa.Integer(), + sa.ForeignKey( + f"gn_commons.t_modules.id_module", + name="fk_t_site_complements_id_module", + ondelete="CASCADE", + onupdate="CASCADE", + ), + nullable=True, + ), + schema=monitorings_schema, + ) + # Cannot use orm here because need the model to be "downgraded" as well + # Need to set nullable True above for existing rows + # FIXME: find a better way because need to assign a module... + statement = sa.text( + f""" + update {monitorings_schema}.t_site_complements + set id_module = (select id_module + from gn_commons.t_modules tm + where module_code = :module_code); + """ + ).bindparams(module_code=MODULE_CODE) + op.execute(statement) + op.alter_column("t_site_complements", "id_module", nullable=False, schema=monitorings_schema) diff --git a/backend/gn_module_monitoring/migrations/a54bafb13ce8_create_cor_module_type.py b/backend/gn_module_monitoring/migrations/a54bafb13ce8_create_cor_module_type.py new file mode 100644 index 000000000..5bb62791f --- /dev/null +++ b/backend/gn_module_monitoring/migrations/a54bafb13ce8_create_cor_module_type.py @@ -0,0 +1,52 @@ +"""create_cor_module_type + +Revision ID: a54bafb13ce8 +Revises: ce54ba49ce5c +Create Date: 2022-12-06 16:18:24.512562 + +""" +from alembic import op +import sqlalchemy as sa + +# revision identifiers, used by Alembic. +revision = "a54bafb13ce8" +down_revision = "ce54ba49ce5c" +branch_labels = None +depends_on = None + +monitorings_schema = "gn_monitoring" +referent_schema = "gn_commons" + + +def upgrade(): + op.create_table( + "cor_module_type", + sa.Column( + "id_type_site", + sa.Integer(), + sa.ForeignKey( + f"{monitorings_schema}.bib_type_site.id_nomenclature_type_site", + name="fk_cor_module_type_id_nomenclature_type_site", + ondelete="CASCADE", + onupdate="CASCADE", + ), + nullable=False, + ), + sa.Column( + "id_module", + sa.Integer(), + sa.ForeignKey( + f"{referent_schema}.t_modules.id_module", + name="fk_cor_module_type_id_module", + ondelete="CASCADE", + onupdate="CASCADE", + ), + nullable=False, + ), + sa.PrimaryKeyConstraint("id_type_site", "id_module", name="pk_cor_module_type"), + schema=monitorings_schema, + ) + + +def downgrade(): + op.drop_table("cor_module_type", 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..b079edf9e --- /dev/null +++ b/backend/gn_module_monitoring/migrations/b53bafb13ce8_create_bib_type_site.py @@ -0,0 +1,62 @@ +"""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" + + +def upgrade(): + op.create_table( + "bib_type_site", + sa.Column( + "id_nomenclature_type_site", + sa.Integer(), + sa.ForeignKey( + f"{nomenclature_schema}.t_nomenclatures.id_nomenclature", + name="fk_t_nomenclatures_id_nomenclature_type_site", + ), + nullable=False, + unique=True, + ), + sa.PrimaryKeyConstraint("id_nomenclature_type_site"), + sa.Column("config", sa.JSON(), nullable=True), + schema=monitorings_schema, + ) + + # FIXME: if sqlalchemy >= 1.4.32, it should work with postgresql_not_valid=True: cleaner + # op.create_check_constraint( + # "ck_bib_type_site_id_nomenclature_type_site", + # "bib_type_site", + # f"{nomenclature_schema}.check_nomenclature_type_by_mnemonique(id_nomenclature_type_site,'TYPE_SITE')", + # schema=monitorings_schema, + # postgresql_not_valid=True + # ) + statement = sa.text( + f""" + ALTER TABLE {monitorings_schema}.bib_type_site + ADD + CONSTRAINT ck_bib_type_site_id_nomenclature_type_site CHECK ( + {nomenclature_schema}.check_nomenclature_type_by_mnemonique( + id_nomenclature_type_site, 'TYPE_SITE' :: character varying + ) + ) NOT VALID + """ + ) + op.execute(statement) + + +def downgrade(): + op.drop_table("bib_type_site", schema=monitorings_schema) 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..f02e267e8 --- /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_type_site", + name="fk_cor_type_site_id_nomenclature_type_site", + 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/f24adb481f54_remove_id_module_from_sites_groups.py b/backend/gn_module_monitoring/migrations/f24adb481f54_remove_id_module_from_sites_groups.py new file mode 100644 index 000000000..9280f92ed --- /dev/null +++ b/backend/gn_module_monitoring/migrations/f24adb481f54_remove_id_module_from_sites_groups.py @@ -0,0 +1,54 @@ +"""remove_id_module_from_sites_groups + +Revision ID: f24adb481f54 +Revises: 6673266fb79c +Create Date: 2022-12-13 16:00:00.512562 + +""" +import sqlalchemy as sa +from alembic import op + +from gn_module_monitoring import MODULE_CODE + +# revision identifiers, used by Alembic. +revision = "f24adb481f54" +down_revision = "6673266fb79c" +branch_labels = None +depends_on = None + +monitorings_schema = "gn_monitoring" + + +def upgrade(): + op.drop_column("t_sites_groups", "id_module", schema=monitorings_schema) + + +def downgrade(): + op.add_column( + "t_sites_groups", + sa.Column( + "id_module", + sa.Integer(), + sa.ForeignKey( + f"gn_commons.t_modules.id_module", + name="fk_t_sites_groups_id_module", + ondelete="CASCADE", + onupdate="CASCADE", + ), + nullable=True, + ), + schema=monitorings_schema, + ) + # Cannot use orm here because need the model to be "downgraded" as well + # Need to set nullable True above for existing rows + # FIXME: find a better way because need to assign a module... + statement = sa.text( + f""" + update {monitorings_schema}.t_sites_groups + set id_module = (select id_module + from gn_commons.t_modules tm + where module_code = :module_code); + """ + ).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/modules/repositories.py b/backend/gn_module_monitoring/modules/repositories.py index 303c6d73d..feaa0db4a 100644 --- a/backend/gn_module_monitoring/modules/repositories.py +++ b/backend/gn_module_monitoring/modules/repositories.py @@ -4,6 +4,7 @@ get_modules """ +from sqlalchemy.orm import Load from sqlalchemy.orm.exc import MultipleResultsFound, NoResultFound from geonature.utils.env import DB @@ -87,7 +88,12 @@ def get_modules(): try: res = ( - DB.session.query(TMonitoringModules).order_by(TMonitoringModules.module_label) + DB.session.query(TMonitoringModules) + .options( + # Raise load not to load any relationship + Load(TMonitoringModules).raiseload("*") + ) + .order_by(TMonitoringModules.module_label) .all() ) diff --git a/backend/gn_module_monitoring/monitoring/admin.py b/backend/gn_module_monitoring/monitoring/admin.py new file mode 100644 index 000000000..5e77d0a0a --- /dev/null +++ b/backend/gn_module_monitoring/monitoring/admin.py @@ -0,0 +1,75 @@ +from flask_admin.contrib.sqla import ModelView +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 BibTypeSite + + +SITE_TYPE = "TYPE_SITE" + + +class Unique: + """validator that checks field uniqueness""" + + def __init__(self, model, field, compare_field, message=None): + self.model = model + self.field = field + self.compare_field = compare_field + if not message: + message = "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.compare_field) + ).first(): + raise ValidationError(self.message) + + +class BibTypeSiteView(CruvedProtectedMixin, ModelView): + """ + Surcharge de l'administration des types de sites + """ + + module_code = "MONITORINGS" + object_code = None + + def __init__(self, session, **kwargs): + # Référence au model utilisé + super(BibTypeSiteView, self).__init__(BibTypeSite, session, **kwargs) + + def get_only_nomenclature_asc(): + return ( + DB.session.query(TNomenclatures) + .join(TNomenclatures.nomenclature_type) + .filter(BibNomenclaturesTypes.mnemonique == SITE_TYPE) + .order_by(TNomenclatures.label_fr.asc()) + ) + + def get_label_fr_nomenclature(x): + return x.label_fr + + def list_label_nomenclature_formatter(view, _context, model, _name): + return model.nomenclature.label_fr + + # Nom de colonne user friendly + column_labels = dict(nomenclature="Types de site") + # Description des colonnes + column_descriptions = dict(nomenclature="Nomenclature de type de site à choisir") + + column_hide_backrefs = False + + form_args = dict( + nomenclature=dict( + query_factory=get_only_nomenclature_asc, + get_label=get_label_fr_nomenclature, + validators=[Unique(BibTypeSite, "id_nomenclature_type_site", "id_nomenclature")], + ) + ) + + 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 3eae53aa6..1aa51c989 100644 --- a/backend/gn_module_monitoring/monitoring/models.py +++ b/backend/gn_module_monitoring/monitoring/models.py @@ -11,7 +11,7 @@ from sqlalchemy.ext.hybrid import hybrid_property - +from pypnnomenclature.models import TNomenclatures, BibNomenclaturesTypes from geonature.core.gn_commons.models import TMedias from geonature.core.gn_monitoring.models import TBaseSites, TBaseVisits from geonature.core.gn_meta.models import TDatasets @@ -19,7 +19,55 @@ 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_type = DB.Table( + "cor_module_type", + DB.Column( + "id_module", + DB.Integer, + DB.ForeignKey("gn_commons.t_modules.id_module"), + primary_key=True, + ), + DB.Column( + "id_type_site", + DB.Integer, + DB.ForeignKey("gn_monitoring.bib_type_site.id_nomenclature_type_site"), + primary_key=True, + ), schema="gn_monitoring") + +cor_type_site = DB.Table( + "cor_type_site", + DB.Column( + "id_base_site", + DB.Integer, + DB.ForeignKey("gn_monitoring.t_base_sites.id_base_site"), + primary_key=True, + ), + DB.Column( + "id_type_site", + DB.Integer, + DB.ForeignKey("gn_monitoring.bib_type_site.id_nomenclature_type_site"), + 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_type_site = 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" @@ -135,7 +183,7 @@ class TMonitoringVisits(TBaseVisits): ) -@geoserializable +@geoserializable(geoCol="geom", idCol="id_base_site") class TMonitoringSites(TBaseSites): __tablename__ = 't_site_complements' @@ -143,6 +191,7 @@ class TMonitoringSites(TBaseSites): __mapper_args__ = { 'polymorphic_identity': 'monitoring_site', } + query_class = MonitoringQuery id_base_site = DB.Column( DB.ForeignKey('gn_monitoring.t_base_sites.id_base_site'), @@ -150,10 +199,6 @@ class TMonitoringSites(TBaseSites): primary_key=True ) - id_module = DB.Column( - DB.ForeignKey('gn_commons.t_modules.id_module'), - nullable=False, - ) id_sites_group = DB.Column( DB.ForeignKey('gn_monitoring.t_sites_groups.id_sites_group', @@ -195,11 +240,17 @@ 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 class TMonitoringSitesGroups(DB.Model): __tablename__ = 't_sites_groups' __table_args__ = {'schema': 'gn_monitoring'} + query_class = MonitoringQuery id_sites_group = DB.Column( DB.Integer, @@ -208,12 +259,6 @@ class TMonitoringSitesGroups(DB.Model): unique=True ) - id_module = DB.Column( - DB.ForeignKey('gn_commons.t_modules.id_module'), - nullable=False, - unique=True - ) - uuid_sites_group = DB.Column(UUID(as_uuid=True), default=uuid4) sites_group_name = DB.Column(DB.Unicode) @@ -287,21 +332,22 @@ class TMonitoringModules(TModules): lazy='joined' ) - sites = DB.relationship( - 'TMonitoringSites', - uselist=True, # pourquoi pas par defaut ? - primaryjoin=TMonitoringSites.id_module == id_module, - foreign_keys=[id_module], - lazy="select", - ) + # TODO: restore it with CorCategorySite + # sites = DB.relationship( + # 'TMonitoringSites', + # uselist=True, # pourquoi pas par defaut ? + # primaryjoin=TMonitoringSites.id_module == id_module, + # foreign_keys=[id_module], + # lazy="select", + # ) - sites_groups = DB.relationship( - 'TMonitoringSitesGroups', - uselist=True, # pourquoi pas par defaut ? - primaryjoin=TMonitoringSitesGroups.id_module == id_module, - foreign_keys=[id_module], - lazy="select", - ) + # sites_groups = DB.relationship( + # 'TMonitoringSitesGroups', + # uselist=True, # pourquoi pas par defaut ? + # primaryjoin=TMonitoringSitesGroups.id_module == id_module, + # foreign_keys=[id_module], + # lazy="select", + # ) datasets = DB.relationship( 'TDatasets', @@ -310,6 +356,12 @@ class TMonitoringModules(TModules): lazy="joined", ) + types_site = DB.relationship( + "BibTypeSite", + secondary=cor_module_type, + lazy="joined" + ) + data = DB.Column(JSONB) diff --git a/backend/gn_module_monitoring/monitoring/queries.py b/backend/gn_module_monitoring/monitoring/queries.py new file mode 100644 index 000000000..768fe2ad1 --- /dev/null +++ b/backend/gn_module_monitoring/monitoring/queries.py @@ -0,0 +1,37 @@ +from flask_sqlalchemy import BaseQuery +from sqlalchemy import Unicode, and_ +from werkzeug.datastructures import MultiDict + + +class Query(BaseQuery): + def _get_entity(self, entity): + if hasattr(entity, "_entities"): + return self._get_entity(entity._entities[0]) + return entity.entities[0] + + def _get_model(self): + # When sqlalchemy is updated: + # return self._raw_columns[0].entity_namespace + # But for now: + entity = self._get_entity(self) + return entity.c + + def filter_by_params(self, params: MultiDict = None): + model = self._get_model() + and_list = [] + for key, value in params.items(): + column = getattr(model, key) + if isinstance(column.type, Unicode): + and_list.append(column.ilike(f"%{value}%")) + else: + and_list.append(column == value) + and_query = and_(*and_list) + return self.filter(and_query) + + def sort(self, label: str, direction: str): + model = self._get_model() + order_by = getattr(model, label) + if direction == "desc": + order_by = order_by.desc() + + return self.order_by(order_by) diff --git a/backend/gn_module_monitoring/monitoring/schemas.py b/backend/gn_module_monitoring/monitoring/schemas.py new file mode 100644 index 000000000..f34066c55 --- /dev/null +++ b/backend/gn_module_monitoring/monitoring/schemas.py @@ -0,0 +1,60 @@ +import json + +import geojson +from marshmallow import Schema, fields +from marshmallow_sqlalchemy import SQLAlchemyAutoSchema +from pypnnomenclature.schemas import NomenclatureSchema + +from gn_module_monitoring.monitoring.models import ( + BibTypeSite, + TMonitoringSites, + TMonitoringSitesGroups, +) + + +def paginate_schema(schema): + class PaginationSchema(Schema): + count = fields.Integer() + limit = fields.Integer() + page = fields.Integer() + items = fields.Nested(schema, many=True, dump_only=True) + + return PaginationSchema + + +class MonitoringSitesGroupsSchema(SQLAlchemyAutoSchema): + class Meta: + model = TMonitoringSitesGroups + exclude = ("geom_geojson",) + + geometry = fields.Method("serialize_geojson", dump_only=True) + + def serialize_geojson(self, obj): + if obj.geom_geojson is not None: + return json.loads(obj.geom_geojson) + + +class MonitoringSitesSchema(SQLAlchemyAutoSchema): + class Meta: + model = TMonitoringSites + exclude = ("geom_geojson", "geom") + + geometry = fields.Method("serialize_geojson", dump_only=True) + + def serialize_geojson(self, obj): + if obj.geom is not None: + return geojson.dumps(obj.as_geofeature().get("geometry")) + + +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 = 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 3451ed6ad..6f2731070 100644 --- a/backend/gn_module_monitoring/routes/data_utils.py +++ b/backend/gn_module_monitoring/routes/data_utils.py @@ -33,8 +33,7 @@ from ..blueprint import blueprint from ..config.repositories import get_config - -from ..monitoring.models import TMonitoringSitesGroups, TMonitoringSites +from gn_module_monitoring.monitoring.models import TMonitoringSitesGroups, TMonitoringSites, BibTypeSite model_dict = { "habitat": Habref, @@ -42,6 +41,7 @@ "user": User, "taxonomy": Taxref, "dataset": TDatasets, + "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 new file mode 100644 index 000000000..92a209ad0 --- /dev/null +++ b/backend/gn_module_monitoring/routes/site.py @@ -0,0 +1,66 @@ +from flask import request +from flask.json import jsonify +from werkzeug.datastructures import MultiDict + +from gn_module_monitoring.blueprint import blueprint +from gn_module_monitoring.monitoring.models import BibTypeSite, TMonitoringSites +from gn_module_monitoring.utils.routes import ( + filter_params, + get_limit_page, + get_sort, + paginate, + sort, +) +from gn_module_monitoring.monitoring.schemas import MonitoringSitesSchema,BibTypeSiteSchema + + +@blueprint.route("/sites/types", methods=["GET"]) +def get_types_site(): + params = MultiDict(request.args) + limit, page = get_limit_page(params=params) + sort_label, sort_dir = get_sort( + params=params, default_sort="id_nomenclature_type_site", default_direction="desc" + ) + + query = filter_params(query=BibTypeSite.query, params=params) + query = sort(query=query, sort=sort_label, sort_dir=sort_dir) + + return paginate( + query=query, + schema=BibTypeSiteSchema, + limit=limit, + page=page, + ) + + +@blueprint.route("/sites/types/", methods=["GET"]) +def get_type_site_by_id(id_type_site): + query = BibTypeSite.query.filter_by(id_nomenclature_type_site=id_type_site) + res = query.first() + schema = BibTypeSiteSchema() + return schema.dump(res) + + +@blueprint.route("/sites", methods=["GET"]) +def get_sites(): + params = MultiDict(request.args) + # TODO: add filter support + limit, page = get_limit_page(params=params) + sort_label, sort_dir = get_sort( + params=params, default_sort="id_base_site", default_direction="desc" + ) + query = TMonitoringSites.query + query = filter_params(query=query, params=params) + query = sort(query=query, sort=sort_label, sort_dir=sort_dir) + return paginate( + query=query, + schema=MonitoringSitesSchema, + limit=limit, + page=page, + ) + + +@blueprint.route("/sites/module/", methods=["GET"]) +def get_module_sites(module_code: str): + # TODO: load with site_categories.json API + return jsonify({"module_code": module_code}) diff --git a/backend/gn_module_monitoring/routes/sites_groups.py b/backend/gn_module_monitoring/routes/sites_groups.py new file mode 100644 index 000000000..e999c2351 --- /dev/null +++ b/backend/gn_module_monitoring/routes/sites_groups.py @@ -0,0 +1,31 @@ +from flask import request +from werkzeug.datastructures import MultiDict + +from gn_module_monitoring.blueprint import blueprint +from gn_module_monitoring.monitoring.models import TMonitoringSitesGroups +from gn_module_monitoring.utils.routes import ( + filter_params, + get_limit_page, + get_sort, + paginate, + sort, +) +from gn_module_monitoring.monitoring.schemas import MonitoringSitesGroupsSchema + + +@blueprint.route("/sites_groups", methods=["GET"]) +def get_sites_groups(): + params = MultiDict(request.args) + limit, page = get_limit_page(params=params) + sort_label, sort_dir = get_sort( + params=params, default_sort="id_sites_group", default_direction="desc" + ) + query = filter_params(query=TMonitoringSitesGroups.query, params=params) + + query = sort(query=query, sort=sort_label, sort_dir=sort_dir) + return paginate( + query=query, + schema=MonitoringSitesGroupsSchema, + limit=limit, + page=page, + ) diff --git a/backend/gn_module_monitoring/tests/__init__.py b/backend/gn_module_monitoring/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/backend/gn_module_monitoring/tests/conftest.py b/backend/gn_module_monitoring/tests/conftest.py new file mode 100644 index 000000000..422312d22 --- /dev/null +++ b/backend/gn_module_monitoring/tests/conftest.py @@ -0,0 +1,9 @@ +from geonature.tests.fixtures import * +from geonature.tests.fixtures import _session, app, users + +pytest_plugins = [ + "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/__init__.py b/backend/gn_module_monitoring/tests/fixtures/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/backend/gn_module_monitoring/tests/fixtures/module.py b/backend/gn_module_monitoring/tests/fixtures/module.py new file mode 100644 index 000000000..29f678f25 --- /dev/null +++ b/backend/gn_module_monitoring/tests/fixtures/module.py @@ -0,0 +1,23 @@ +from uuid import uuid4 + +import pytest +from geonature.utils.env import db + +from gn_module_monitoring.monitoring.models import TMonitoringModules + + +@pytest.fixture +def monitoring_module(types_site): + t_monitoring_module = TMonitoringModules( + module_code=uuid4(), + module_label="test", + active_frontend=True, + active_backend=False, + module_path="test", + types_site=list(types_site.values()), + ) + + with db.session.begin_nested(): + db.session.add(t_monitoring_module) + + return t_monitoring_module diff --git a/backend/gn_module_monitoring/tests/fixtures/site.py b/backend/gn_module_monitoring/tests/fixtures/site.py new file mode 100644 index 000000000..e3d16d973 --- /dev/null +++ b/backend/gn_module_monitoring/tests/fixtures/site.py @@ -0,0 +1,28 @@ +import pytest +from geoalchemy2.shape import from_shape +from geonature.utils.env import db +from shapely.geometry import Point + +from gn_module_monitoring.monitoring.models import TMonitoringSites + + +@pytest.fixture() +def sites(users, types_site, sites_groups): + user = users["user"] + geom_4326 = from_shape(Point(43, 24), srid=4326) + sites = {} + for i, key in enumerate(types_site.keys()): + sites[key] = TMonitoringSites( + id_inventor=user.id_role, + id_digitiser=user.id_role, + base_site_name=f"Site{i}", + base_site_description=f"Description{i}", + base_site_code=f"Code{i}", + geom=geom_4326, + id_nomenclature_type_site=types_site[key].id_nomenclature_type_site, + types_site=[types_site[key]], + id_sites_group=sites_groups["Site_Groupe"].id_sites_group, + ) + with db.session.begin_nested(): + db.session.add_all(sites.values()) + return sites diff --git a/backend/gn_module_monitoring/tests/fixtures/sites_groups.py b/backend/gn_module_monitoring/tests/fixtures/sites_groups.py new file mode 100644 index 000000000..bfadf071e --- /dev/null +++ b/backend/gn_module_monitoring/tests/fixtures/sites_groups.py @@ -0,0 +1,16 @@ +import pytest +from geonature.utils.env import db + +from gn_module_monitoring.monitoring.models import TMonitoringSitesGroups + + +@pytest.fixture +def sites_groups(): + names = ["Site_eolien", "Site_Groupe"] + + groups = {name: TMonitoringSitesGroups(sites_group_name=name) for name in names} + + with db.session.begin_nested(): + db.session.add_all(groups.values()) + + return groups 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..1a213a037 --- /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_type_site=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/__init__.py b/backend/gn_module_monitoring/tests/test_monitoring/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/backend/gn_module_monitoring/tests/test_monitoring/test_models/__init__.py b/backend/gn_module_monitoring/tests/test_monitoring/test_models/__init__.py new file mode 100644 index 000000000..e69de29bb 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..ae580ccbf --- /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=type_site.id_nomenclature_type_site + ).one() + + assert get_type_site.id_nomenclature_type_site == type_site.id_nomenclature_type_site + + def test_get_all_bib_type_site(self, types_site): + get_types_site = BibTypeSite.query.all() + + assert all( + type_site.id_nomenclature_type_site + in [get_type_site.id_nomenclature_type_site 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 new file mode 100644 index 000000000..978b9d1ca --- /dev/null +++ b/backend/gn_module_monitoring/tests/test_monitoring/test_models/test_module.py @@ -0,0 +1,19 @@ +import pytest +from geonature.utils.env import db + +from gn_module_monitoring.monitoring.models import TMonitoringModules + + +@pytest.mark.usefixtures("temporary_transaction") +class TestModule: + 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, types_site): + with db.session.begin_nested(): + monitoring_module.types_site.pop(0) + + mon = TMonitoringModules.query.filter_by(id_module=monitoring_module.id_module).one() + + assert len(mon.types_site) == len(types_site) - 1 diff --git a/backend/gn_module_monitoring/tests/test_monitoring/test_models/test_sites_groups.py b/backend/gn_module_monitoring/tests/test_monitoring/test_models/test_sites_groups.py new file mode 100644 index 000000000..99a54f811 --- /dev/null +++ b/backend/gn_module_monitoring/tests/test_monitoring/test_models/test_sites_groups.py @@ -0,0 +1,36 @@ +import pytest + +from gn_module_monitoring.monitoring.models import TMonitoringSitesGroups + + +@pytest.mark.usefixtures("temporary_transaction") +class TestTMonitoringSitesGroups: + def test_sort_desc(self, sites_groups): + if len(sites_groups) < 2: + pytest.xfail( + "This test cannot work if there is less than 2 sites_groups in database (via fixtures or not)" + ) + + query = TMonitoringSitesGroups.query.filter( + TMonitoringSitesGroups.id_sites_group.in_( + group.id_sites_group for group in sites_groups.values() + ) + ).sort(label="id_sites_group", direction="desc") + result = query.all() + + assert result[0].id_sites_group > result[1].id_sites_group + + def test_sort_asc(self, sites_groups): + if len(sites_groups) < 2: + pytest.xfail( + "This test cannot work if there is less than 2 sites_groups in database (via fixtures or not)" + ) + + query = TMonitoringSitesGroups.query.filter( + TMonitoringSitesGroups.id_sites_group.in_( + group.id_sites_group for group in sites_groups.values() + ) + ).sort(label="id_sites_group", direction="asc") + result = query.all() + + assert result[0].id_sites_group < result[1].id_sites_group 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..91aa19e0f --- /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_type_site"] == one_type_site.id_nomenclature_type_site diff --git a/backend/gn_module_monitoring/tests/test_routes/__init__.py b/backend/gn_module_monitoring/tests/test_routes/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/backend/gn_module_monitoring/tests/test_routes/test_site.py b/backend/gn_module_monitoring/tests/test_routes/test_site.py new file mode 100644 index 000000000..07448b1a3 --- /dev/null +++ b/backend/gn_module_monitoring/tests/test_routes/test_site.py @@ -0,0 +1,64 @@ +import pytest +from flask import url_for + +from gn_module_monitoring.monitoring.schemas import BibTypeSiteSchema, MonitoringSitesSchema + + +@pytest.mark.usefixtures("client_class", "temporary_transaction") +class TestSite: + 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_type_site_by_id", + id_type_site=type_site.id_nomenclature_type_site, + ) + ) + assert r.json["id_nomenclature_type_site"] == type_site.id_nomenclature_type_site + + def test_get_types_site(self, types_site): + schema = BibTypeSiteSchema() + + r = self.client.get(url_for("monitorings.get_types_site")) + + 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() + + r = self.client.get(url_for("monitorings.get_sites")) + + assert r.json["count"] >= len(sites) + assert any([schema.dump(site) in r.json["items"] for site in sites.values()]) + + def test_get_sites_limit(self, sites): + limit = 34 + + r = self.client.get(url_for("monitorings.get_sites", limit=limit)) + + assert len(r.json["items"]) == limit + + def test_get_sites_base_site_name(self, sites): + site = list(sites.values())[0] + base_site_name = site.base_site_name + + r = self.client.get(url_for("monitorings.get_sites", base_site_name=base_site_name)) + + assert len(r.json["items"]) == 1 + assert r.json["items"][0]["base_site_name"] == base_site_name + + def test_get_sites_id_base_site(self, sites): + site = list(sites.values())[0] + id_base_site = site.id_base_site + + r = self.client.get(url_for("monitorings.get_sites", id_base_site=id_base_site)) + + assert len(r.json["items"]) == 1 + assert r.json["items"][0]["id_base_site"] == id_base_site + + def test_get_module_sites(self): + module_code = "TEST" + r = self.client.get(url_for("monitorings.get_module_sites", module_code=module_code)) + + assert r.json["module_code"] == module_code diff --git a/backend/gn_module_monitoring/tests/test_routes/test_sites_groups.py b/backend/gn_module_monitoring/tests/test_routes/test_sites_groups.py new file mode 100644 index 000000000..a81abf66d --- /dev/null +++ b/backend/gn_module_monitoring/tests/test_routes/test_sites_groups.py @@ -0,0 +1,41 @@ +import pytest +from flask import url_for + +from gn_module_monitoring.monitoring.models import TMonitoringSitesGroups +from gn_module_monitoring.monitoring.schemas import MonitoringSitesGroupsSchema + + +@pytest.mark.usefixtures("client_class", "temporary_transaction") +class TestSitesGroups: + def test_get_sites_groups(self, sites_groups): + r = self.client.get(url_for("monitorings.get_sites_groups")) + + assert r.json["count"] >= len(sites_groups) + assert all( + [ + MonitoringSitesGroupsSchema().dump(group) in r.json["items"] + for group in sites_groups.values() + ] + ) + + def test_get_sites_groups_filter_name(self, sites_groups): + name, name_not_present = list(sites_groups.keys()) + schema = MonitoringSitesGroupsSchema() + + r = self.client.get( + url_for("monitorings.get_sites_groups"), query_string={"sites_group_name": name} + ) + + assert r.json["count"] >= 1 + json_sites_groups = r.json["items"] + assert schema.dump(sites_groups[name]) in json_sites_groups + assert schema.dump(sites_groups[name_not_present]) not in json_sites_groups + + def test_serialize_sites_groups(self, sites_groups, sites): + groups = TMonitoringSitesGroups.query.filter( + TMonitoringSitesGroups.id_sites_group.in_( + [s.id_sites_group for s in sites_groups.values()] + ) + ).all() + schema = MonitoringSitesGroupsSchema() + assert [schema.dump(site) for site in groups] diff --git a/backend/gn_module_monitoring/tests/test_utils/__init__.py b/backend/gn_module_monitoring/tests/test_utils/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/backend/gn_module_monitoring/tests/test_utils/test_routes.py b/backend/gn_module_monitoring/tests/test_utils/test_routes.py new file mode 100644 index 000000000..e81cb8017 --- /dev/null +++ b/backend/gn_module_monitoring/tests/test_utils/test_routes.py @@ -0,0 +1,27 @@ +import pytest +from werkzeug.datastructures import MultiDict + +from gn_module_monitoring.monitoring.models import TMonitoringSites +from gn_module_monitoring.monitoring.schemas import MonitoringSitesSchema +from gn_module_monitoring.utils.routes import get_limit_page, paginate + + +@pytest.mark.parametrize("limit, page", [("1", "2"), (1, 2), ("1", 2), (1, "2")]) +def test_get_limit_page(limit, page): + multi_dict = MultiDict([("limit", limit), ("page", page)]) + + comp_limit, comp_page = get_limit_page(params=multi_dict) + + assert isinstance(comp_limit, int) + assert isinstance(comp_page, int) + + +def test_paginate(sites): + limit = 1 + page = 2 + + res = paginate( + query=TMonitoringSites.query, schema=MonitoringSitesSchema, limit=limit, page=page + ) + + assert res.json["page"] == page diff --git a/backend/gn_module_monitoring/utils/routes.py b/backend/gn_module_monitoring/utils/routes.py new file mode 100644 index 000000000..473f8500b --- /dev/null +++ b/backend/gn_module_monitoring/utils/routes.py @@ -0,0 +1,39 @@ +from typing import Tuple + +from flask import Response +from flask.json import jsonify +from marshmallow import Schema +from sqlalchemy.orm import Query +from werkzeug.datastructures import MultiDict + +from gn_module_monitoring.monitoring.queries import Query as MonitoringQuery +from gn_module_monitoring.monitoring.schemas import paginate_schema + + +def get_limit_page(params: MultiDict) -> Tuple[int]: + return int(params.pop("limit", 50)), int(params.pop("page", 1)) + + +def get_sort(params: MultiDict, default_sort: str, default_direction) -> Tuple[str]: + return params.pop("sort", default_sort), params.pop("sort_dir", default_direction) + + +def paginate(query: Query, schema: Schema, limit: int, page: int) -> Response: + result = query.paginate(page=page, error_out=False, per_page=limit) + pagination_schema = paginate_schema(schema) + data = pagination_schema().dump( + dict(items=result.items, count=result.total, limit=limit, page=page) + ) + return jsonify(data) + + +def filter_params(query: MonitoringQuery, params: MultiDict) -> MonitoringQuery: + if len(params) != 0: + query = query.filter_by_params(params) + return query + + +def sort(query: MonitoringQuery, sort: str, sort_dir: str) -> MonitoringQuery: + if sort_dir in ["desc", "asc"]: + query = query.sort(label=sort, direction=sort_dir) + return query diff --git a/config/conf_gn_module.toml.example b/config/conf_gn_module.toml.example index f04d33639..e77362652 100644 --- a/config/conf_gn_module.toml.example +++ b/config/conf_gn_module.toml.example @@ -1 +1,5 @@ # Fichier listant les paramètres du module et leurs valeurs par défaut + +#Possibilité de rajouter une description au module de suivi +#DESCRIPTION_MODULE = "ceci est une description" +#TITLE_MODULE = "Module de suivi" \ No newline at end of file diff --git a/config/monitoring/generic/module.json b/config/monitoring/generic/module.json index 46af0de80..1c60466dc 100644 --- a/config/monitoring/generic/module.json +++ b/config/monitoring/generic/module.json @@ -129,6 +129,21 @@ "attribut_label": "Afficher dans le menu ?", "definition": "Afficher le module dans le menu de GéoNature. (Recharger la page pour voir les modifications)." }, + + "types_site": { + "type_widget": "datalist", + "attribut_label": "Types de sites", + "type_util": "types_site", + "keyValue": "id_nomenclature", + "keyLabel": "label", + "multiple": true, + "api" : "__MONITORINGS_PATH/sites/types", + "application": "GeoNature", + "required": true, + "data_path": "items", + "definition": "Permet de paramétrer la compatibilité de ce module avec les types de sites" + }, + "medias": { "type_widget": "medias", "attribut_label": "Médias", diff --git a/frontend/app/components/modules/modules.component.css b/frontend/app/components/modules/modules.component.css index 658574e78..19ee16247 100644 --- a/frontend/app/components/modules/modules.component.css +++ b/frontend/app/components/modules/modules.component.css @@ -38,14 +38,14 @@ h2 { } a { - text-decoration:none; - color:initial; + text-decoration: none; + color: initial; } .flex-item:hover { - opacity: 1; - box-shadow: 0px 0px 10px black; - transition: opacity 0.2s, box-shadow 0.2s; + opacity: 1; + box-shadow: 0px 0px 10px black; + transition: opacity 0.2s, box-shadow 0.2s; } .module h2 { @@ -53,16 +53,14 @@ a { } -.module-card:hover -{ - border: 1px solid #303030; - padding: 5px; - color:gray; - transition: 1s ease; +.module-card:hover { + border: 1px solid #303030; + padding: 5px; + color: gray; + transition: 1s ease; } -.module-card -{ - background-color:#71717129; +.module-card { + background-color: #71717129; transition: 1s ease; -} +} \ No newline at end of file diff --git a/frontend/app/components/modules/modules.component.html b/frontend/app/components/modules/modules.component.html index 821736b9c..611c18166 100644 --- a/frontend/app/components/modules/modules.component.html +++ b/frontend/app/components/modules/modules.component.html @@ -1,38 +1,65 @@ -
- -

Chargement en cours

-
- -
-
- -

Modules de suivi

+ +

Chargement en cours

+
+ +
+
-
-
- \ No newline at end of file diff --git a/frontend/app/components/modules/modules.component.ts b/frontend/app/components/modules/modules.component.ts index 979d2402c..9a45f8f00 100644 --- a/frontend/app/components/modules/modules.component.ts +++ b/frontend/app/components/modules/modules.component.ts @@ -6,6 +6,7 @@ import { mergeMap } from "rxjs/operators"; import { DataMonitoringObjectService } from "../../services/data-monitoring-object.service"; import { ConfigService } from "../../services/config.service"; import { get } from "https"; +import { AuthService, User } from "@geonature/components/auth/auth.service"; @Component({ selector: "pnx-monitoring-modules", @@ -13,6 +14,12 @@ import { get } from "https"; styleUrls: ["./modules.component.css"], }) export class ModulesComponent implements OnInit { + + + currentUser: User; + + description: string; + titleModule: string; modules: Array = []; backendUrl: string; @@ -24,9 +31,10 @@ export class ModulesComponent implements OnInit { bLoading = false; constructor( + private _auth: AuthService, private _dataMonitoringObjectService: DataMonitoringObjectService, private _configService: ConfigService - ) {} + ) { } ngOnInit() { this.bLoading = true; @@ -50,6 +58,19 @@ export class ModulesComponent implements OnInit { this._configService.backendUrl() + "/static/external_assets/monitorings/"; this.bLoading = false; + this.description = this._configService.descriptionModule(); + this.titleModule = this._configService.titleModule(); }); + + this.currentUser = this._auth.getCurrentUser(); + + this.currentUser["cruved"] = {}; + this.currentUser["cruved_objects"] = {}; + } + + onAccessSitesClick(modules) { + console.log("accès aux sites avec droits ") + console.log(modules) } + } diff --git a/frontend/app/services/config.service.ts b/frontend/app/services/config.service.ts index cb163ad8c..d37243b66 100644 --- a/frontend/app/services/config.service.ts +++ b/frontend/app/services/config.service.ts @@ -66,6 +66,12 @@ export class ConfigService { return `${api_url}${this._moduleService.currentModule.module_path}`; } + descriptionModule() { + return ModuleConfig.DESCRIPTION_MODULE; + } + titleModule() { + return ModuleConfig.TITLE_MODULE; + } /** Frontend Module Monitoring Url */ frontendModuleMonitoringUrl() { return this._moduleService.currentModule.module_path;