Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CONFIG] Taxon sheet and config #3312

Open
wants to merge 7 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 74 additions & 65 deletions backend/geonature/core/gn_synthese/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
VMTaxrefListForautocomplete,
)

from geonature import app
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Souvent, on passe plutôt par le current_app de flask


routes = Blueprint("gn_synthese", __name__)

Expand Down Expand Up @@ -958,80 +959,88 @@ def general_stats(permissions):
return data


@routes.route("/taxon_stats/<int:cd_nom>", 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/<int:cd_nom>", 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])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
select([LAreas.id_area])
select(LAreas.id_area)

.where(LAreas.id_type == BibAreasTypes.id_type)
.where(BibAreasTypes.type_code == area_type)
Comment on lines +992 to +993
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
.where(LAreas.id_type == BibAreasTypes.id_type)
.where(BibAreasTypes.type_code == area_type)
.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"])
Expand Down
15 changes: 13 additions & 2 deletions backend/geonature/utils/config_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -279,8 +279,8 @@
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):
Expand Down Expand Up @@ -439,6 +439,7 @@

# --------------------------------------------------------------------
# SYNTHESE - TAXON_SHEET
ENABLE_TAXON_SHEETS = fields.Boolean(load_default=True)
TAXON_SHEET = fields.Nested(TaxonSheet, load_default=TaxonSheet().load({}))

@pre_load
Expand Down Expand Up @@ -612,3 +613,13 @@
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

Check warning on line 623 in backend/geonature/utils/config_schema.py

View check run for this annotation

Codecov / codecov/patch

backend/geonature/utils/config_schema.py#L623

Added line #L623 was not covered by tests

return data
18 changes: 10 additions & 8 deletions config/default_config.toml.example
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -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]
Expand All @@ -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)
Expand All @@ -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

Expand All @@ -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
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ <h4 class="mr-auto gn-color">
<a
color="primary"
class="btn btn-xs align-self-start mr-2 link-infos"
*ngIf="selectedObsTaxonDetail && config.FRONTEND['ENABLE_PROFILES']"
*ngIf="selectedObsTaxonDetail && config.SYNTHESE.ENABLE_TAXON_SHEETS"
[routerLink]="['/synthese/taxon', selectedObsTaxonDetail?.cd_ref]"
target="_blank"
mat-stroked-button
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,10 @@
<a
class="Link"
[routerLink]="['taxon/' + row.cd_nom]"
*ngIf="row.hasOwnProperty('cd_nom'); else cellDefault"
*ngIf="
config.SYNTHESE.ENABLE_TAXON_SHEETS && row.hasOwnProperty('cd_nom');
else cellDefault
"
matTooltip="Afficher la fiche du taxon"
>
<ng-container *ngTemplateOutlet="cellDefault"></ng-container>
Expand Down
1 change: 1 addition & 0 deletions frontend/src/app/syntheseModule/synthese.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ const routes: Routes = [
{
path: 'taxon/:cd_ref',
component: TaxonSheetComponent,
canActivate: [RouteService],
canActivateChild: [RouteService],
children: [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
RouterStateSnapshot,
Router,
CanActivateChild,
CanActivate
} from '@angular/router';
import { ConfigService } from '@geonature/services/config.service';
import { Observable } from 'rxjs';
Expand All @@ -28,21 +29,21 @@ export const ALL_TAXON_SHEET_ADVANCED_INFOS_ROUTES: Array<Tab> = [
{
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,
},
];

@Injectable({
providedIn: 'root',
})
export class RouteService implements CanActivateChild {
export class RouteService implements CanActivate, CanActivateChild {
readonly TAB_LINKS = [];
constructor(
private _config: ConfigService,
Expand All @@ -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<boolean> | Promise<boolean> | boolean {
): boolean {
const targetedPath = childRoute.routeConfig.path;
if (this.TAB_LINKS.map((tab) => tab.path).includes(targetedPath)) {
return true;
Expand Down
Loading