diff --git a/backend/geonature/core/gn_synthese/routes.py b/backend/geonature/core/gn_synthese/routes.py index e2dc80e61e..6ef882d551 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__) @@ -958,80 +959,88 @@ 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) + .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) ) - .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/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;