Skip to content

Commit

Permalink
Merge commit 'cf2b79c8d99cef24dbc9be064b50d9de4553d400' into feat/spe…
Browse files Browse the repository at this point in the history
…cies-pages/observers-rebase
  • Loading branch information
edelclaux committed Jan 14, 2025
2 parents cf0a1a6 + cf2b79c commit b96cfe7
Show file tree
Hide file tree
Showing 8 changed files with 158 additions and 61 deletions.
105 changes: 55 additions & 50 deletions backend/geonature/core/gn_synthese/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -965,65 +965,70 @@ def general_stats(permissions):
return data


@routes.route("/taxon_stats/<int:cd_ref>", methods=["GET"])
@permissions.check_cruved_scope("R", get_scope=True, module_code="SYNTHESE")
@json_resp
def taxon_stats(scope, cd_ref):
"""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_ref>", methods=["GET"])
@permissions.check_cruved_scope("R", get_scope=True, module_code="SYNTHESE")
@json_resp
def taxon_stats(scope, cd_ref):
"""Return stats for a specific taxon"""

if not TaxonSheetUtils.is_valid_area_type(area_type):
raise BadRequest("Invalid area_type parameter")
area_type = request.args.get("area_type")
if not area_type:
raise BadRequest("Missing area_type parameter")

areas_subquery = TaxonSheetUtils.get_area_subquery(area_type)
taxref_cd_nom_list = TaxonSheetUtils.get_cd_nom_list_from_cd_ref(cd_ref)
if not TaxonSheetUtils.is_valid_area_type(area_type):
raise BadRequest("Invalid area_type parameter")

# 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,
areas_subquery = TaxonSheetUtils.get_area_subquery(area_type)
taxref_cd_nom_list = TaxonSheetUtils.get_cd_nom_list_from_cd_ref(cd_ref)

# 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)
)
.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))
)
.where(Synthese.cd_nom.in_(taxref_cd_nom_list))
)

synthese_query = TaxonSheetUtils.get_synthese_query_with_scope(g.current_user, scope, query)
result = DB.session.execute(synthese_query)
synthese_stats = result.fetchone()

data = {
"cd_ref": cd_ref,
"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 = TaxonSheetUtils.get_synthese_query_with_scope(g.current_user, scope, query)
result = DB.session.execute(synthese_query)
synthese_stats = result.fetchone()

data = {
"cd_ref": cd_ref,
"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


if app.config["SYNTHESE"]["TAXON_SHEET"]["ENABLE_TAB_OBSERVERS"]:
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,9 +279,9 @@ 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_OBSERVERS = 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 @@ -440,6 +440,7 @@ class Synthese(Schema):

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

FIELD_OBSERVERS_SEPARATOR = fields.String(load_default=",")
Expand Down Expand Up @@ -615,3 +616,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
6 changes: 4 additions & 2 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
# Permet d'activer ou non la section "Observers"
ENABLE_TAB_OBSERVERS = true

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 @@ -29,13 +30,13 @@ 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,
},
{
Expand All @@ -49,7 +50,7 @@ export const ALL_TAXON_SHEET_ADVANCED_INFOS_ROUTES: Array<Tab> = [
@Injectable({
providedIn: 'root',
})
export class RouteService implements CanActivateChild {
export class RouteService implements CanActivate, CanActivateChild {
readonly TAB_LINKS = [];
constructor(
private _config: ConfigService,
Expand All @@ -62,11 +63,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

0 comments on commit b96cfe7

Please sign in to comment.