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

[Fiche taxon] Ajout d'un onglet "Observateurs" dans la fiche taxon #3204

Merged
Merged
Changes from 1 commit
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
7fc66bb
feat: add 'ENABLE_TAXON_SHEETS' option in 'SYNTHESE'
edelclaux Jan 10, 2025
31d7353
feat: ENABLE_XXX to ENABLE_TAB_XXX for taxon sheet tab
edelclaux Jan 10, 2025
a57d0bc
feat: add a relation between 'FRONTEND.ENABLE_PROFILES' and 'SYNTHESE…
edelclaux Jan 10, 2025
9cf9a35
fix: ENABLE_TAB_PROFILES --> ENABLE_TAB_PROFILE
edelclaux Jan 10, 2025
ed90d50
lint
edelclaux Jan 10, 2025
eafc120
feat(config): profile display config coherence moved to config_schema
jacquesfize Jan 13, 2025
17678ab
Remove obsolete comment
edelclaux Jan 13, 2025
a956d35
feat: add test for config processing
edelclaux Jan 14, 2025
cf2b79c
fix: apply suggestion on id_area
edelclaux Jan 14, 2025
cf0a1a6
feat: squash commit for observers
edelclaux Jan 14, 2025
b96cfe7
Merge commit 'cf2b79c8d99cef24dbc9be064b50d9de4553d400' into feat/spe…
edelclaux Jan 14, 2025
8aa1aed
feat: adjust test to cd_nom --> cd_ref
edelclaux Jan 14, 2025
c750e03
Merge branch 'develop' into test-rebase
edelclaux Jan 15, 2025
04240f5
fix: remove @json_resp
edelclaux Jan 15, 2025
1b1d22f
feat: use SyntehseDataPagination/Sort in home discussions
edelclaux Jan 15, 2025
7fd401b
Merge commit '9c792e4754c16f539e9bd519f6a57e2dcd957454' into feat/spe…
edelclaux Feb 3, 2025
d8993c1
feat: squash commit for observers
edelclaux Jan 14, 2025
821c6f8
feat: ENABLE_XXX to ENABLE_TAB_XXX for taxon sheet tab
edelclaux Jan 10, 2025
31bffb0
feat: add a relation between 'FRONTEND.ENABLE_PROFILES' and 'SYNTHESE…
edelclaux Jan 10, 2025
6cfd852
lint
edelclaux Jan 10, 2025
086a9b7
feat(config): profile display config coherence moved to config_schema
jacquesfize Jan 13, 2025
5f7cd72
Remove obsolete comment
edelclaux Jan 13, 2025
39e32e4
feat: use SyntehseDataPagination/Sort in home discussions
edelclaux Jan 15, 2025
3cd0fc0
fix rebase
jacquesfize Feb 28, 2025
2b75e5a
Merge branch 'species-page-rebased' into feat/species-pages/observers
edelclaux Mar 3, 2025
d9d5077
feat: make observer name case insensitive by having the all lowercase
edelclaux Mar 3, 2025
b724fed
ui: add titlecase to observer column
edelclaux Mar 3, 2025
4387e8f
feat: use a list of separator instead of a single separator to split …
edelclaux Mar 3, 2025
1af6ebf
lint: black 25
edelclaux Mar 3, 2025
8808a80
fix: update taxon observers route tests to lowercase
edelclaux Mar 4, 2025
04e5a58
fix: update taxon observers route tests to lowercase
edelclaux Mar 4, 2025
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
Next Next commit
feat: add 'ENABLE_TAXON_SHEETS' option in 'SYNTHESE'
edelclaux committed Jan 10, 2025
commit 7fc66bb98f08a3084032763db902236592cecc05
136 changes: 71 additions & 65 deletions backend/geonature/core/gn_synthese/routes.py
Original file line number Diff line number Diff line change
@@ -82,6 +82,7 @@
VMTaxrefListForautocomplete,
)

from geonature import app

routes = Blueprint("gn_synthese", __name__)

@@ -957,81 +958,86 @@ def general_stats(permissions):
}
return data

## ############################################################################
## TAXON SHEET ROUTES
## ############################################################################

@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"""
if app.config["SYNTHESE"]["ENABLE_TAXON_SHEETS"]:

area_type = request.args.get("area_type")
@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"""

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

# 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))
if not area_type:
raise BadRequest("Missing 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"),
]
# 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,
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))

# 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"),
]
)
.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)
.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))
)
.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()

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"])
1 change: 1 addition & 0 deletions backend/geonature/utils/config_schema.py
Original file line number Diff line number Diff line change
@@ -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
2 changes: 2 additions & 0 deletions config/default_config.toml.example
Original file line number Diff line number Diff line change
@@ -441,6 +441,8 @@ 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"
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -93,7 +93,7 @@
<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>
1 change: 1 addition & 0 deletions frontend/src/app/syntheseModule/synthese.module.ts
Original file line number Diff line number Diff line change
@@ -41,6 +41,7 @@ const routes: Routes = [
{
path: 'taxon/:cd_ref',
component: TaxonSheetComponent,
canActivate: [RouteService],
canActivateChild: [RouteService],
children: [
{
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@ import {
RouterStateSnapshot,
Router,
CanActivateChild,
CanActivate
} from '@angular/router';
import { ConfigService } from '@geonature/services/config.service';
import { Observable } from 'rxjs';
@@ -42,7 +43,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,
@@ -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;