diff --git a/backend/geonature/core/gn_synthese/synthese_config.py b/backend/geonature/core/gn_synthese/synthese_config.py index d838c937c4..84caecfcc6 100644 --- a/backend/geonature/core/gn_synthese/synthese_config.py +++ b/backend/geonature/core/gn_synthese/synthese_config.py @@ -15,6 +15,7 @@ "regne", "group1_inpn", "group2_inpn", + "group3_inpn", "classe", "ordre", "famille", 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 7ecf1f5267..7f970845a2 100644 --- a/backend/geonature/core/gn_synthese/utils/query_select_sqla.py +++ b/backend/geonature/core/gn_synthese/utils/query_select_sqla.py @@ -255,12 +255,6 @@ def filter_taxonomy(self): if len(cd_ref_childs) > 0: self.add_join(Taxref, Taxref.cd_nom, self.model.cd_nom) self.query = self.query.where(Taxref.cd_ref.in_(cd_ref_childs)) - if "taxonomy_group2_inpn" in self.filters: - self.add_join(Taxref, Taxref.cd_nom, self.model.cd_nom) - self.query = self.query.where( - Taxref.group2_inpn.in_(self.filters.pop("taxonomy_group2_inpn")) - ) - if "taxonomy_id_hab" in self.filters: self.add_join(Taxref, Taxref.cd_nom, self.model.cd_nom) self.query = self.query.where( @@ -272,6 +266,13 @@ def filter_taxonomy(self): red_list_filters = {} for colname, value in self.filters.items(): + if colname.startswith("taxonomy_group"): + # colname = group type (group2 or group3 inpn) + # value = list of group values + colname = colname.split("taxonomy_")[-1] + self.add_join(Taxref, Taxref.cd_nom, self.model.cd_nom) + self.query = self.query.where(getattr(Taxref, colname).in_(value)) + if colname.startswith("taxhub_attribut"): self.add_join(Taxref, Taxref.cd_nom, self.model.cd_nom) taxhub_id_attr = colname[16:] diff --git a/backend/geonature/migrations/versions/9f4db1786c22_add_group3_inpn_to_synthese_views.py b/backend/geonature/migrations/versions/9f4db1786c22_add_group3_inpn_to_synthese_views.py new file mode 100644 index 0000000000..af63ece1a5 --- /dev/null +++ b/backend/geonature/migrations/versions/9f4db1786c22_add_group3_inpn_to_synthese_views.py @@ -0,0 +1,331 @@ +"""add group3 inpn to synthese views + +Revision ID: 9f4db1786c22 +Revises: 446e902a14e7 +Create Date: 2023-07-21 14:15:23.311469 + +""" + +from alembic import op +from sqlalchemy.sql import text + +# revision identifiers, used by Alembic. +revision = "9f4db1786c22" +down_revision = "5a2c9c65129f" +branch_labels = None +depends_on = ("c4415009f164",) # Taxref v15 db structure + + +def upgrade(): + op.execute( + """ + DROP VIEW gn_synthese.v_synthese_for_export; + """ + ) + + op.execute( + """ +CREATE VIEW gn_synthese.v_synthese_for_export AS + SELECT + s.id_synthese AS id_synthese, + s.date_min::date AS date_debut, + s.date_max::date AS date_fin, + s.date_min::time AS heure_debut, + s.date_max::time AS heure_fin, + t.cd_nom AS cd_nom, + t.cd_ref AS cd_ref, + t.nom_valide AS nom_valide, + t.nom_vern as nom_vernaculaire, + s.nom_cite AS nom_cite, + t.regne AS regne, + t.group1_inpn AS group1_inpn, + t.group2_inpn AS group2_inpn, + t.group3_inpn AS group3_inpn, + t.classe AS classe, + t.ordre AS ordre, + t.famille AS famille, + t.id_rang AS rang_taxo, + s.count_min AS nombre_min, + s.count_max AS nombre_max, + s.altitude_min AS alti_min, + s.altitude_max AS alti_max, + s.depth_min AS prof_min, + s.depth_max AS prof_max, + s.observers AS observateurs, + s.id_digitiser AS id_digitiser, -- Utile pour le CRUVED + s.determiner AS determinateur, + communes AS communes, + public.ST_astext(s.the_geom_4326) AS geometrie_wkt_4326, + public.ST_x(s.the_geom_point) AS x_centroid_4326, + public.ST_y(s.the_geom_point) AS y_centroid_4326, + public.ST_asgeojson(s.the_geom_4326) AS geojson_4326,-- Utile pour la génération de l'export en SHP + public.ST_asgeojson(s.the_geom_local) AS geojson_local,-- Utile pour la génération de l'export en SHP + s.place_name AS nom_lieu, + s.comment_context AS comment_releve, + s.comment_description AS comment_occurrence, + s.validator AS validateur, + n21.label_default AS niveau_validation, + s.meta_validation_date as date_validation, + s.validation_comment AS comment_validation, + s.digital_proof AS preuve_numerique_url, + s.non_digital_proof AS preuve_non_numerique, + d.dataset_name AS jdd_nom, + d.unique_dataset_id AS jdd_uuid, + d.id_dataset AS jdd_id, -- Utile pour le CRUVED + af.acquisition_framework_name AS ca_nom, + af.unique_acquisition_framework_id AS ca_uuid, + d.id_acquisition_framework AS ca_id, + s.cd_hab AS cd_habref, + hab.lb_code AS cd_habitat, + hab.lb_hab_fr AS nom_habitat, + s.precision as precision_geographique, + n1.label_default AS nature_objet_geo, + n2.label_default AS type_regroupement, + s.grp_method AS methode_regroupement, + n3.label_default AS technique_observation, + n5.label_default AS biologique_statut, + n6.label_default AS etat_biologique, + n22.label_default AS biogeographique_statut, + n7.label_default AS naturalite, + n8.label_default AS preuve_existante, + n9.label_default AS niveau_precision_diffusion, + n10.label_default AS stade_vie, + n11.label_default AS sexe, + n12.label_default AS objet_denombrement, + n13.label_default AS type_denombrement, + n14.label_default AS niveau_sensibilite, + n15.label_default AS statut_observation, + n16.label_default AS floutage_dee, + n17.label_default AS statut_source, + n18.label_default AS type_info_geo, + n19.label_default AS methode_determination, + n20.label_default AS comportement, + s.reference_biblio AS reference_biblio, + s.entity_source_pk_value AS id_origine, + s.unique_id_sinp AS uuid_perm_sinp, + s.unique_id_sinp_grp AS uuid_perm_grp_sinp, + s.meta_create_date AS date_creation, + s.meta_update_date AS date_modification, + s.additional_data as champs_additionnels, + COALESCE(s.meta_update_date, s.meta_create_date) AS derniere_action + FROM gn_synthese.synthese s + JOIN taxonomie.taxref t ON t.cd_nom = s.cd_nom + JOIN gn_meta.t_datasets d ON d.id_dataset = s.id_dataset + JOIN gn_meta.t_acquisition_frameworks af ON d.id_acquisition_framework = af.id_acquisition_framework + LEFT OUTER JOIN ( + SELECT id_synthese, string_agg(DISTINCT area_name, ', ') AS communes + FROM gn_synthese.cor_area_synthese cas + LEFT OUTER JOIN ref_geo.l_areas a_1 ON cas.id_area = a_1.id_area + JOIN ref_geo.bib_areas_types ta ON ta.id_type = a_1.id_type AND ta.type_code ='COM' + GROUP BY id_synthese + ) sa ON sa.id_synthese = s.id_synthese + LEFT JOIN ref_nomenclatures.t_nomenclatures n1 ON s.id_nomenclature_geo_object_nature = n1.id_nomenclature + LEFT JOIN ref_nomenclatures.t_nomenclatures n2 ON s.id_nomenclature_grp_typ = n2.id_nomenclature + LEFT JOIN ref_nomenclatures.t_nomenclatures n3 ON s.id_nomenclature_obs_technique = n3.id_nomenclature + LEFT JOIN ref_nomenclatures.t_nomenclatures n5 ON s.id_nomenclature_bio_status = n5.id_nomenclature + LEFT JOIN ref_nomenclatures.t_nomenclatures n6 ON s.id_nomenclature_bio_condition = n6.id_nomenclature + LEFT JOIN ref_nomenclatures.t_nomenclatures n7 ON s.id_nomenclature_naturalness = n7.id_nomenclature + LEFT JOIN ref_nomenclatures.t_nomenclatures n8 ON s.id_nomenclature_exist_proof = n8.id_nomenclature + LEFT JOIN ref_nomenclatures.t_nomenclatures n9 ON s.id_nomenclature_diffusion_level = n9.id_nomenclature + LEFT JOIN ref_nomenclatures.t_nomenclatures n10 ON s.id_nomenclature_life_stage = n10.id_nomenclature + LEFT JOIN ref_nomenclatures.t_nomenclatures n11 ON s.id_nomenclature_sex = n11.id_nomenclature + LEFT JOIN ref_nomenclatures.t_nomenclatures n12 ON s.id_nomenclature_obj_count = n12.id_nomenclature + LEFT JOIN ref_nomenclatures.t_nomenclatures n13 ON s.id_nomenclature_type_count = n13.id_nomenclature + LEFT JOIN ref_nomenclatures.t_nomenclatures n14 ON s.id_nomenclature_sensitivity = n14.id_nomenclature + LEFT JOIN ref_nomenclatures.t_nomenclatures n15 ON s.id_nomenclature_observation_status = n15.id_nomenclature + LEFT JOIN ref_nomenclatures.t_nomenclatures n16 ON s.id_nomenclature_blurring = n16.id_nomenclature + LEFT JOIN ref_nomenclatures.t_nomenclatures n17 ON s.id_nomenclature_source_status = n17.id_nomenclature + LEFT JOIN ref_nomenclatures.t_nomenclatures n18 ON s.id_nomenclature_info_geo_type = n18.id_nomenclature + LEFT JOIN ref_nomenclatures.t_nomenclatures n19 ON s.id_nomenclature_determination_method = n19.id_nomenclature + LEFT JOIN ref_nomenclatures.t_nomenclatures n20 ON s.id_nomenclature_behaviour = n20.id_nomenclature + LEFT JOIN ref_nomenclatures.t_nomenclatures n21 ON s.id_nomenclature_valid_status = n21.id_nomenclature + LEFT JOIN ref_nomenclatures.t_nomenclatures n22 ON s.id_nomenclature_biogeo_status = n22.id_nomenclature + LEFT JOIN ref_habitats.habref hab ON hab.cd_hab = s.cd_hab; +""" + ) + + op.execute( + """ + DROP VIEW gn_synthese.v_synthese_taxon_for_export_view; + """ + ) + op.execute( + """ +-- Vue export des taxons de la synthèse +-- Première version qui reste à affiner/étoffer +CREATE VIEW gn_synthese.v_synthese_taxon_for_export_view AS + SELECT DISTINCT + ref.nom_valide, + ref.cd_ref, + ref.nom_vern, + ref.group1_inpn, + ref.group2_inpn, + ref.group3_inpn, + ref.regne, + ref.phylum, + ref.classe, + ref.ordre, + ref.famille, + ref.id_rang +FROM gn_synthese.synthese s +JOIN taxonomie.taxref t ON s.cd_nom = t.cd_nom +JOIN taxonomie.taxref ref ON t.cd_ref = ref.cd_nom; +""" + ) + + +def downgrade(): + op.execute( + """ + DROP VIEW gn_synthese.v_synthese_for_export; + """ + ) + + op.execute( + """ +CREATE VIEW gn_synthese.v_synthese_for_export AS + SELECT + s.id_synthese AS id_synthese, + s.date_min::date AS date_debut, + s.date_max::date AS date_fin, + s.date_min::time AS heure_debut, + s.date_max::time AS heure_fin, + t.cd_nom AS cd_nom, + t.cd_ref AS cd_ref, + t.nom_valide AS nom_valide, + t.nom_vern as nom_vernaculaire, + s.nom_cite AS nom_cite, + t.regne AS regne, + t.group1_inpn AS group1_inpn, + t.group2_inpn AS group2_inpn, + t.classe AS classe, + t.ordre AS ordre, + t.famille AS famille, + t.id_rang AS rang_taxo, + s.count_min AS nombre_min, + s.count_max AS nombre_max, + s.altitude_min AS alti_min, + s.altitude_max AS alti_max, + s.depth_min AS prof_min, + s.depth_max AS prof_max, + s.observers AS observateurs, + s.id_digitiser AS id_digitiser, -- Utile pour le CRUVED + s.determiner AS determinateur, + communes AS communes, + public.ST_astext(s.the_geom_4326) AS geometrie_wkt_4326, + public.ST_x(s.the_geom_point) AS x_centroid_4326, + public.ST_y(s.the_geom_point) AS y_centroid_4326, + public.ST_asgeojson(s.the_geom_4326) AS geojson_4326,-- Utile pour la génération de l'export en SHP + public.ST_asgeojson(s.the_geom_local) AS geojson_local,-- Utile pour la génération de l'export en SHP + s.place_name AS nom_lieu, + s.comment_context AS comment_releve, + s.comment_description AS comment_occurrence, + s.validator AS validateur, + n21.label_default AS niveau_validation, + s.meta_validation_date as date_validation, + s.validation_comment AS comment_validation, + s.digital_proof AS preuve_numerique_url, + s.non_digital_proof AS preuve_non_numerique, + d.dataset_name AS jdd_nom, + d.unique_dataset_id AS jdd_uuid, + d.id_dataset AS jdd_id, -- Utile pour le CRUVED + af.acquisition_framework_name AS ca_nom, + af.unique_acquisition_framework_id AS ca_uuid, + d.id_acquisition_framework AS ca_id, + s.cd_hab AS cd_habref, + hab.lb_code AS cd_habitat, + hab.lb_hab_fr AS nom_habitat, + s.precision as precision_geographique, + n1.label_default AS nature_objet_geo, + n2.label_default AS type_regroupement, + s.grp_method AS methode_regroupement, + n3.label_default AS technique_observation, + n5.label_default AS biologique_statut, + n6.label_default AS etat_biologique, + n22.label_default AS biogeographique_statut, + n7.label_default AS naturalite, + n8.label_default AS preuve_existante, + n9.label_default AS niveau_precision_diffusion, + n10.label_default AS stade_vie, + n11.label_default AS sexe, + n12.label_default AS objet_denombrement, + n13.label_default AS type_denombrement, + n14.label_default AS niveau_sensibilite, + n15.label_default AS statut_observation, + n16.label_default AS floutage_dee, + n17.label_default AS statut_source, + n18.label_default AS type_info_geo, + n19.label_default AS methode_determination, + n20.label_default AS comportement, + s.reference_biblio AS reference_biblio, + s.entity_source_pk_value AS id_origine, + s.unique_id_sinp AS uuid_perm_sinp, + s.unique_id_sinp_grp AS uuid_perm_grp_sinp, + s.meta_create_date AS date_creation, + s.meta_update_date AS date_modification, + s.additional_data as champs_additionnels, + COALESCE(s.meta_update_date, s.meta_create_date) AS derniere_action + FROM gn_synthese.synthese s + JOIN taxonomie.taxref t ON t.cd_nom = s.cd_nom + JOIN gn_meta.t_datasets d ON d.id_dataset = s.id_dataset + JOIN gn_meta.t_acquisition_frameworks af ON d.id_acquisition_framework = af.id_acquisition_framework + LEFT OUTER JOIN ( + SELECT id_synthese, string_agg(DISTINCT area_name, ', ') AS communes + FROM gn_synthese.cor_area_synthese cas + LEFT OUTER JOIN ref_geo.l_areas a_1 ON cas.id_area = a_1.id_area + JOIN ref_geo.bib_areas_types ta ON ta.id_type = a_1.id_type AND ta.type_code ='COM' + GROUP BY id_synthese + ) sa ON sa.id_synthese = s.id_synthese + LEFT JOIN ref_nomenclatures.t_nomenclatures n1 ON s.id_nomenclature_geo_object_nature = n1.id_nomenclature + LEFT JOIN ref_nomenclatures.t_nomenclatures n2 ON s.id_nomenclature_grp_typ = n2.id_nomenclature + LEFT JOIN ref_nomenclatures.t_nomenclatures n3 ON s.id_nomenclature_obs_technique = n3.id_nomenclature + LEFT JOIN ref_nomenclatures.t_nomenclatures n5 ON s.id_nomenclature_bio_status = n5.id_nomenclature + LEFT JOIN ref_nomenclatures.t_nomenclatures n6 ON s.id_nomenclature_bio_condition = n6.id_nomenclature + LEFT JOIN ref_nomenclatures.t_nomenclatures n7 ON s.id_nomenclature_naturalness = n7.id_nomenclature + LEFT JOIN ref_nomenclatures.t_nomenclatures n8 ON s.id_nomenclature_exist_proof = n8.id_nomenclature + LEFT JOIN ref_nomenclatures.t_nomenclatures n9 ON s.id_nomenclature_diffusion_level = n9.id_nomenclature + LEFT JOIN ref_nomenclatures.t_nomenclatures n10 ON s.id_nomenclature_life_stage = n10.id_nomenclature + LEFT JOIN ref_nomenclatures.t_nomenclatures n11 ON s.id_nomenclature_sex = n11.id_nomenclature + LEFT JOIN ref_nomenclatures.t_nomenclatures n12 ON s.id_nomenclature_obj_count = n12.id_nomenclature + LEFT JOIN ref_nomenclatures.t_nomenclatures n13 ON s.id_nomenclature_type_count = n13.id_nomenclature + LEFT JOIN ref_nomenclatures.t_nomenclatures n14 ON s.id_nomenclature_sensitivity = n14.id_nomenclature + LEFT JOIN ref_nomenclatures.t_nomenclatures n15 ON s.id_nomenclature_observation_status = n15.id_nomenclature + LEFT JOIN ref_nomenclatures.t_nomenclatures n16 ON s.id_nomenclature_blurring = n16.id_nomenclature + LEFT JOIN ref_nomenclatures.t_nomenclatures n17 ON s.id_nomenclature_source_status = n17.id_nomenclature + LEFT JOIN ref_nomenclatures.t_nomenclatures n18 ON s.id_nomenclature_info_geo_type = n18.id_nomenclature + LEFT JOIN ref_nomenclatures.t_nomenclatures n19 ON s.id_nomenclature_determination_method = n19.id_nomenclature + LEFT JOIN ref_nomenclatures.t_nomenclatures n20 ON s.id_nomenclature_behaviour = n20.id_nomenclature + LEFT JOIN ref_nomenclatures.t_nomenclatures n21 ON s.id_nomenclature_valid_status = n21.id_nomenclature + LEFT JOIN ref_nomenclatures.t_nomenclatures n22 ON s.id_nomenclature_biogeo_status = n22.id_nomenclature + LEFT JOIN ref_habitats.habref hab ON hab.cd_hab = s.cd_hab; + +""" + ) + + op.execute( + """ + DROP VIEW gn_synthese.v_synthese_taxon_for_export_view; + """ + ) + op.execute( + """ +-- Vue export des taxons de la synthèse +-- Première version qui reste à affiner/étoffer +CREATE VIEW gn_synthese.v_synthese_taxon_for_export_view AS + SELECT DISTINCT + ref.nom_valide, + ref.cd_ref, + ref.nom_vern, + ref.group1_inpn, + ref.group2_inpn, + ref.regne, + ref.phylum, + ref.classe, + ref.ordre, + ref.famille, + ref.id_rang +FROM gn_synthese.synthese s +JOIN taxonomie.taxref t ON s.cd_nom = t.cd_nom +JOIN taxonomie.taxref ref ON t.cd_ref = ref.cd_nom; +""" + ) diff --git a/backend/geonature/tests/test_synthese.py b/backend/geonature/tests/test_synthese.py index a0e0607e98..492bf09909 100644 --- a/backend/geonature/tests/test_synthese.py +++ b/backend/geonature/tests/test_synthese.py @@ -512,6 +512,31 @@ def test_get_observations_for_web_param_column_frontend( set(feature["properties"].keys()) == expected_columns for feature in data["features"] ) + @pytest.mark.parametrize( + "group_inpn", + [ + ("group2_inpn"), + ("group3_inpn"), + ], + ) + def test_get_observations_for_web_filter_group_inpn(self, users, synthese_data, group_inpn): + obs = synthese_data["obs1"] + taxref_from_cd_nom = Taxref.query.filter_by(cd_nom=obs.cd_nom).one() + group_from_taxref = getattr(taxref_from_cd_nom, group_inpn) + filter_name = "taxonomie_" + group_inpn + + set_logged_user(self.client, users["self_user"]) + response = self.client.get( + url_for("gn_synthese.get_observations_for_web"), + json={ + filter_name: [group_from_taxref], + }, + ) + response_json = response.json + assert obs.id_synthese in { + synthese["properties"]["id"] for synthese in response_json["features"] + } + def test_export(self, users): set_logged_user(self.client, users["self_user"]) @@ -560,6 +585,7 @@ def test_export_observations(self, users, synthese_data, synthese_sensitive_data '"regne"', '"group1_inpn"', '"group2_inpn"', + '"group3_inpn"', '"classe"', '"ordre"', '"famille"', @@ -736,6 +762,7 @@ def test_export_taxons(self, users, synthese_data, synthese_sensitive_data): '"nom_vern"', '"group1_inpn"', '"group2_inpn"', + '"group3_inpn"', '"regne"', '"phylum"', '"classe"', @@ -761,7 +788,6 @@ def assert_export_taxons_results(user, set_expected_cd_ref): rows_data_response = response.data.decode("utf-8").split("\r\n")[0:-1] row_header = rows_data_response[0] rows_taxons_data_response = rows_data_response[1:] - assert row_header.split(";") == expected_columns_exports nb_expected_cd_noms = len(set_expected_cd_ref) diff --git a/contrib/occtax/frontend/app/occtax-form/counting/counting.component.html b/contrib/occtax/frontend/app/occtax-form/counting/counting.component.html index c2dc0c27d3..e0d7883920 100644 --- a/contrib/occtax/frontend/app/occtax-form/counting/counting.component.html +++ b/contrib/occtax/frontend/app/occtax-form/counting/counting.component.html @@ -8,7 +8,8 @@ codeNomenclatureType="OBJ_DENBR" [parentFormControl]="form.get('id_nomenclature_obj_count')" [regne]="(occtaxFormOccurrenceService.taxref|async)?.regne" - [group2Inpn]="(occtaxFormOccurrenceService.taxref|async)?.group2_inpn"> + [group2Inpn]="(occtaxFormOccurrenceService.taxref|async)?.group2_inpn" + [group3Inpn]="(occtaxFormOccurrenceService.taxref|async)?.group3_inpn">