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

Merged
138 changes: 73 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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

il me semble que la, on est avant le current_app


routes = Blueprint("gn_synthese", __name__)

Expand Down Expand Up @@ -958,80 +959,87 @@ 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)
.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
68 changes: 67 additions & 1 deletion backend/geonature/tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -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'

Expand All @@ -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:
Expand All @@ -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
)
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
Loading
Loading