From 6f6b91a452551353eb5652a06885cb4747a9c840 Mon Sep 17 00:00:00 2001 From: jacquesfize Date: Fri, 10 Jan 2025 15:22:08 +0100 Subject: [PATCH 01/13] feat(changelog): update changelog --- docs/CHANGELOG.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 0f6785e27a..ff941afdb6 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,27 +1,27 @@ # CHANGELOG -## 2.15.1 (unreleased) +## 2.15.1 (2025-01-10) **🚀 Nouveautés** -- Amélioration de la recherche libre des métadonnées en cherchant chaque mot indépendamment (#3295, par @jbrieuclp) -- Amélioration de l'affichage de la photo du taxon sur les fiches taxon (#3287, par @edelclaux) -- Conversion du changelog en format markdown (#3297, par @jacquesfize) -- Ajout d'un fichier `Makefile` pour faciliter l'usage des commandes de développement (#3300, par @jacquesfize & @edelclaux) -- Complément et mise en forme de la documentation et publication sur Readthedocs (#3306, par @jacquesfize) -- Ajout des nouvelles mailles INPN lors de l'installation de GeoNature (#3293, par @jacquesfize) +- [Métadonnées]Amélioration de la recherche libre des métadonnées en cherchant chaque mot indépendamment (#3295, par @jbrieuclp) +- [FicheTaxon] Amélioration de l'affichage de la photo du taxon sur les fiches taxon (#3287, par @edelclaux) +- [Documentation] Conversion du changelog en format markdown (#3297, par @jacquesfize) +- [Documentation] Complément et mise en forme de la documentation et publication sur Readthedocs (#3306, par @jacquesfize) +- [Dévelopement] Ajout d'un fichier `Makefile` pour faciliter l'usage des commandes de développement (#3300, par @jacquesfize & @edelclaux) +- [Installation] Ajout des nouvelles mailles INPN lors de l'installation de GeoNature (#3293, par @jacquesfize) **🐛 Corrections** -- Correction de la pagination quand on filtre les discussions de la page d'accueil sur "Mes discussions" (#3288, par @edelclaux) -- Correction du nombre de taxons sur les fiches des cadres d'acquisition (#3228, par @jacquesfize) -- Correction de la redirection de l'authentification (#3305, par @jacquesfize) -- Correction des performances de la requête de récupération des discussions (#3307, par @jacquesfize) -- [Import] Correction de l'export des rapports d'import Occhab (#3293, par @jacquesfize) +- [Discussions] Correction de la pagination quand on filtre les discussions de la page d'accueil sur "Mes discussions" (#3288, par @edelclaux) +- [Discussions] Correction des performances de la requête de récupération des discussions (#3307, par @jacquesfize) +- [Métadonnées] Correction du nombre de taxons sur les fiches des cadres d'acquisition (#3228, par @jacquesfize) +- [Authentification] Correction des redirections lors de l'authentification (#3305, par @jacquesfize) - [Import] Correction de la selection automatique du JDD lors de l'import depuis la fiche d'un JDD (#3293, par @jacquesfize) - [Import] Correction de la mise à jour des mappings publics (#3293, par @jacquesfize) - [Import] Correction de la sauvegarde des checkbox dans le mapping des champs (#3293, par @Pierre-Narcisi) - [Import] Correction de la sélection des champs `auto_generate` (#3293, par @Pierre-Narcisi) +- [Import] Correction du template des notifications d'un import terminé (#3310 par @jacquesfize) ## 2.15.0 - Pavo cristatus 🦚 (2025-12-11) From 9a017861bca799271bf5b3e959ae2191a6cc0985 Mon Sep 17 00:00:00 2001 From: Jacques Fize <4259846+jacquesfize@users.noreply.github.com> Date: Mon, 13 Jan 2025 14:40:53 +0100 Subject: [PATCH 02/13] feat(home): optimize `general_stats` route (#3313) * feat(home): optimize `general_stats` route * feat(synthese): add index on gn_synthese.observers to increase perf of `general_stats` computation Co-authored-by: dba-sig-sfepm --- backend/geonature/core/gn_synthese/routes.py | 34 +++++++++++------- ...669c_create_index_on_synthese_observers.py | 35 +++++++++++++++++++ .../tests/benchmarks/test_benchmark_home.py | 24 +++++++++++++ 3 files changed, 80 insertions(+), 13 deletions(-) create mode 100644 backend/geonature/migrations/versions/5cf0ce9e669c_create_index_on_synthese_observers.py create mode 100644 backend/geonature/tests/benchmarks/test_benchmark_home.py diff --git a/backend/geonature/core/gn_synthese/routes.py b/backend/geonature/core/gn_synthese/routes.py index e2dc80e61e..f5cb9b0cab 100644 --- a/backend/geonature/core/gn_synthese/routes.py +++ b/backend/geonature/core/gn_synthese/routes.py @@ -939,21 +939,29 @@ def general_stats(permissions): .select_from(TDatasets) .where(TDatasets.filter_by_readable().whereclause) ) - query = select( - func.count(Synthese.id_synthese), - func.count(func.distinct(Synthese.cd_nom)), - func.count(func.distinct(Synthese.observers)), - ) - synthese_query_obj = SyntheseQuery(Synthese, query, {}) - synthese_query_obj.filter_query_with_cruved(g.current_user, permissions) - result = DB.session.execute(synthese_query_obj.query) - synthese_counts = result.fetchone() + results = {"nb_allowed_datasets": nb_allowed_datasets} + + queries = { + "nb_obs": select( + func.count(Synthese.id_synthese), + ), + "nb_distinct_species": select( + func.count(func.distinct(Synthese.cd_nom)), + ), + "nb_distinct_observer": select( + func.count(func.distinct(Synthese.observers)), + ), + } + for key, query in queries.items(): + synthese_query = SyntheseQuery(Synthese, query, {}) + synthese_query.filter_query_with_cruved(g.current_user, permissions) + results[key] = db.session.scalar(synthese_query.query) data = { - "nb_data": synthese_counts[0], - "nb_species": synthese_counts[1], - "nb_observers": synthese_counts[2], - "nb_dataset": nb_allowed_datasets, + "nb_data": results["nb_obs"], + "nb_species": results["nb_distinct_species"], + "nb_observers": results["nb_distinct_observer"], + "nb_dataset": results["nb_allowed_datasets"], } return data diff --git a/backend/geonature/migrations/versions/5cf0ce9e669c_create_index_on_synthese_observers.py b/backend/geonature/migrations/versions/5cf0ce9e669c_create_index_on_synthese_observers.py new file mode 100644 index 0000000000..2c82e2f298 --- /dev/null +++ b/backend/geonature/migrations/versions/5cf0ce9e669c_create_index_on_synthese_observers.py @@ -0,0 +1,35 @@ +"""create_index_on_synthese_observers + +Revision ID: 5cf0ce9e669c +Revises: 9df933cc3c7a +Create Date: 2025-01-13 14:14:30.085725 + +""" + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = "5cf0ce9e669c" +down_revision = "9df933cc3c7a" +branch_labels = None +depends_on = None + + +def upgrade(): + op.create_index( + "synthese_observers_idx", + "synthese", + ["observers"], + if_not_exists=True, + schema="gn_synthese", + ) + + +def downgrade(): + op.drop_index( + "synthese_observers_idx", + table_name="synthese", + schema="gn_synthese", + ) diff --git a/backend/geonature/tests/benchmarks/test_benchmark_home.py b/backend/geonature/tests/benchmarks/test_benchmark_home.py new file mode 100644 index 0000000000..0b4bd842aa --- /dev/null +++ b/backend/geonature/tests/benchmarks/test_benchmark_home.py @@ -0,0 +1,24 @@ +import logging +import pytest +from geonature.tests.benchmarks import * +from geonature.tests.test_pr_occhab import stations + +from .benchmark_generator import BenchmarkTest, CLater +from .utils import activate_profiling_sql + +logging.basicConfig() +logger = logging.getLogger("logger-name") +logger.setLevel(logging.DEBUG) + +from .utils import CLIENT_GET, CLIENT_POST + + +@pytest.mark.benchmark(group="home") +@pytest.mark.usefixtures("client_class", "temporary_transaction", "activate_profiling_sql") +class TestBenchmarkHome: + + test_general_stats = BenchmarkTest( + CLIENT_GET, + [CLater("""url_for("gn_synthese.general_stats")""")], + dict(user_profile="admin_user", fixtures=[]), + )() From 6c38ec559d304c5083d05eb423ccbd7b9b2f0090 Mon Sep 17 00:00:00 2001 From: Jacques Fize <4259846+jacquesfize@users.noreply.github.com> Date: Mon, 13 Jan 2025 15:49:41 +0100 Subject: [PATCH 03/13] Fix sensitivity remove message (#3314) * fix(sensitivity): fix delete sensitivity rule message * feat(test): add test for deletion of sensitivity rules --- backend/geonature/core/sensitivity/utils.py | 7 +++- backend/geonature/tests/test_sensitivity.py | 39 +++++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/backend/geonature/core/sensitivity/utils.py b/backend/geonature/core/sensitivity/utils.py index 5ec278ff83..42106d0151 100644 --- a/backend/geonature/core/sensitivity/utils.py +++ b/backend/geonature/core/sensitivity/utils.py @@ -131,4 +131,9 @@ def insert_sensitivity_referential(source, csvfile): def remove_sensitivity_referential(source): - return db.session.execute(sa.delete(SensitivityRule).where(SensitivityRule.source == source)) + whereclause = SensitivityRule.source == source + count_source = db.session.scalar( + sa.select(sa.func.count()).where(whereclause), + ) + db.session.execute(sa.delete(SensitivityRule).where(whereclause)) + return count_source diff --git a/backend/geonature/tests/test_sensitivity.py b/backend/geonature/tests/test_sensitivity.py index 9166d9fdde..0eaba67951 100644 --- a/backend/geonature/tests/test_sensitivity.py +++ b/backend/geonature/tests/test_sensitivity.py @@ -19,6 +19,35 @@ from ref_geo.models import LAreas, BibAreasTypes from apptax.taxonomie.models import Taxref from pypnnomenclature.models import TNomenclatures, BibNomenclaturesTypes +from geonature.core.sensitivity.utils import remove_sensitivity_referential +from click.testing import CliRunner + + +@pytest.fixture +def sensitivity_rule_source_name(): + return "sensitivity_rules_test" + + +@pytest.fixture(scope="function") +def sensitivity_rules(sensitivity_rule_source_name): + with db.session.begin_nested(): + cd_nom = db.session.scalar(sa.select(Taxref.cd_nom).limit(1)) + sensitivity_nomenc_type = db.session.execute( + sa.select(BibNomenclaturesTypes).filter_by(mnemonique="SENSIBILITE") + ).scalar_one() + diffusion_maille = db.session.execute( + sa.select(TNomenclatures).filter_by( + id_type=sensitivity_nomenc_type.id_type, mnemonique="2" + ) + ).scalar_one() + for _ in range(100): + rule = SensitivityRule( + cd_nom=cd_nom, + sensitivity_duration=5, + source=sensitivity_rule_source_name, + id_nomenclature_sensitivity=diffusion_maille.id_nomenclature, + ) + db.session.add(rule) @pytest.fixture(scope="function") @@ -28,8 +57,14 @@ def clean_all_sensitivity_rules(): db.session.execute(sa.delete(SensitivityRule)) +@pytest.fixture +def client_click(): + return CliRunner() + + @pytest.mark.usefixtures("client_class", "temporary_transaction", "clean_all_sensitivity_rules") class TestSensitivity: + def test_get_id_nomenclature_sensitivity(self, app): taxon = db.session.scalars(sa.select(Taxref)).first() geom = WKTElement("POINT(6.15 44.85)", srid=4326) @@ -363,3 +398,7 @@ def test_synthese_sensitivity(self, app, source): s.date_max = date_obs db.session.refresh(s) assert s.id_nomenclature_sensitivity == nomenc_not_sensitive.id_nomenclature + + def test_remove_sensitivy_rule(self, sensitivity_rules, sensitivity_rule_source_name): + res = remove_sensitivity_referential(sensitivity_rule_source_name) + assert res == 100 From c5443bce220c4391be5d00196279b1f209c7b02f Mon Sep 17 00:00:00 2001 From: jacquesfize Date: Mon, 13 Jan 2025 16:25:59 +0100 Subject: [PATCH 04/13] fix(synthese): fix id_import form value disappearance --- frontend/src/app/syntheseModule/synthese.component.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/frontend/src/app/syntheseModule/synthese.component.ts b/frontend/src/app/syntheseModule/synthese.component.ts index 2faaafd4e1..599d34e01a 100644 --- a/frontend/src/app/syntheseModule/synthese.component.ts +++ b/frontend/src/app/syntheseModule/synthese.component.ts @@ -85,7 +85,6 @@ export class SyntheseComponent implements OnInit { this._changeDetector.detectChanges(); this.loadAndStoreData(this._fs.formatParams()); - this._fs.processedDefaultFilters['id_import'] = null; }); }); } From 19010fa3a9ab55f0a90cb16658b1554389b4fcf0 Mon Sep 17 00:00:00 2001 From: Jacques Fize <4259846+jacquesfize@users.noreply.github.com> Date: Mon, 13 Jan 2025 17:54:15 +0100 Subject: [PATCH 05/13] feat(commands): watch for .toml files changes when dev_back running (#3316) * feat(commands): dev-back now watch for geonature_config.toml changes * extend to other toml files --- backend/geonature/core/command/main.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/backend/geonature/core/command/main.py b/backend/geonature/core/command/main.py index a5db10d0e4..dd2df1bc94 100644 --- a/backend/geonature/core/command/main.py +++ b/backend/geonature/core/command/main.py @@ -10,7 +10,7 @@ import click from flask.cli import run_command -from geonature.utils.env import GEONATURE_VERSION +from geonature.utils.env import GEONATURE_VERSION, ROOT_DIR from geonature.utils.module import iter_modules_dist from geonature import create_app from geonature.utils.config import config @@ -19,6 +19,8 @@ create_frontend_module_config, build_frontend, ) +from os.path import join +import glob from flask.cli import FlaskGroup @@ -57,7 +59,13 @@ def dev_back(ctx, host, port): """ if not environ.get("FLASK_DEBUG"): environ["FLASK_DEBUG"] = "true" - ctx.invoke(run_command, host=host, port=port) + + ctx.invoke( + run_command, + host=host, + port=port, + extra_files=[file for file in glob.glob(join(ROOT_DIR, "config", "*.toml"))], + ) @main.command() From ef7c9c34a0275500ebe5d0fd393e0ce2169e5d97 Mon Sep 17 00:00:00 2001 From: Etienne Delclaux <150020787+edelclaux@users.noreply.github.com> Date: Wed, 15 Jan 2025 10:33:24 +0100 Subject: [PATCH 06/13] [CONFIG] Taxon sheet can be disabled + ENABLE_PROFILE value now impact ENABLE_TAB_PROFILE (#3312) * feat: add 'ENABLE_TAXON_SHEETS' option in 'SYNTHESE' * feat: ENABLE_XXX to ENABLE_TAB_XXX for taxon sheet tab * feat: add a relation between 'FRONTEND.ENABLE_PROFILES' and 'SYNTHESE.TAXON_SHEET.ENABLE_TAB_PROFILE' Co-authored-by: Jacques Fize <4259846+jacquesfize@users.noreply.github.com> * feat: add test for config processing --------- Co-authored-by: jacquesfize Co-authored-by: Jacques Fize <4259846+jacquesfize@users.noreply.github.com> --- backend/geonature/core/gn_synthese/routes.py | 138 +++++++++--------- backend/geonature/tests/test_utils.py | 68 ++++++++- backend/geonature/utils/config_schema.py | 15 +- config/default_config.toml.example | 18 ++- .../synthese-info-obs.component.html | 2 +- .../synthese-list.component.html | 5 +- .../src/app/syntheseModule/synthese.module.ts | 1 + .../taxon-sheet/taxon-sheet.route.service.ts | 17 ++- 8 files changed, 182 insertions(+), 82 deletions(-) diff --git a/backend/geonature/core/gn_synthese/routes.py b/backend/geonature/core/gn_synthese/routes.py index f5cb9b0cab..de822af52f 100644 --- a/backend/geonature/core/gn_synthese/routes.py +++ b/backend/geonature/core/gn_synthese/routes.py @@ -82,6 +82,7 @@ VMTaxrefListForautocomplete, ) +from geonature import app routes = Blueprint("gn_synthese", __name__) @@ -966,80 +967,87 @@ def general_stats(permissions): return data -@routes.route("/taxon_stats/", methods=["GET"]) -@permissions.check_cruved_scope("R", get_scope=True, module_code="SYNTHESE") -@json_resp -def taxon_stats(scope, cd_nom): - """Return stats for a specific taxon""" +## ############################################################################ +## TAXON SHEET ROUTES +## ############################################################################ - area_type = request.args.get("area_type") +if app.config["SYNTHESE"]["ENABLE_TAXON_SHEETS"]: - if not area_type: - raise BadRequest("Missing area_type parameter") + @routes.route("/taxon_stats/", methods=["GET"]) + @permissions.check_cruved_scope("R", get_scope=True, module_code="SYNTHESE") + @json_resp + def taxon_stats(scope, cd_nom): + """Return stats for a specific taxon""" - # Ensure area_type is valid - valid_area_types = ( - db.session.query(BibAreasTypes.type_code) - .distinct() - .filter(BibAreasTypes.type_code == area_type) - .scalar() - ) - if not valid_area_types: - raise BadRequest("Invalid area_type") - - # Subquery to fetch areas based on area_type - areas_subquery = ( - select([LAreas.id_area]) - .where(LAreas.id_type == BibAreasTypes.id_type) - .where(BibAreasTypes.type_code == area_type) - .alias("areas") - ) - cd_ref = db.session.scalar(select(Taxref.cd_ref).where(Taxref.cd_nom == cd_nom)) - taxref_cd_nom_list = db.session.scalars(select(Taxref.cd_nom).where(Taxref.cd_ref == cd_ref)) + area_type = request.args.get("area_type") - # Main query to fetch stats - query = ( - select( - [ - func.count(distinct(Synthese.id_synthese)).label("observation_count"), - func.count(distinct(Synthese.observers)).label("observer_count"), - func.count(distinct(areas_subquery.c.id_area)).label("area_count"), - func.min(Synthese.altitude_min).label("altitude_min"), - func.max(Synthese.altitude_max).label("altitude_max"), - func.min(Synthese.date_min).label("date_min"), - func.max(Synthese.date_max).label("date_max"), - ] + if not area_type: + raise BadRequest("Missing area_type parameter") + + # Ensure area_type is valid + valid_area_types = ( + db.session.query(BibAreasTypes.type_code) + .distinct() + .filter(BibAreasTypes.type_code == area_type) + .scalar() ) - .select_from( - sa.join( - Synthese, - CorAreaSynthese, - Synthese.id_synthese == CorAreaSynthese.id_synthese, - ) - .join(areas_subquery, CorAreaSynthese.id_area == areas_subquery.c.id_area) - .join(LAreas, CorAreaSynthese.id_area == LAreas.id_area) - .join(BibAreasTypes, LAreas.id_type == BibAreasTypes.id_type) + if not valid_area_types: + raise BadRequest("Invalid area_type") + + # Subquery to fetch areas based on area_type + areas_subquery = ( + select(LAreas.id_area) + .where(LAreas.id_type == BibAreasTypes.id_type, BibAreasTypes.type_code == area_type) + .alias("areas") + ) + cd_ref = db.session.scalar(select(Taxref.cd_ref).where(Taxref.cd_nom == cd_nom)) + taxref_cd_nom_list = db.session.scalars( + select(Taxref.cd_nom).where(Taxref.cd_ref == cd_ref) ) - .where(Synthese.cd_nom.in_(taxref_cd_nom_list)) - ) - synthese_query_obj = SyntheseQuery(Synthese, query, {}) - synthese_query_obj.filter_query_with_cruved(g.current_user, scope) - result = DB.session.execute(synthese_query_obj.query) - synthese_stats = result.fetchone() + # Main query to fetch stats + query = ( + select( + [ + func.count(distinct(Synthese.id_synthese)).label("observation_count"), + func.count(distinct(Synthese.observers)).label("observer_count"), + func.count(distinct(areas_subquery.c.id_area)).label("area_count"), + func.min(Synthese.altitude_min).label("altitude_min"), + func.max(Synthese.altitude_max).label("altitude_max"), + func.min(Synthese.date_min).label("date_min"), + func.max(Synthese.date_max).label("date_max"), + ] + ) + .select_from( + sa.join( + Synthese, + CorAreaSynthese, + Synthese.id_synthese == CorAreaSynthese.id_synthese, + ) + .join(areas_subquery, CorAreaSynthese.id_area == areas_subquery.c.id_area) + .join(LAreas, CorAreaSynthese.id_area == LAreas.id_area) + .join(BibAreasTypes, LAreas.id_type == BibAreasTypes.id_type) + ) + .where(Synthese.cd_nom.in_(taxref_cd_nom_list)) + ) - data = { - "cd_ref": cd_nom, - "observation_count": synthese_stats["observation_count"], - "observer_count": synthese_stats["observer_count"], - "area_count": synthese_stats["area_count"], - "altitude_min": synthese_stats["altitude_min"], - "altitude_max": synthese_stats["altitude_max"], - "date_min": synthese_stats["date_min"], - "date_max": synthese_stats["date_max"], - } + synthese_query_obj = SyntheseQuery(Synthese, query, {}) + synthese_query_obj.filter_query_with_cruved(g.current_user, scope) + result = DB.session.execute(synthese_query_obj.query) + synthese_stats = result.fetchone() + + data = { + "cd_ref": cd_nom, + "observation_count": synthese_stats["observation_count"], + "observer_count": synthese_stats["observer_count"], + "area_count": synthese_stats["area_count"], + "altitude_min": synthese_stats["altitude_min"], + "altitude_max": synthese_stats["altitude_max"], + "date_min": synthese_stats["date_min"], + "date_max": synthese_stats["date_max"], + } - return data + return data @routes.route("/taxons_tree", methods=["GET"]) diff --git a/backend/geonature/tests/test_utils.py b/backend/geonature/tests/test_utils.py index f3055e97da..0e4559d292 100644 --- a/backend/geonature/tests/test_utils.py +++ b/backend/geonature/tests/test_utils.py @@ -8,10 +8,14 @@ from marshmallow.exceptions import ValidationError +############################################################################# +# BASIC TEMPLATE CONFIG FILE +############################################################################# + TEMPLATE_CONFIG_FILE = """ SQLALCHEMY_DATABASE_URI = "postgresql://monuser:monpassachanger@localhost:5432/mabase" URL_APPLICATION = 'http://url.com/geonature' -API_ENDPOINT = 'http://url.com/geonature/api' +API_ENDPOINT = 'http://url.com/geonature/api' SECRET_KEY = 'super secret key' @@ -37,6 +41,44 @@ [MEDIAS] """ +############################################################################# +# TAXON SHEET CONFIG FILE +############################################################################# + +TEMPLATE_TAXON_SHEET_CONFIG_FILE = """ + SQLALCHEMY_DATABASE_URI = "postgresql://monuser:monpassachanger@localhost:5432/mabase" + URL_APPLICATION = 'http://url.com/geonature' + API_ENDPOINT = 'http://url.com/geonature/api' + + SECRET_KEY = 'super secret key' + + DEFAULT_LANGUAGE=fr + [HOME] + TITLE = "Bienvenue dans GeoNature" + INTRODUCTION = "Texte d'introduction, configurable pour le modifier régulièrement ou le masquer" + FOOTER = "" + + # Configuration liée aux ID de BDD + [BDD] + + # Configuration générale du frontend + [FRONTEND] + ENABLE_PROFILES={ENABLE_PROFILES} + + # Configuration de la Synthese + [SYNTHESE] + ENABLE_TAXON_SHEETS={ENABLE_TAXON_SHEETS} + [SYNTHESE.TAXON_SHEET] + ENABLE_TAB_TAXONOMY={ENABLE_TAB_TAXONOMY} + ENABLE_TAB_PROFILE={ENABLE_TAB_PROFILE} + + # Configuration cartographique + [MAPCONFIG] + + # Configuration médias + [MEDIAS] + """ + @pytest.mark.usefixtures("temporary_transaction") class TestUtils: @@ -59,3 +101,27 @@ def test_utilstoml(self): with pytest.raises(ConfigError): load_and_validate_toml(f.name, GnPySchemaConf) + + @pytest.mark.parametrize( + "enable_profiles,enable_tab_profile,expected_enable_tab_profile", + [(True, True, True), (True, False, False), (False, False, False), (False, True, False)], + ) + def test_config_profiles_consistency( + self, enable_profiles, enable_tab_profile, expected_enable_tab_profile + ): + + profiles_config = TEMPLATE_TAXON_SHEET_CONFIG_FILE.format( + ENABLE_TAXON_SHEETS=True, + ENABLE_TAB_TAXONOMY=True, + ENABLE_PROFILES=enable_profiles, + ENABLE_TAB_PROFILE=enable_tab_profile, + ) + + with tempfile.NamedTemporaryFile(mode="w") as f: + f.write(profiles_config) + with pytest.raises(ConfigError): + config = load_and_validate_toml(f.name, GnPySchemaConf) + assert ( + config["SYNTHESE"]["TAXON_SHEET"]["ENABLE_TAB_PROFILE"] + == expected_enable_tab_profile + ) diff --git a/backend/geonature/utils/config_schema.py b/backend/geonature/utils/config_schema.py index 4d21312bba..d6c9574263 100644 --- a/backend/geonature/utils/config_schema.py +++ b/backend/geonature/utils/config_schema.py @@ -279,8 +279,8 @@ class ExportObservationSchema(Schema): class TaxonSheet(Schema): # -------------------------------------------------------------------- # SYNTHESE - TAXON_SHEET - ENABLE_PROFILE = fields.Boolean(load_default=True) - ENABLE_TAXONOMY = fields.Boolean(load_default=True) + ENABLE_TAB_PROFILE = fields.Boolean(load_default=True) + ENABLE_TAB_TAXONOMY = fields.Boolean(load_default=True) class Synthese(Schema): @@ -439,6 +439,7 @@ class Synthese(Schema): # -------------------------------------------------------------------- # SYNTHESE - TAXON_SHEET + ENABLE_TAXON_SHEETS = fields.Boolean(load_default=True) TAXON_SHEET = fields.Nested(TaxonSheet, load_default=TaxonSheet().load({})) @pre_load @@ -612,3 +613,13 @@ def insert_module_config(self, data, **kwargs): continue data[module_code] = get_module_config(dist) return data + + @post_load + def profile_display_coherence(self, data, **kwargs): + if ( + data["SYNTHESE"]["TAXON_SHEET"]["ENABLE_TAB_PROFILE"] + and not data["FRONTEND"]["ENABLE_PROFILES"] + ): + data["SYNTHESE"]["TAXON_SHEET"]["ENABLE_TAB_PROFILE"] = False + + return data diff --git a/config/default_config.toml.example b/config/default_config.toml.example index c6a2e164c6..773c0d2459 100644 --- a/config/default_config.toml.example +++ b/config/default_config.toml.example @@ -441,12 +441,14 @@ MEDIA_CLEAN_CRONTAB = "0 1 * * *" # Seulement les données de présence cd_nomenclature_observation_status = ['Pr'] + # Activer l'affichage des informations liées à la fiche taxon dans la synthèse + ENABLE_TAXON_SHEETS = true [SYNTHESE.TAXON_SHEET] # Options dédiées à la fiche taxon # Permet d'activer ou non l'onglet "Profil" - ENABLE_PROFILE = true + ENABLE_TAB_PROFILE = true # Permet d'activer ou non l'onglet "Taxonomie" - ENABLE_TAXONOMY = true + ENABLE_TAB_TAXONOMY = true # Gestion des demandes d'inscription [ACCOUNT_MANAGEMENT] @@ -623,8 +625,8 @@ MEDIA_CLEAN_CRONTAB = "0 1 * * *" # Encodage des fichiers importés autorisées ENCODAGE = ["UTF-8"] - # Bounding box des données de l'instance. - # Utilisé pour lever des warning lorsque les données sont en dehors. + # Bounding box des données de l'instance. + # Utilisé pour lever des warning lorsque les données sont en dehors. # Format: [XMIN, YMIN, XMAX, YMAX] # Par défaut: France métropolitaine incluant la Corse INSTANCE_BOUNDING_BOX = [-5.0, 41.0, 10.0, 51.15] @@ -643,7 +645,7 @@ MEDIA_CLEAN_CRONTAB = "0 1 * * *" # SRID autorisés pour les fichiers en entrée SRID = [ - {name = "WGS84", code = 4326}, + {name = "WGS84", code = 4326}, {name = "Lambert93", code = 2154} ] # Extensions autorisées (seul le csv est accepté actuellement) @@ -655,7 +657,7 @@ MEDIA_CLEAN_CRONTAB = "0 1 * * *" # Si le mapping des valeurs est désactivé, specifier l'identifiant du mapping qui doit être utilisé DEFAULT_VALUE_MAPPING_ID = 3 - # rempli les valeurs de nomenclature erroné par la valeur par defaut + # rempli les valeurs de nomenclature erroné par la valeur par defaut # Leve un warning et non une erreur sur les lignes concernées FILL_MISSING_NOMENCLATURE_WITH_DEFAULT_VALUE = false @@ -676,7 +678,7 @@ MEDIA_CLEAN_CRONTAB = "0 1 * * *" # Customiser le nom du fichier de rapport de l'import # Pour indiquer des données liés à l'import dans le nom du fichier ajouter le nom de la variable - # contenant cette dernière. Les variables suivantes sont accessibles : + # contenant cette dernière. Les variables suivantes sont accessibles : # - date_create_import -> date de création de l'import # - dataset.dataset_name -> nom du jeu de données de destination # - dataset.active -> Si le jeu de données de destination est actif @@ -703,7 +705,7 @@ MEDIA_CLEAN_CRONTAB = "0 1 * * *" # Id d'une liste de taxons permettant de restreindre l'import d'observations de taxons comprises dans cette dernière - # Lève une exception si un taxon n'appartenant pas à liste indiquée apparaît dans les donnés importées. + # Lève une exception si un taxon n'appartenant pas à liste indiquée apparaît dans les donnés importées. ID_LIST_TAXA_RESTRICTION = fields.Integer(load_default=None) # URL d'accès au module d'import diff --git a/frontend/src/app/shared/syntheseSharedModule/synthese-info-obs/synthese-info-obs.component.html b/frontend/src/app/shared/syntheseSharedModule/synthese-info-obs/synthese-info-obs.component.html index 5e0dfbcef3..876dfde2c8 100644 --- a/frontend/src/app/shared/syntheseSharedModule/synthese-info-obs/synthese-info-obs.component.html +++ b/frontend/src/app/shared/syntheseSharedModule/synthese-info-obs/synthese-info-obs.component.html @@ -191,7 +191,7 @@

diff --git a/frontend/src/app/syntheseModule/synthese.module.ts b/frontend/src/app/syntheseModule/synthese.module.ts index 43c2f6cded..46baba7032 100644 --- a/frontend/src/app/syntheseModule/synthese.module.ts +++ b/frontend/src/app/syntheseModule/synthese.module.ts @@ -41,6 +41,7 @@ const routes: Routes = [ { path: 'taxon/:cd_ref', component: TaxonSheetComponent, + canActivate: [RouteService], canActivateChild: [RouteService], children: [ { diff --git a/frontend/src/app/syntheseModule/taxon-sheet/taxon-sheet.route.service.ts b/frontend/src/app/syntheseModule/taxon-sheet/taxon-sheet.route.service.ts index 8676c6bb89..358800f474 100644 --- a/frontend/src/app/syntheseModule/taxon-sheet/taxon-sheet.route.service.ts +++ b/frontend/src/app/syntheseModule/taxon-sheet/taxon-sheet.route.service.ts @@ -4,6 +4,7 @@ import { RouterStateSnapshot, Router, CanActivateChild, + CanActivate } from '@angular/router'; import { ConfigService } from '@geonature/services/config.service'; import { Observable } from 'rxjs'; @@ -28,13 +29,13 @@ export const ALL_TAXON_SHEET_ADVANCED_INFOS_ROUTES: Array = [ { label: 'Taxonomie', path: 'taxonomy', - configEnabledField: 'ENABLE_TAXONOMY', + configEnabledField: 'ENABLE_TAB_TAXONOMY', component: TabTaxonomyComponent, }, { label: 'Profil', path: 'profile', - configEnabledField: 'ENABLE_PROFILE', + configEnabledField: 'ENABLE_TAB_PROFILE', component: TabProfileComponent, }, ]; @@ -42,7 +43,7 @@ export const ALL_TAXON_SHEET_ADVANCED_INFOS_ROUTES: Array = [ @Injectable({ providedIn: 'root', }) -export class RouteService implements CanActivateChild { +export class RouteService implements CanActivate, CanActivateChild { readonly TAB_LINKS = []; constructor( private _config: ConfigService, @@ -55,11 +56,19 @@ export class RouteService implements CanActivateChild { ); } } + canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean { + if(!this._config.SYNTHESE.ENABLE_TAXON_SHEETS){ + this._router.navigate(['/404'], { skipLocationChange: true }); + return false; + } + + return true; + } canActivateChild( childRoute: ActivatedRouteSnapshot, state: RouterStateSnapshot - ): Observable | Promise | boolean { + ): boolean { const targetedPath = childRoute.routeConfig.path; if (this.TAB_LINKS.map((tab) => tab.path).includes(targetedPath)) { return true; From 5e166f3747735f457ec007b64a0060c1a9bc211f Mon Sep 17 00:00:00 2001 From: Pierre-Narcisi Date: Wed, 15 Jan 2025 10:34:18 +0100 Subject: [PATCH 07/13] Fix(model): temporary fix for dataset counts of habs and taxs (#3320) --- .../geonature/core/gn_meta/models/datasets.py | 21 ------------------- backend/geonature/core/gn_meta/schemas.py | 2 -- .../acquisition_framework_template_pdf.html | 8 ------- .../metadataModule/af/af-card.component.html | 6 ------ 4 files changed, 37 deletions(-) diff --git a/backend/geonature/core/gn_meta/models/datasets.py b/backend/geonature/core/gn_meta/models/datasets.py index ea199f4204..eacb7785e8 100644 --- a/backend/geonature/core/gn_meta/models/datasets.py +++ b/backend/geonature/core/gn_meta/models/datasets.py @@ -139,27 +139,6 @@ def user_actors(self): def organism_actors(self): return [actor.organism for actor in self.cor_dataset_actor if actor.organism is not None] - @hybrid_property - def obs_count(self): - from geonature.core.gn_synthese.models import Synthese - - return db.session.scalar( - select(func.count(Synthese.id_synthese)) - .select_from(Synthese) - .where(Synthese.id_dataset == self.id_dataset) - ) - - @hybrid_property - def hab_count(self): - from gn_module_occhab.models import OccurenceHabitat, Station - - return db.session.scalar( - select(func.count(OccurenceHabitat.id_habitat)) - .select_from(OccurenceHabitat) - .where(Station.id_station == OccurenceHabitat.id_station) - .where(Station.id_dataset == self.id_dataset) - ) - def is_deletable(self): return not DB.session.execute(self.synthese_records.exists().select()).scalar() diff --git a/backend/geonature/core/gn_meta/schemas.py b/backend/geonature/core/gn_meta/schemas.py index b34f67aba3..d4e04bf44f 100644 --- a/backend/geonature/core/gn_meta/schemas.py +++ b/backend/geonature/core/gn_meta/schemas.py @@ -67,8 +67,6 @@ class Meta: cor_territories = MA.Nested(NomenclatureSchema, many=True, unknown=EXCLUDE) acquisition_framework = MA.Nested("AcquisitionFrameworkSchema", dump_only=True) sources = MA.Nested(SourceSchema, many=True, dump_only=True) - obs_count = fields.Int(dump_only=True) - hab_count = fields.Int(dump_only=True) @post_dump(pass_many=False, pass_original=True) def module_input(self, item, original, many, **kwargs): diff --git a/backend/geonature/templates/acquisition_framework_template_pdf.html b/backend/geonature/templates/acquisition_framework_template_pdf.html index 2bf12cee04..a7aa5b1990 100644 --- a/backend/geonature/templates/acquisition_framework_template_pdf.html +++ b/backend/geonature/templates/acquisition_framework_template_pdf.html @@ -298,14 +298,6 @@ {% if dataset['dataset_name']: %} {{ dataset['dataset_name'] }} {% endif %} -
- {% if dataset['obs_count'] > 0: %} - Nombre d'observations dans la Synthèse : {{ dataset['obs_count'] }} - {% endif %} -
- {% if dataset['hab_count'] > 0: %} - Nombre d'habitats dans occhab : {{ dataset['hab_count'] }} - {% endif %}

{%- endfor %} diff --git a/frontend/src/app/metadataModule/af/af-card.component.html b/frontend/src/app/metadataModule/af/af-card.component.html index 1a812625f2..8b6aff2f33 100644 --- a/frontend/src/app/metadataModule/af/af-card.component.html +++ b/frontend/src/app/metadataModule/af/af-card.component.html @@ -421,12 +421,6 @@
Jeux de données associés
{{ dataset.unique_dataset_id }}
-
- Nombre d'observations dans la Synthèse : {{ dataset['obs_count'] }} -
-
- Nombre d'habitats dans occhab : {{ dataset['hab_count'] }} -
From e0790c6746ed636d7719a306a10d1bd3153ac99a Mon Sep 17 00:00:00 2001 From: Jacques Fize <4259846+jacquesfize@users.noreply.github.com> Date: Wed, 15 Jan 2025 12:31:22 +0100 Subject: [PATCH 08/13] refact(sensitivity): instead of count query use rowcount to get number of removed sensitivity rule (#3323) --- backend/geonature/core/sensitivity/utils.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/backend/geonature/core/sensitivity/utils.py b/backend/geonature/core/sensitivity/utils.py index 42106d0151..bf4a881884 100644 --- a/backend/geonature/core/sensitivity/utils.py +++ b/backend/geonature/core/sensitivity/utils.py @@ -132,8 +132,4 @@ def insert_sensitivity_referential(source, csvfile): def remove_sensitivity_referential(source): whereclause = SensitivityRule.source == source - count_source = db.session.scalar( - sa.select(sa.func.count()).where(whereclause), - ) - db.session.execute(sa.delete(SensitivityRule).where(whereclause)) - return count_source + return db.session.execute(sa.delete(SensitivityRule).where(whereclause)).rowcount From 0f031f4a855460896ba95af55e09ea62a0251477 Mon Sep 17 00:00:00 2001 From: Jacques Fize <4259846+jacquesfize@users.noreply.github.com> Date: Wed, 15 Jan 2025 12:47:59 +0100 Subject: [PATCH 09/13] fix(login): fix bad login redirection when request admin panel (#3322) --- backend/geonature/core/errors.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/backend/geonature/core/errors.py b/backend/geonature/core/errors.py index cdf2a45ca5..cad5bc5ab8 100644 --- a/backend/geonature/core/errors.py +++ b/backend/geonature/core/errors.py @@ -26,13 +26,7 @@ def handle_unauthenticated_request(e): else: base_url = current_app.config["URL_APPLICATION"] login_path = "/#/login" # FIXME: move in config - api_endpoint = current_app.config["API_ENDPOINT"] - url_application = current_app.config["URL_APPLICATION"] - if urlparse(api_endpoint).netloc == urlparse(url_application).netloc: - next_url = request.full_path - else: - next_url = request.url - query_string = urlencode({"next": next_url}) + query_string = urlencode({"next": request.url}) return redirect(f"{base_url}{login_path}?{query_string}") From 36eaf474d688de89bed70a1ffde8f4d3577ccd5c Mon Sep 17 00:00:00 2001 From: Camille Monchicourt Date: Wed, 15 Jan 2025 13:52:25 +0100 Subject: [PATCH 10/13] feat(doc): complete validation and sensitivity documentation (#3317) --- docs/admin-manual.rst | 14 ++++++++------ docs/sensitivity.rst | 6 ++++-- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/docs/admin-manual.rst b/docs/admin-manual.rst index 4afedc6090..539e0e9cee 100644 --- a/docs/admin-manual.rst +++ b/docs/admin-manual.rst @@ -521,7 +521,7 @@ Pour chaque taxon (cd_ref) disposant de données dans la vue ``gn_profiles.v_syn Ces profils sont déclinés sur : -- Le module de validation permet d'attirer l'attention des validateurs sur les données qui sortent du "cadre" déjà connu pour le taxon considéré, et d'apporter des éléments de contexte en complément de la donnée en cours de validation +- Le module Validation permet d'attirer l'attention des validateurs sur les données qui sortent du "cadre" déjà connu pour le taxon considéré, et d'apporter des éléments de contexte en complément de la donnée en cours de validation - Le module Synthèse (fiche d'information, onglet validation) permet d'apporter des éléments de contexte en complément des données brutes consultées - Le module Occtax permet d'alerter les utilisateurs lors de la saisie de données qui sortent du "cadre" déjà connu pour un taxon considéré - Le processus de validation automatique permet de valider automatiquement les observations respectant le profil de taxons (non activé par défaut). @@ -2318,11 +2318,11 @@ D'autres élements sont paramètrables dans le module Synthese. La liste complè Module VALIDATION ----------------- -Le module VALIDATION, integré depuis la version 2.1.0 dans le coeur de GeoNature permet de valider des occurrences de taxon en s'appuyant sur les données présentes dans la SYNTHESE. Le module s'appuie sur le `standard Validation `_ du SINP et sur ses `nomenclatures officiels `_. +Le module VALIDATION, integré depuis la version 2.1.0 dans le coeur de GeoNature (mais optionnel) permet de valider des occurrences de taxon en s'appuyant sur les données présentes dans la SYNTHESE. Le module s'appuie sur le standard Validation du SINP et sur ses `nomenclatures officielles `_. -Afin de valider une occurrence, celle-ci doit impérativement avoir un UUID. En effet, la validation est stockée en BDD dans la table transversale ``gn_commons.t_validations`` (`voir doc `_ ) qui impose la présence de cet UUID. +Afin de valider une occurrence de taxons, celle-ci doit impérativement avoir un UUID. En effet, la validation est stockée en BDD dans la table transversale ``gn_commons.t_validations`` (`voir doc `_ ) qui impose la présence de cet UUID. -La table ``gn_commons.t_validations`` contient l'ensemble de l'historique de validation des occurrences. Pour une même occurrence (identifiée par un UUID unique) on peut donc retrouver plusieurs lignes dans la table correspondant au différents statuts de validation attribués à cet occurrence dans le temps. +La table ``gn_commons.t_validations`` contient l'ensemble de l'historique de validation des occurrences de taxons. Pour une même occurrence (identifiée par un UUID unique) on peut donc retrouver plusieurs lignes dans la table correspondant aux différents statuts de validation attribués à cette occurrence dans le temps. La vue ``gn_commons.v_latest_validation`` permet de récupérer le dernier statut de validation d'une occurrence. @@ -2353,10 +2353,10 @@ Gestion de l'affichage des colonnes de la liste via le paramètre ``COLUMN_LIST` E-mail `````` -Il est possible de personnaliser le message du mail envoyé aux observateurs. +Il est possible de personnaliser le message de l'email envoyé aux observateurs d'une observation quand on clique sur le bouton dédié à cela depuis la fiche détail d'une observation. Pour ce faire il faut modifier les paramètres ``MAIL_BODY`` et ``MAIL_SUBJECT`` -Pour afficher dans le mail des données relatives à l'observation ou au taxon il faut respecter la syntaxe suivante: +Pour afficher dans l'email des données relatives à l'observation ou au taxon il faut respecter la syntaxe suivante : ``${ d.NOM_PROPRIETE }`` Liste des propriétés disponibles : @@ -2367,6 +2367,8 @@ Liste des propriétés disponibles : - tous les champs de la synthèse (acquisition_framework, altitude_max, altitude_min, bio_status, blurring, cd_hab, cd_nom, comment_context, comment_description, date_min, depth_max, depth_min, determiner, diffusion_level, digital_proof, entity_source_pk_value, exist_proof, grp_method, grp_typ, last_action, life_stage, meta_create_date, meta_update_date, meta_v_taxref, meta_validation_date, nat_obj_geo, naturalness, nom_cite, non_digital_proof, obj_count, obs_technique, observation_status, observers, occ_behaviour, occ_stat_biogeo, place_name, precision, sample_number_proof, sensitivity, sex, source, type_count, unique_id_sinp, unique_id_sinp_grp, valid_status, validation_comment) - tous les champs du taxon (cd_nom, cd_ref, cd_sup, cd_taxsup, regne, ordre, classe, famille, group1_inpn, group2_inpn, id_rang, nom_complet, nom_habitat, nom_rang, nom_statut, nom_valide, nom_vern) +Il est aussi possible de modifier la structure du message de notification envoyé automatiquement à un observateur quand une de ses observations voit son statut de validation modifié, dans la table `gn_notifications.bib_notifications_templates`. + Validation automatique """""""""""""""""""""" diff --git a/docs/sensitivity.rst b/docs/sensitivity.rst index d32e303209..13f4983946 100644 --- a/docs/sensitivity.rst +++ b/docs/sensitivity.rst @@ -73,8 +73,10 @@ que l’un des critères ou l’une des zones correspond à l’observation. Si des règles de sensibilité sont définies avec des conditions de critères sur une nomenclature (Statut biologique ou Comportement), alors ces règles sont appliquées aussi si la nomenclature n'est pas renseignée -(par principe de précaution), en dupliquant les règles pour les valeurs de -nomenclature "Non renseigné", "Ne sait pas", "Indéterminé",... (https://github.com/PnX-SI/GeoNature/blob/30c27266495b4affc635f79748c9984feb81a6d7/backend/geonature/core/sensitivity/utils.py#L37-L54). +(par principe de précaution), en associant aussi des critères à ces règles +(dans la table `gn_sensitivity.cor_sensitivity_criteria`) pour les valeurs +de nomenclature "Non renseigné", "Ne sait pas", "Indéterminé",... +(https://github.com/PnX-SI/GeoNature/blob/30c27266495b4affc635f79748c9984feb81a6d7/backend/geonature/core/sensitivity/utils.py#L37-L54). Certaines règles sont définies non pas pour une espèce donnée mais pour un rang supérieur. Ces règles sont artificiellement dupliquées pour chaques espèces From 6ce72e41d0a7f59d9e3fe2f11b530933500e0db1 Mon Sep 17 00:00:00 2001 From: Jacques Fize <4259846+jacquesfize@users.noreply.github.com> Date: Wed, 15 Jan 2025 13:52:48 +0100 Subject: [PATCH 11/13] bump : taxhub 2.1.1 (#3321) --- backend/dependencies/TaxHub | 2 +- backend/requirements-dependencies.in | 2 +- backend/requirements-dev.txt | 6 ++++-- backend/requirements.txt | 8 +++++--- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/backend/dependencies/TaxHub b/backend/dependencies/TaxHub index 8e36e7e39a..1e505008fa 160000 --- a/backend/dependencies/TaxHub +++ b/backend/dependencies/TaxHub @@ -1 +1 @@ -Subproject commit 8e36e7e39a3df2e1e1c1bff47038d29e21b5011d +Subproject commit 1e505008fa74b9cdc6ccf096db00a5b6dfd8568e diff --git a/backend/requirements-dependencies.in b/backend/requirements-dependencies.in index be8cd5e56f..a70d9dca40 100644 --- a/backend/requirements-dependencies.in +++ b/backend/requirements-dependencies.in @@ -3,5 +3,5 @@ pypnnomenclature>=1.6.4,<2 pypn_habref_api>=0.4.1,<1 utils-flask-sqlalchemy-geo>=0.3.2,<1 utils-flask-sqlalchemy>=0.4.1,<1 -taxhub==2.1.0 +taxhub==2.1.1 pypn-ref-geo>=1.5.3,<2 diff --git a/backend/requirements-dev.txt b/backend/requirements-dev.txt index 9e231c07b8..b628e5b45e 100644 --- a/backend/requirements-dev.txt +++ b/backend/requirements-dev.txt @@ -104,7 +104,7 @@ cligj==0.7.2 # via fiona contourpy==1.3.0 # via bokeh -cryptography==44.0.0 +cryptography==43.0.3 # via authlib cssselect2==0.7.0 # via weasyprint @@ -180,7 +180,9 @@ flask-wtf==1.2.2 # -r requirements-common.in # usershub fonttools[woff]==4.55.3 - # via weasyprint + # via + # fonttools + # weasyprint geoalchemy2==0.16.0 # via utils-flask-sqlalchemy-geo geojson==3.2.0 diff --git a/backend/requirements.txt b/backend/requirements.txt index 36b85df8b9..683da3c34d 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -72,7 +72,7 @@ cligj==0.7.2 # via fiona contourpy==1.3.0 # via bokeh -cryptography==44.0.0 +cryptography==43.0.3 # via authlib cssselect2==0.7.0 # via weasyprint @@ -143,7 +143,9 @@ flask-weasyprint==1.1.0 flask-wtf==1.2.2 # via -r requirements-common.in fonttools[woff]==4.55.3 - # via weasyprint + # via + # fonttools + # weasyprint geoalchemy2==0.16.0 # via utils-flask-sqlalchemy-geo geojson==3.2.0 @@ -312,7 +314,7 @@ sqlalchemy==1.4.54 # utils-flask-sqlalchemy # utils-flask-sqlalchemy-geo # wtforms-sqlalchemy -taxhub==2.1.0 +taxhub==2.1.1 # via # -r requirements-dependencies.in # pypnnomenclature From 03322ed7f65cb62503f4262a0d50aabe3445cca3 Mon Sep 17 00:00:00 2001 From: Camille Monchicourt Date: Wed, 15 Jan 2025 14:11:52 +0100 Subject: [PATCH 12/13] Changelog 2.15.2 --- docs/CHANGELOG.md | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index ff941afdb6..047160207a 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,14 +1,32 @@ # CHANGELOG +## 2.15.2 (2025-01-15) + +**🚀 Nouveautés** + +- [Accueil] Optimisation du calcul des statistiques (#3309, par @dba-sig-sfepm et @jacquesfize) +- [Profils de taxon] Amélioration, homogénéisation et mise en cohérence des paramètres d'activation ou non des profils de taxons (#3311, par @edelclaux) +- [TaxHub] Mise à jour de TaxHub en version 2.1.1 (#3321, par @amandine-sahl et @jacquesfize) +- [Documentation] Compléments de la documentation du module Validation et de la sensibilité (#3317, par @camillemonchicourt) +- [Développement] Redémarrage automatique du backend quand un fichier de configuration `.toml` est modifié (#3316, par @jacquesfize) + +**🐛 Corrections** + +- [Accueil] Correction de la prise en compte de la portée des permissions dans le calcul des statistiques (#3166, par @jacquesfize et @edelclaux) +- [Sensibilité] Correction du comptage du nombre de règles supprimées dans la commande `geonature sensitivity remove-referential` (#3323, par @jacquesfize) +- [Synthèse] Correction de la disparition du filtre par `id_import` après l'affichage d'une fiche observation (par @jacquesfize) +- [Authentification] Correction des redirections du module Admin lors de l'authentification (#3322, par @jacquesfize) +- Correction d'une régression de performances de la récupération des JDD, introduite dans la 2.15.1 (#3320, par @Pierre-Narcisi) + ## 2.15.1 (2025-01-10) **🚀 Nouveautés** -- [Métadonnées]Amélioration de la recherche libre des métadonnées en cherchant chaque mot indépendamment (#3295, par @jbrieuclp) +- [Métadonnées] Amélioration de la recherche libre des métadonnées en cherchant chaque mot indépendamment (#3295, par @jbrieuclp) - [FicheTaxon] Amélioration de l'affichage de la photo du taxon sur les fiches taxon (#3287, par @edelclaux) - [Documentation] Conversion du changelog en format markdown (#3297, par @jacquesfize) - [Documentation] Complément et mise en forme de la documentation et publication sur Readthedocs (#3306, par @jacquesfize) -- [Dévelopement] Ajout d'un fichier `Makefile` pour faciliter l'usage des commandes de développement (#3300, par @jacquesfize & @edelclaux) +- [Développement] Ajout d'un fichier `Makefile` pour faciliter l'usage des commandes de développement (#3300, par @jacquesfize & @edelclaux) - [Installation] Ajout des nouvelles mailles INPN lors de l'installation de GeoNature (#3293, par @jacquesfize) **🐛 Corrections** @@ -17,7 +35,7 @@ - [Discussions] Correction des performances de la requête de récupération des discussions (#3307, par @jacquesfize) - [Métadonnées] Correction du nombre de taxons sur les fiches des cadres d'acquisition (#3228, par @jacquesfize) - [Authentification] Correction des redirections lors de l'authentification (#3305, par @jacquesfize) -- [Import] Correction de la selection automatique du JDD lors de l'import depuis la fiche d'un JDD (#3293, par @jacquesfize) +- [Import] Correction de la sélection automatique du JDD lors de l'import depuis la fiche d'un JDD (#3293, par @jacquesfize) - [Import] Correction de la mise à jour des mappings publics (#3293, par @jacquesfize) - [Import] Correction de la sauvegarde des checkbox dans le mapping des champs (#3293, par @Pierre-Narcisi) - [Import] Correction de la sélection des champs `auto_generate` (#3293, par @Pierre-Narcisi) From 3361d27ccd57b0b8fc2b41b406e25605158bf161 Mon Sep 17 00:00:00 2001 From: Jacques Fize <4259846+jacquesfize@users.noreply.github.com> Date: Wed, 15 Jan 2025 14:40:03 +0100 Subject: [PATCH 13/13] fix(general_stats): re-enable cruved on the stats computed (#3327) * fix(general_stats): fix problem where cruved had no impact on the stats computed --- backend/geonature/core/gn_synthese/routes.py | 19 +++++++++---------- .../gn_synthese/utils/query_select_sqla.py | 3 ++- .../tests/benchmarks/test_benchmark_home.py | 6 ++++++ 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/backend/geonature/core/gn_synthese/routes.py b/backend/geonature/core/gn_synthese/routes.py index de822af52f..3df6e82d7c 100644 --- a/backend/geonature/core/gn_synthese/routes.py +++ b/backend/geonature/core/gn_synthese/routes.py @@ -943,20 +943,19 @@ def general_stats(permissions): results = {"nb_allowed_datasets": nb_allowed_datasets} queries = { - "nb_obs": select( - func.count(Synthese.id_synthese), - ), + "nb_obs": select(Synthese.id_synthese), "nb_distinct_species": select( - func.count(func.distinct(Synthese.cd_nom)), - ), - "nb_distinct_observer": select( - func.count(func.distinct(Synthese.observers)), + func.distinct(Synthese.cd_nom), ), + "nb_distinct_observer": select(func.distinct(Synthese.observers)), } + for key, query in queries.items(): synthese_query = SyntheseQuery(Synthese, query, {}) - synthese_query.filter_query_with_cruved(g.current_user, permissions) - results[key] = db.session.scalar(synthese_query.query) + synthese_query.filter_query_with_permissions(g.current_user, permissions) + results[key] = db.session.scalar( + sa.select(func.count("*")).select_from(synthese_query.query) + ) data = { "nb_data": results["nb_obs"], @@ -1561,7 +1560,7 @@ def list_all_reports(permissions): # On vérifie les permissions en lecture sur la synthese synthese_query = select(Synthese.id_synthese).select_from(Synthese) synthese_query_obj = SyntheseQuery(Synthese, synthese_query, {}) - synthese_query_obj.filter_query_with_cruved(g.current_user, permissions) + synthese_query_obj.filter_query_with_permissions(g.current_user, permissions) cte_synthese = synthese_query_obj.query.cte("cte_synthese") query = query.where(TReport.id_synthese == cte_synthese.c.id_synthese) 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 732d0b5df2..6496c0888d 100644 --- a/backend/geonature/core/gn_synthese/utils/query_select_sqla.py +++ b/backend/geonature/core/gn_synthese/utils/query_select_sqla.py @@ -204,10 +204,11 @@ def filter_query_with_permissions(self, user, permissions): where_clause = self.build_permissions_filter(user=user, permissions=permissions) self.query = self.query.where(where_clause) - def filter_query_with_cruved(self, user, scope): + def filter_query_with_cruved(self, user, scope: int): """ Filter the query with the cruved authorization of a user """ + assert isinstance(scope, int) if scope in (1, 2): # get id synthese where user is observer subquery_observers = ( diff --git a/backend/geonature/tests/benchmarks/test_benchmark_home.py b/backend/geonature/tests/benchmarks/test_benchmark_home.py index 0b4bd842aa..ba5ae4303e 100644 --- a/backend/geonature/tests/benchmarks/test_benchmark_home.py +++ b/backend/geonature/tests/benchmarks/test_benchmark_home.py @@ -18,6 +18,12 @@ class TestBenchmarkHome: test_general_stats = BenchmarkTest( + CLIENT_GET, + [CLater("""url_for("gn_synthese.general_stats")""")], + dict(user_profile="user", fixtures=[]), + )() + + test_general_stats_admin = BenchmarkTest( CLIENT_GET, [CLater("""url_for("gn_synthese.general_stats")""")], dict(user_profile="admin_user", fixtures=[]),