diff --git a/backend/dependencies/TaxHub b/backend/dependencies/TaxHub index 41b1aa2624..908b1f663e 160000 --- a/backend/dependencies/TaxHub +++ b/backend/dependencies/TaxHub @@ -1 +1 @@ -Subproject commit 41b1aa26241ea501e81599676dc2db5dddb3586d +Subproject commit 908b1f663e126d95f21b87f5b5abac4d19f6c4d1 diff --git a/backend/geonature/core/gn_synthese/utils/query_select_sqla.py b/backend/geonature/core/gn_synthese/utils/query_select_sqla.py index a24918610e..fa6085a633 100644 --- a/backend/geonature/core/gn_synthese/utils/query_select_sqla.py +++ b/backend/geonature/core/gn_synthese/utils/query_select_sqla.py @@ -17,7 +17,9 @@ from utils_flask_sqla_geo.utilsgeometry import circle_from_point from geonature.utils.env import DB -from geonature.core.taxonomie.models import Taxref, CorTaxonAttribut, TaxrefLR +from geonature.core.taxonomie.models import ( + TaxrefLR +) from geonature.core.gn_synthese.models import ( Synthese, CorObserverSynthese, @@ -30,7 +32,17 @@ TDatasets, ) from geonature.utils.errors import GeonatureApiError - +from geonature.core.ref_geo.models import ( + CorAreaStatus, +) +from apptax.taxonomie.models import ( + Taxref, + CorTaxonAttribut, + TaxrefBdcStatutTaxon, + TaxrefBdcStatutCorTextValues, + TaxrefBdcStatutText, + TaxrefBdcStatutValues, +) class SyntheseQuery: @@ -325,6 +337,80 @@ def filter_other_filters(self): if colname.startswith("area"): self.add_join(CorAreaSynthese, CorAreaSynthese.id_synthese, self.model.id_synthese) self.query = self.query.where(CorAreaSynthese.id_area.in_(value)) + elif colname.endswith("_red_lists"): + red_list_id = colname.replace("_red_lists", "") + all_red_lists_cfg = current_app.config["SYNTHESE"]["RED_LISTS_FILTERS"] + red_list_cfg = next((item for item in all_red_lists_cfg if item["id"] == red_list_id), None) + red_list_cte = ( + select([TaxrefBdcStatutTaxon.cd_ref, CorAreaStatus.id_area]) + .select_from( + TaxrefBdcStatutTaxon.__table__ + .join( + TaxrefBdcStatutCorTextValues, + TaxrefBdcStatutCorTextValues.id_value_text == TaxrefBdcStatutTaxon.id_value_text + ) + .join( + TaxrefBdcStatutText, + TaxrefBdcStatutText.id_text == TaxrefBdcStatutCorTextValues.id_text + ) + .join( + TaxrefBdcStatutValues, + TaxrefBdcStatutValues.id_value == TaxrefBdcStatutCorTextValues.id_value + ) + .join( + CorAreaStatus, + CorAreaStatus.cd_sig == TaxrefBdcStatutText.cd_sig + ) + ) + .where(TaxrefBdcStatutValues.code_statut.in_(value)) + .where(TaxrefBdcStatutText.cd_type_statut == red_list_cfg['status_type']) + .where(TaxrefBdcStatutText.enable == True) + .cte(name=f"{red_list_id}_red_list") + ) + cas_red_list = aliased(CorAreaSynthese) + self.add_join(cas_red_list, cas_red_list.id_synthese, self.model.id_synthese) + self.add_join(Taxref, Taxref.cd_nom, self.model.cd_nom) + self.add_join_multiple_cond(red_list_cte, [ + red_list_cte.c.cd_ref == Taxref.cd_ref, + red_list_cte.c.id_area == cas_red_list.id_area, + ]) + + elif colname.endswith("_status"): + status_id = colname.replace("_status", "") + all_status_cfg = current_app.config["SYNTHESE"]["STATUS_FILTERS"] + status_cfg = next((item for item in all_status_cfg if item["id"] == status_id), None) + # Check if a checkbox was used. + if (isinstance(value, list) and value[0] == True and len(status_cfg['status_types']) == 1): + value = status_cfg['status_types'] + status_cte = ( + select([TaxrefBdcStatutTaxon.cd_ref, CorAreaStatus.id_area]) + .select_from( + TaxrefBdcStatutTaxon.__table__ + .join( + TaxrefBdcStatutCorTextValues, + TaxrefBdcStatutCorTextValues.id_value_text == TaxrefBdcStatutTaxon.id_value_text + ) + .join( + TaxrefBdcStatutText, + TaxrefBdcStatutText.id_text == TaxrefBdcStatutCorTextValues.id_text + ) + .join( + CorAreaStatus, + CorAreaStatus.cd_sig == TaxrefBdcStatutText.cd_sig + ) + ) + .where(TaxrefBdcStatutText.cd_type_statut.in_(value)) + .where(TaxrefBdcStatutText.enable == True) + .distinct() + .cte(name=f"{status_id}_status") + ) + cas_status = aliased(CorAreaSynthese) + self.add_join(cas_status, cas_status.id_synthese, self.model.id_synthese) + self.add_join(Taxref, Taxref.cd_nom, self.model.cd_nom) + self.add_join_multiple_cond(status_cte, [ + status_cte.c.cd_ref == Taxref.cd_ref, + status_cte.c.id_area == cas_status.id_area, + ]) elif colname.startswith("id_"): col = getattr(self.model.__table__.columns, colname) self.query = self.query.where(col.in_(value)) diff --git a/backend/geonature/core/ref_geo/models.py b/backend/geonature/core/ref_geo/models.py index 05139ba813..e0d987ed69 100644 --- a/backend/geonature/core/ref_geo/models.py +++ b/backend/geonature/core/ref_geo/models.py @@ -66,3 +66,10 @@ class LiMunicipalities(DB.Model): insee_commune_nouvelle = DB.Column(DB.Unicode) meta_create_date = DB.Column(DB.DateTime) meta_update_date = DB.Column(DB.DateTime) + +@serializable +class CorAreaStatus(DB.Model): + __tablename__ = "cor_area_status" + __table_args__ = {"schema": "ref_geo"} + cd_sig = DB.Column(DB.Unicode, primary_key=True) + id_area = DB.Column(DB.Integer, ForeignKey("ref_geo.l_areas.id_area"), primary_key=True) diff --git a/backend/geonature/migrations/versions/f350e23bee07_link_bdc_status_to_ref_geo.py b/backend/geonature/migrations/versions/f350e23bee07_link_bdc_status_to_ref_geo.py new file mode 100644 index 0000000000..cb96c41b85 --- /dev/null +++ b/backend/geonature/migrations/versions/f350e23bee07_link_bdc_status_to_ref_geo.py @@ -0,0 +1,324 @@ +"""Add table to link bdc_status and ref_geo + +Revision ID: f350e23bee07 +Revises: c0fdf2ee7f4f +Create Date: 2021-09-24 17:39:42.062506 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'f350e23bee07' +down_revision = 'c0fdf2ee7f4f' +branch_labels = None +depends_on = None + + +def upgrade(): + op.execute(""" + CREATE TABLE ref_geo.cor_area_status ( + cd_sig varchar(25) NOT NULL, + id_area int4 NOT NULL, + CONSTRAINT pk_cor_area_status PRIMARY KEY (cd_sig, id_area), + CONSTRAINT fk_cor_area_status_id_area FOREIGN KEY (id_area) REFERENCES ref_geo.l_areas(id_area) ON UPDATE CASCADE + ) + """) + op.execute(""" + INSERT INTO ref_geo.cor_area_status ( + WITH old_regions AS ( + SELECT + '11' AS code, -- Île-de-France + id_area + FROM ref_geo.l_areas + WHERE id_type = ref_geo.get_id_area_type('DEP') + AND area_code IN ('75','77','78','91','92','93','94','95') + UNION + SELECT + '21' AS code, -- Champagne-Ardenne + id_area + FROM ref_geo.l_areas + WHERE id_type = ref_geo.get_id_area_type('DEP') + AND area_code IN ('08','10','51','52') + UNION + SELECT + '22' AS code, -- Picardie + id_area + FROM ref_geo.l_areas + WHERE id_type = ref_geo.get_id_area_type('DEP') + AND area_code IN ('02','60','80') + UNION + SELECT + '23' AS code, -- Haute-Normandie + id_area + FROM ref_geo.l_areas + WHERE id_type = ref_geo.get_id_area_type('DEP') + AND area_code IN ('27', '76') + UNION + SELECT + '24' AS code, -- Centre + id_area + FROM ref_geo.l_areas + WHERE id_type = ref_geo.get_id_area_type('DEP') + AND area_code IN ('18','28','36','37','41','45') + UNION + SELECT + '25' AS code, -- Basse-Normandie + id_area + FROM ref_geo.l_areas + WHERE id_type = ref_geo.get_id_area_type('DEP') + AND area_code IN ('14','50','61') + UNION + SELECT + '26' AS code, -- Bourgogne + id_area + FROM ref_geo.l_areas + WHERE id_type = ref_geo.get_id_area_type('DEP') + AND area_code IN ('21','58','71','89') + UNION + SELECT + '31' AS code, -- Nord-Pas-de-Calais + id_area + FROM ref_geo.l_areas + WHERE id_type = ref_geo.get_id_area_type('DEP') + AND area_code IN ('59', '62') + UNION + SELECT + '41' AS code, -- Lorraine + id_area + FROM ref_geo.l_areas + WHERE id_type = ref_geo.get_id_area_type('DEP') + AND area_code IN ('54','55','57','88') + UNION + SELECT + '42' AS code, -- Alsace + id_area + FROM ref_geo.l_areas + WHERE id_type = ref_geo.get_id_area_type('DEP') + AND area_code IN ('67', '68') + UNION + SELECT + '43' AS code, -- Franche-Comté + id_area + FROM ref_geo.l_areas + WHERE id_type = ref_geo.get_id_area_type('DEP') + AND area_code IN ('25','39','70','90') + UNION + SELECT + '52' AS code, -- Pays de la Loire + id_area + FROM ref_geo.l_areas + WHERE id_type = ref_geo.get_id_area_type('DEP') + AND area_code IN ('44','49','53','72','85') + UNION + SELECT + '53' AS code, -- Bretagne + id_area + FROM ref_geo.l_areas + WHERE id_type = ref_geo.get_id_area_type('DEP') + AND area_code IN ('22','29','35','56') + UNION + SELECT + '54' AS code, -- Poitou-Charentes + id_area + FROM ref_geo.l_areas + WHERE id_type = ref_geo.get_id_area_type('DEP') + AND area_code IN ('16','17','79','86') + UNION + SELECT + '72' AS code, -- Aquitaine + id_area + FROM ref_geo.l_areas + WHERE id_type = ref_geo.get_id_area_type('DEP') + AND area_code IN ('24','33','40','47','64') + UNION + SELECT + '73' AS code, -- Midi-Pyrénées + id_area + FROM ref_geo.l_areas + WHERE id_type = ref_geo.get_id_area_type('DEP') + AND area_code IN ('9','12','31','32','46','65','81','82') + UNION + SELECT + '74' AS code, -- Limousin + id_area + FROM ref_geo.l_areas + WHERE id_type = ref_geo.get_id_area_type('DEP') + AND area_code IN ('19','23','87') + UNION + SELECT + '82' AS code, -- Rhône-Alpes + id_area + FROM ref_geo.l_areas + WHERE id_type = ref_geo.get_id_area_type('DEP') + AND area_code IN ('01','07','26','38','42','69','73','74') + UNION + SELECT + '83' AS code, -- Auvergne + id_area + FROM ref_geo.l_areas + WHERE id_type = ref_geo.get_id_area_type('DEP') + AND area_code IN ('03', '15', '43', '63') + UNION + SELECT + '91' AS code, -- Languedoc-Roussillon + id_area + FROM ref_geo.l_areas + WHERE id_type = ref_geo.get_id_area_type('DEP') + AND area_code IN ('11','30','34','48','66') + UNION + SELECT + '93' AS code, -- Provence-Alpes-Côte d’Azur + id_area + FROM ref_geo.l_areas + WHERE id_type = ref_geo.get_id_area_type('DEP') + AND area_code IN ('04', '05', '06', '13', '83', '84') + UNION + SELECT + '94' AS code, -- Corse + id_area + FROM ref_geo.l_areas + WHERE id_type = ref_geo.get_id_area_type('DEP') + AND area_code IN ('2A', '2B') + ), + new_regions AS ( + SELECT + '11' AS code, -- Île-de-France + id_area + FROM ref_geo.l_areas + WHERE id_type = ref_geo.get_id_area_type('DEP') + AND area_code IN ('75','77','78','91','92','93','94','95') + UNION + SELECT + '24' AS code, -- Centre-Val de Loire + id_area + FROM ref_geo.l_areas + WHERE id_type = ref_geo.get_id_area_type('DEP') + AND area_code IN ('18','28','36','37','41','45') + UNION + SELECT + '27' AS code, -- Bourgogne-Franche-Comté + id_area + FROM ref_geo.l_areas + WHERE id_type = ref_geo.get_id_area_type('DEP') + AND area_code IN ('21','25','39','58','70','71','89','90') + UNION + SELECT + '28' AS code, -- Normandie + id_area + FROM ref_geo.l_areas + WHERE id_type = ref_geo.get_id_area_type('DEP') + AND area_code IN ('14','27','50','61','76') + UNION + SELECT + '32' AS code, -- Hauts-de-France + id_area + FROM ref_geo.l_areas + WHERE id_type = ref_geo.get_id_area_type('DEP') + AND area_code IN ('02', '59', '60', '62', '80') + UNION + SELECT + '44' AS code, -- Grand Est + id_area + FROM ref_geo.l_areas + WHERE id_type = ref_geo.get_id_area_type('DEP') + AND area_code IN ('08','10','51','52','54','55','57','67','68','88') + UNION + SELECT + '52' AS code, -- Pays de la Loire + id_area + FROM ref_geo.l_areas + WHERE id_type = ref_geo.get_id_area_type('DEP') + AND area_code IN ('44','49','53','72','85') + UNION + SELECT + '53' AS code, -- Bretagne + id_area + FROM ref_geo.l_areas + WHERE id_type = ref_geo.get_id_area_type('DEP') + AND area_code IN ('22','29','35','56') + UNION + SELECT + '75' AS code, -- Nouvelle-Aquitaine + id_area + FROM ref_geo.l_areas + WHERE id_type = ref_geo.get_id_area_type('DEP') + AND area_code IN ('16','17','19','23','24','33','40','47','64','79','86','87') + UNION + SELECT + '76' AS code, -- Occitanie + id_area + FROM ref_geo.l_areas + WHERE id_type = ref_geo.get_id_area_type('DEP') + AND area_code IN ('09', '11', '12', '30', '31', '32', '34', '46', '48', '65', '66', '81', '82') + UNION + SELECT + '84' AS code, -- Auvergne-Rhône-Alpes + id_area + FROM ref_geo.l_areas + WHERE id_type = ref_geo.get_id_area_type('DEP') + AND area_code IN ('01', '03', '07', '15', '26', '38', '42', '43', '63', '69', '73', '74') + UNION + SELECT + '93' AS code, -- Provence-Alpes-Côte d’Azur + id_area + FROM ref_geo.l_areas + WHERE id_type = ref_geo.get_id_area_type('DEP') + AND area_code IN ('04', '05', '06', '13', '83', '84') + UNION + SELECT + '94' AS code, -- Corse + id_area + FROM ref_geo.l_areas + WHERE id_type = ref_geo.get_id_area_type('DEP') + AND area_code IN ('2A', '2B') + ), + sig AS ( + SELECT + 'ETATFRA' AS cd_sig, + la.id_area + FROM ref_geo.l_areas AS la + WHERE la.id_type = ref_geo.get_id_area_type('DEP') + UNION + SELECT DISTINCT + cd_sig, + ( + SELECT id_area + FROM ref_geo.l_areas + WHERE area_code = REPLACE(cd_sig, 'INSEED', '') + AND id_type = ref_geo.get_id_area_type('DEP') + ) + FROM taxonomie.bdc_statut_text AS bst + WHERE cd_sig ILIKE 'INSEED%' + UNION + SELECT DISTINCT + cd_sig, + nrs.id_area + FROM taxonomie.bdc_statut_text AS bst + JOIN new_regions AS nrs ON (REPLACE(cd_sig, 'INSEENR', '') = nrs.code) + WHERE cd_sig ILIKE 'INSEENR%' + UNION + SELECT DISTINCT + cd_sig, + ors.id_area + FROM taxonomie.bdc_statut_text AS bst + JOIN old_regions AS ors ON (REPLACE(cd_sig, 'INSEER', '') = ors.code) + WHERE cd_sig ILIKE 'INSEER%' + UNION + SELECT + 'TERFXFR' AS cd_sig, + la.id_area + FROM ref_geo.l_areas AS la + WHERE la.id_type = ref_geo.get_id_area_type('DEP') + ) + SELECT s.* + FROM sig AS s + WHERE s.id_area IS NOT NULL + ORDER BY s.cd_sig, s.id_area ASC + )""") + op.execute("CREATE INDEX idx_cabs_cd_sig ON ref_geo.cor_area_status(cd_sig);") + + +def downgrade(): + op.execute("DROP TABLE ref_geo.cor_area_status") diff --git a/backend/geonature/utils/config_schema.py b/backend/geonature/utils/config_schema.py index 04be28394a..18fee2f9a2 100644 --- a/backend/geonature/utils/config_schema.py +++ b/backend/geonature/utils/config_schema.py @@ -238,9 +238,72 @@ class GnFrontEndConf(Schema): class Synthese(Schema): + #-------------------------------------------------------------------- + # SYNTHESE - SEARCH FORM AREA_FILTERS = fields.List( fields.Dict, load_default=[{"label": "Communes", "id_type": DEFAULT_ID_MUNICIPALITY}] ) + # Nombre de résultat à afficher pour la rechercher autocompleté de taxon + TAXON_RESULT_NUMBER = fields.Integer(load_default=20) + # Afficher ou non l'arbre taxonomique + DISPLAY_TAXON_TREE = fields.Boolean(load_default=True) + # Ajouter le filtre sur l'observers_txt en ILIKE sur les portée 1 et 2 du CRUVED + CRUVED_SEARCH_WITH_OBSERVER_AS_TXT = fields.Boolean(load_default=False) + # Switch the observer form input in free text input (true) or in select input (false) + SEARCH_OBSERVER_WITH_LIST = fields.Boolean(load_default=False) + # Id of the observer list -- utilisateurs.t_menus + ID_SEARCH_OBSERVER_LIST = fields.Integer(load_default=1) + # Regulatory or not status list of fields + STATUS_FILTERS = fields.List(fields.Dict, missing=[ + { + "id": "protections", + "show": True, + "display_name": "Taxons protégés", + "status_types": ["PN", "PR", "PD"], + }, + { + "id": "regulations", + "show": True, + "display_name": "Taxons réglementés", + "status_types": ["REGLII", "REGLLUTTE", "REGL", "REGLSO"], + }, + { + "id": "znief", + "show": True, + "display_name": "Espèces déterminantes ZNIEFF", + "status_types": ["ZDET"], + }, + ]) + # Red lists list of fields + RED_LISTS_FILTERS = fields.List(fields.Dict, missing=[ + { + "id": "worldwide", + "show": True, + "display_name": "Liste rouge mondiale", + "status_type": "LRM", + }, + { + "id": "european", + "show": True, + "display_name": "Liste rouge européenne", + "status_type": "LRE", + }, + { + "id": "national", + "show": True, + "display_name": "Liste rouge nationale", + "status_type": "LRN", + }, + { + "id": "regional", + "show": True, + "display_name": "Liste rouge régionale", + "status_type": "LRR", + }, + ]) + + #-------------------------------------------------------------------- + # SYNTHESE - OBSERVATIONS LIST # Listes des champs renvoyés par l'API synthese '/synthese' # Si on veut afficher des champs personnalisés dans le frontend (paramètre LIST_COLUMNS_FRONTEND) il faut # d'abbord s'assurer que ces champs sont bien renvoyé par l'API ! @@ -248,6 +311,9 @@ class Synthese(Schema): COLUMNS_API_SYNTHESE_WEB_APP = fields.List(fields.String, load_default=DEFAULT_COLUMNS_API_SYNTHESE) # Colonnes affichées sur la liste des résultats de la sytnthese LIST_COLUMNS_FRONTEND = fields.List(fields.Dict, load_default=DEFAULT_LIST_COLUMN) + + #-------------------------------------------------------------------- + # SYNTHESE - DOWNLOADS (AKA EXPORTS) EXPORT_COLUMNS = fields.List(fields.String(), load_default=DEFAULT_EXPORT_COLUMNS) # Certaines colonnes sont obligatoires pour effectuer les filtres CRUVED EXPORT_ID_SYNTHESE_COL = fields.String(load_default="id_synthese") @@ -260,35 +326,32 @@ class Synthese(Schema): EXPORT_METADATA_ACTOR_COL = fields.String(load_default="acteurs") # Formats d'export disponibles ["csv", "geojson", "shapefile", "gpkg"] EXPORT_FORMAT = fields.List(fields.String(), load_default=["csv", "geojson", "shapefile"]) - # Nombre de résultat à afficher pour la rechercher autocompleté de taxon - TAXON_RESULT_NUMBER = fields.Integer(load_default=20) + # Nombre max d'observation dans les exports + NB_MAX_OBS_EXPORT = fields.Integer(load_default=50000) + + #-------------------------------------------------------------------- + # SYNTHESE - OBSERVATION DETAILS # Liste des id attributs Taxhub à afficher sur la fiche détaile de la synthese # et sur les filtres taxonomiques avancés ID_ATTRIBUT_TAXHUB = fields.List(fields.Integer(), load_default=[102, 103]) - # nom des colonnes de la table gn_synthese.synthese que l'on veux retirer des filres dynamiques - # et de la modale d'information détaillée d'une observation - # example = "[non_digital_proof]" + # Display email on synthese and validation info obs modal + DISPLAY_EMAIL = fields.Boolean(load_default=True) + + #-------------------------------------------------------------------- + # SYNTHESE - SHARED PARAMETERS + # Nom des colonnes de la table gn_synthese.synthese que l'on veux retirer des filtres dynamiques + # et de la modale d'information détaillée d'une observation example = "[non_digital_proof]" EXCLUDED_COLUMNS = fields.List(fields.String(), load_default=[]) - # Afficher ou non l'arbre taxonomique - DISPLAY_TAXON_TREE = fields.Boolean(load_default=True) - # rajoute le filtre sur l'observers_txt en ILIKE sur les portée 1 et 2 du CRUVED - CRUVED_SEARCH_WITH_OBSERVER_AS_TXT = fields.Boolean(load_default=False) - # Switch the observer form input in free text input (true) or in select input (false) - SEARCH_OBSERVER_WITH_LIST = fields.Boolean(load_default=False) - # id of the observer list -- utilisateurs.t_menus - ID_SEARCH_OBSERVER_LIST = fields.Integer(load_default=1) + + #-------------------------------------------------------------------- + # SYNTHESE - MAP # Nombre max d'observation à afficher sur la carte NB_MAX_OBS_MAP = fields.Integer(load_default=50000) - # clusteriser les layers sur la carte + # Clusteriser les layers sur la carte ENABLE_LEAFLET_CLUSTER = fields.Boolean(load_default=True) - # Nombre max d'observation dans les exports - NB_MAX_OBS_EXPORT = fields.Integer(load_default=50000) - # Nombre des "dernières observations" affiché à l'arrive sur la synthese + # Nombre des "dernières observations" affichées à l'arrivée sur la synthese NB_LAST_OBS = fields.Integer(load_default=100) - # Display email on synthese and validation info obs modal - DISPLAY_EMAIL = fields.Boolean(load_default=True) - # Map configuration BASEMAP = [ diff --git a/config/default_config.toml.example b/config/default_config.toml.example index b6aabf3472..51be9baf36 100644 --- a/config/default_config.toml.example +++ b/config/default_config.toml.example @@ -381,6 +381,18 @@ MAIL_ON_ERROR = false "date_modification", "champs_additionnels ] + RED_LISTS_FILTERS = [ + { "id" = "worldwide", "show" = true, "display_name" = "Liste rouge mondiale", "status_type" = "LRM" }, + { "id" = "european", "show" = true, "display_name" = "Liste rouge européenne", "status_type" = "LRE" }, + { "id" = "national", "show" = true, "display_name" = "Liste rouge nationale", "status_type" = "LRN" }, + { "id" = "regional", "show" = true, "display_name" = "Liste rouge régionale", "status_type" = "LRR" }, + ] + STATUS_FILTERS = [ + { "id" = "protections", "show" = true, "display_name" = "Taxons protégés", "status_types" = ["PN", "PR", "PD"] }, + { "id" = "regulations", "show" = true, "display_name" = "Taxons réglementés", "status_types" = ["REGLII", "REGL", "REGLSO"] }, + { "id" = "invasive", "show" = true, "display_name" = "Espèces envahissantes", "status_types" = ["REGLLUTTE"] }, + { "id" = "znief", "show" = true, "display_name" = "Espèces déterminantes ZNIEFF", "status_types" = ["ZDET"] }, + ] # Configuration de l'accès sans authentication [PUBLIC_ACCESS] diff --git a/frontend/src/app/GN2CommonModule/form/data-form.service.ts b/frontend/src/app/GN2CommonModule/form/data-form.service.ts index a5a597b1ef..7f735b81c5 100644 --- a/frontend/src/app/GN2CommonModule/form/data-form.service.ts +++ b/frontend/src/app/GN2CommonModule/form/data-form.service.ts @@ -336,7 +336,7 @@ export class DataFormService { * * @param params: dict of paramters */ - getAcquisitionFrameworks(params = {}) { + getAcquisitionFrameworks(params = {}) { let queryString: HttpParams = new HttpParams(); for (let key in params) { queryString = queryString.set(key, params[key]) @@ -597,7 +597,7 @@ export class DataFormService { } return this._http.get(`${AppConfig.API_ENDPOINT}/gn_commons/additional_fields`, {params: queryString}).map(additionalFields => { - return additionalFields.map(data => { + return additionalFields.map(data => { return { "id_field": data.id_field, "attribut_label": data.field_label, @@ -618,10 +618,21 @@ export class DataFormService { ...data.additional_attributes } }) - - }); + }); + } + getRedListValues(statusType: String) { + return this._http.get(`${AppConfig.API_TAXHUB}/bdc_statuts/red_lists/${statusType}`); } -} + getStatusType(statusTypes: String[]) { + let queryString: HttpParams = new HttpParams(); + if (statusTypes) { + queryString = queryString.set('codes', statusTypes.join(',')); + } + return this._http.get(`${AppConfig.API_TAXHUB}/bdc_statuts/status_types`, { + params: queryString + }); + } +} diff --git a/frontend/src/app/GN2CommonModule/form/synthese-form/advanced-form/synthese-advanced-form-component.ts b/frontend/src/app/GN2CommonModule/form/synthese-form/advanced-form/synthese-advanced-form-component.ts index d2fe4e417f..3aab6eda6c 100644 --- a/frontend/src/app/GN2CommonModule/form/synthese-form/advanced-form/synthese-advanced-form-component.ts +++ b/frontend/src/app/GN2CommonModule/form/synthese-form/advanced-form/synthese-advanced-form-component.ts @@ -1,11 +1,14 @@ -import { Component, OnInit, ViewChild, AfterContentInit } from '@angular/core'; +import { Component, OnInit, ViewChild, AfterContentInit, Inject } from '@angular/core'; +import { FormGroup } from '@angular/forms'; + import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { TreeNode, TreeComponent, IActionMapping } from 'angular-tree-component'; -import { SyntheseFormService } from '@geonature_common/form/synthese-form/synthese-form.service'; + +import { APP_CONFIG_TOKEN } from '@geonature_config/app.config'; import { DynamicFormService } from '@geonature_common/form/dynamic-form-generator/dynamic-form.service'; -import { FormGroup } from '@angular/forms'; +import { SyntheseFormService } from '@geonature_common/form/synthese-form/synthese-form.service'; import { TaxonAdvancedStoreService } from '@geonature_common/form/synthese-form/advanced-form/synthese-advanced-form-store.service'; -import { AppConfig } from '@geonature_config/app.config'; + @Component({ selector: 'pnx-validation-taxon-advanced', @@ -15,8 +18,7 @@ import { AppConfig } from '@geonature_config/app.config'; }) export class TaxonAdvancedModalComponent implements OnInit, AfterContentInit { @ViewChild('tree') treeComponent: TreeComponent; - public AppConfig = AppConfig; - public URL_AUTOCOMPLETE = AppConfig.API_TAXHUB + '/taxref/search/lb_nom'; + public URL_AUTOCOMPLETE; public taxonsTree; public treeOptions; public selectedNodes = []; @@ -28,10 +30,14 @@ export class TaxonAdvancedModalComponent implements OnInit, AfterContentInit { public showTree = false; constructor( + @Inject(APP_CONFIG_TOKEN) private cfg, public activeModal: NgbActiveModal, public formService: SyntheseFormService, public storeService: TaxonAdvancedStoreService ) { + // Set config parameters + this.URL_AUTOCOMPLETE = this.cfg.API_TAXHUB + '/taxref/search/lb_nom'; + const actionMapping: IActionMapping = { mouse: { click: (tree, node, $event) => {}, diff --git a/frontend/src/app/GN2CommonModule/form/synthese-form/advanced-form/synthese-advanced-form-store.service.ts b/frontend/src/app/GN2CommonModule/form/synthese-form/advanced-form/synthese-advanced-form-store.service.ts index 64ba78031d..3796535308 100644 --- a/frontend/src/app/GN2CommonModule/form/synthese-form/advanced-form/synthese-advanced-form-store.service.ts +++ b/frontend/src/app/GN2CommonModule/form/synthese-form/advanced-form/synthese-advanced-form-store.service.ts @@ -1,15 +1,17 @@ -import { Injectable } from '@angular/core'; +import { Inject, Injectable } from '@angular/core'; + +import { TreeModel } from 'angular-tree-component'; + +import { APP_CONFIG_TOKEN } from '@geonature_config/app.config'; import { DataFormService } from '@geonature_common/form/data-form.service'; -import { SyntheseDataService } from '@geonature_common/form/synthese-form/synthese-data.service'; -import { SyntheseFormService } from '@geonature_common/form/synthese-form/synthese-form.service'; import { DynamicFormService } from '@geonature_common/form/dynamic-form-generator/dynamic-form.service'; -import { TreeModel } from 'angular-tree-component'; -import { AppConfig } from '@geonature_config/app.config'; import { formatTaxonTree } from '@geonature_common/form/taxon-tree/taxon-tree.service'; +import { SyntheseDataService } from '@geonature_common/form/synthese-form/synthese-data.service'; +import { SyntheseFormService } from '@geonature_common/form/synthese-form/synthese-form.service'; @Injectable() export class TaxonAdvancedStoreService { - public AppConfig = AppConfig; + public displayTaxonTree: Boolean = false; public taxonTree: any; public treeModel: TreeModel; public taxonTreeState: any; @@ -18,28 +20,51 @@ export class TaxonAdvancedStoreService { public taxonomyLR: Array; public taxonomyHab: Array; public taxonomyGroup2Inpn: Array; + public redListsValues: any = {}; constructor( + @Inject(APP_CONFIG_TOKEN) private cfg, private _dataService: DataFormService, private _validationDataService: SyntheseDataService, private _formService: SyntheseFormService, private _formGen: DynamicFormService ) { - if (AppConfig.SYNTHESE.DISPLAY_TAXON_TREE) { + // Set taxon tree if needed + if (this.cfg.SYNTHESE.DISPLAY_TAXON_TREE) { + this.displayTaxonTree = true; this._validationDataService.getTaxonTree().subscribe(data => { this.taxonTree = formatTaxonTree(data); }); } + // Set protection status filters data + this._formService.statusFilters.forEach(status => { + this._dataService + .getStatusType(status.status_types) + .subscribe(data => { + status.values = data; // get taxhub attributes + }); + }); + + // Set red lists filters data + this._formService.redListsFilters.forEach(redList => { + this._dataService + .getRedListValues(redList.status_type) + .subscribe(data => { + redList.values = data; + }); + }); + + // Get TaxHub attributes this._dataService.getTaxhubBibAttributes().subscribe(attrs => { - // display only the taxhub attributes set in the config + // Display only the taxhub attributes set in the config this.taxhubAttributes = attrs .filter(attr => { - return AppConfig.SYNTHESE.ID_ATTRIBUT_TAXHUB.indexOf(attr.id_attribut) !== -1; + return this.cfg.SYNTHESE.ID_ATTRIBUT_TAXHUB.indexOf(attr.id_attribut) !== -1; }) .map(attr => { - // format attributes to fit with the GeoNature dynamicFormComponent + // Format attributes to fit with the GeoNature dynamicFormComponent attr['values'] = JSON.parse(attr['liste_valeur_attribut']).values; attr['attribut_name'] = 'taxhub_attribut_' + attr['id_attribut']; attr['required'] = attr['obligatoire']; @@ -55,7 +80,7 @@ export class TaxonAdvancedStoreService { }); this.formBuilded = true; }); - // load LR, habitat and group2inpn + // Load LR, habitat and group2inpn this._dataService.getTaxonomyLR().subscribe(data => { this.taxonomyLR = data; }); diff --git a/frontend/src/app/GN2CommonModule/form/synthese-form/advanced-form/synthese-advanced-form.component.html b/frontend/src/app/GN2CommonModule/form/synthese-form/advanced-form/synthese-advanced-form.component.html index fd1936fe95..2a4298286e 100644 --- a/frontend/src/app/GN2CommonModule/form/synthese-form/advanced-form/synthese-advanced-form.component.html +++ b/frontend/src/app/GN2CommonModule/form/synthese-form/advanced-form/synthese-advanced-form.component.html @@ -47,7 +47,7 @@
Taxon(s) recherché(s):
-
- +
Statuts
+
- + + + +
+
+ + +
+
-
- +
Listes rouges
+
- + + +
-
- - +
+
Attributs TaxRef
+
+ + +
+ +
+ + +
+ +
+ + +
-
Attributs taxhub
+
Attributs TaxHub
Ou ?
-
+
{{areas}}
- - diff --git a/frontend/src/app/GN2CommonModule/form/synthese-form/synthese-form.component.ts b/frontend/src/app/GN2CommonModule/form/synthese-form/synthese-form.component.ts index 12921b58e8..324c98aab8 100644 --- a/frontend/src/app/GN2CommonModule/form/synthese-form/synthese-form.component.ts +++ b/frontend/src/app/GN2CommonModule/form/synthese-form/synthese-form.component.ts @@ -18,7 +18,6 @@ import { ActivatedRoute } from "@angular/router"; export class SyntheseSearchComponent implements OnInit { public AppConfig = AppConfig; public organisms: any; - public areaFilters: Array; public taxonApiEndPoint = `${AppConfig.API_ENDPOINT}/synthese/taxons_autocomplete`; public validationStatus: Array; private params: any; @@ -45,7 +44,7 @@ export class SyntheseSearchComponent implements OnInit { }); // format areas filter - this.areaFilters = AppConfig.SYNTHESE.AREA_FILTERS.map(area => { + this.formService.areasFilters.map(area => { if (typeof area.id_type === 'number') { area['id_type_array'] = [area.id_type]; } else { diff --git a/frontend/src/app/GN2CommonModule/form/synthese-form/synthese-form.service.ts b/frontend/src/app/GN2CommonModule/form/synthese-form/synthese-form.service.ts index c77ca0f7d5..c687dbcb98 100644 --- a/frontend/src/app/GN2CommonModule/form/synthese-form/synthese-form.service.ts +++ b/frontend/src/app/GN2CommonModule/form/synthese-form/synthese-form.service.ts @@ -1,10 +1,12 @@ -import { Injectable } from '@angular/core'; +import { Inject, Injectable } from '@angular/core'; import { FormGroup, FormBuilder, FormControl, ValidatorFn } from '@angular/forms'; -import { AppConfig } from '@geonature_config/app.config'; + import { stringify } from 'wellknown'; import { NgbDateParserFormatter } from '@ng-bootstrap/ng-bootstrap'; -import { NgbDatePeriodParserFormatter } from '@geonature_common/form/date/ngb-date-custom-parser-formatter'; + +import { APP_CONFIG_TOKEN } from '@geonature_config/app.config'; import { DYNAMIC_FORM_DEF } from '@geonature_common/form/synthese-form/dynamicFormConfig'; +import { NgbDatePeriodParserFormatter } from '@geonature_common/form/date/ngb-date-custom-parser-formatter'; @Injectable() export class SyntheseFormService { @@ -14,8 +16,12 @@ export class SyntheseFormService { public selectedCdRefFromTree = []; public selectedTaxonFromRankInput = []; public dynamycFormDef: Array; + public areasFilters; + public redListsFilters; + public statusFilters; constructor( + @Inject(APP_CONFIG_TOKEN) private cfg, private _fb: FormBuilder, private _dateParser: NgbDateParserFormatter, private _periodFormatter: NgbDatePeriodParserFormatter @@ -43,17 +49,36 @@ export class SyntheseFormService { }); this.searchForm.setValidators([this.periodValidator()]); - AppConfig.SYNTHESE.AREA_FILTERS.forEach(area => { + + // Add protection status filters defined in configuration parameters + this.statusFilters = Object.assign([], this.cfg.SYNTHESE.STATUS_FILTERS); + this.statusFilters.forEach(status => { + const control_name = `${status.id}_status`; + this.searchForm.addControl(control_name, new FormControl(new Array())); + status['control_name'] = control_name; + status['control'] = this.searchForm.controls[control_name]; + }); + + // Add red lists filters defined in configuration parameters + this.redListsFilters = Object.assign([], this.cfg.SYNTHESE.RED_LISTS_FILTERS); + this.redListsFilters.forEach(redList => { + const control_name = `${redList.id}_red_lists`; + this.searchForm.addControl(control_name, new FormControl(new Array())); + redList['control'] = this.searchForm.controls[control_name]; + }); + + // Add areas filters defined in configuration parameters + this.areasFilters = Object.assign([], this.cfg.SYNTHESE.AREA_FILTERS); + this.areasFilters.forEach(area => { const control_name = 'area_' + area.id_type; this.searchForm.addControl(control_name, new FormControl(new Array())); - const control = this.searchForm.controls[control_name]; - area['control'] = control; + area['control'] = this.searchForm.controls[control_name]; }); - // init the dynamic form with the user parameters + // Init the dynamic form with the user parameters // remove the filters which are in AppConfig.SYNTHESE.EXCLUDED_COLUMNS this.dynamycFormDef = DYNAMIC_FORM_DEF.filter(formDef => { - return AppConfig.SYNTHESE.EXCLUDED_COLUMNS.indexOf(formDef.attribut_name) === -1; + return this.cfg.SYNTHESE.EXCLUDED_COLUMNS.indexOf(formDef.attribut_name) === -1; }); this.formBuilded = true; }