From c4c362f954a12d1179f7150512d5b461bb5a7def Mon Sep 17 00:00:00 2001 From: TheoLechemia Date: Wed, 6 Mar 2024 15:04:49 +0100 Subject: [PATCH] SYNTHESE : add customizable fields to the list - close #2946 --- backend/geonature/core/gn_synthese/routes.py | 5 +- backend/geonature/tests/test_synthese.py | 13 +++-- backend/geonature/utils/config_schema.py | 10 ++-- config/default_config.toml.example | 10 +++- docs/CHANGELOG.md | 6 +++ .../synthese-list.component.html | 53 +++++++++++++++---- .../synthese-list/synthese-list.component.ts | 28 ++++++++++ 7 files changed, 103 insertions(+), 22 deletions(-) diff --git a/backend/geonature/core/gn_synthese/routes.py b/backend/geonature/core/gn_synthese/routes.py index c7c5ac36ab..52916aea83 100644 --- a/backend/geonature/core/gn_synthese/routes.py +++ b/backend/geonature/core/gn_synthese/routes.py @@ -22,6 +22,7 @@ from werkzeug.exceptions import Forbidden, NotFound, BadRequest, Conflict from werkzeug.datastructures import MultiDict from sqlalchemy import distinct, func, desc, asc, select, case +from sqlalchemy.orm import joinedload, lazyload, selectinload, contains_eager from geojson import FeatureCollection, Feature import sqlalchemy as sa from sqlalchemy.orm import load_only, aliased, Load, with_expression @@ -148,7 +149,9 @@ def get_observations_for_web(permissions): # Get Column Frontend parameter to return only the needed columns param_column_list = { - col["prop"] for col in current_app.config["SYNTHESE"]["LIST_COLUMNS_FRONTEND"] + col["prop"] + for col in current_app.config["SYNTHESE"]["LIST_COLUMNS_FRONTEND"] + + current_app.config["SYNTHESE"]["ADDITIONAL_COLUMNS_FRONTEND"] } # Init with compulsory columns columns = [] diff --git a/backend/geonature/tests/test_synthese.py b/backend/geonature/tests/test_synthese.py index 254f4f4af0..aff7627fe2 100644 --- a/backend/geonature/tests/test_synthese.py +++ b/backend/geonature/tests/test_synthese.py @@ -120,7 +120,11 @@ class CustomRequiredConverter(GeoModelConverter): def _add_column_kwargs(self, kwargs, column): super()._add_column_kwargs(kwargs, column) - default_cols = map(lambda col: col["prop"], config["SYNTHESE"]["LIST_COLUMNS_FRONTEND"]) + default_cols = map( + lambda col: col["prop"], + config["SYNTHESE"]["LIST_COLUMNS_FRONTEND"] + + config["SYNTHESE"]["ADDITIONAL_COLUMNS_FRONTEND"], + ) required_cols = list(default_cols) + MANDATORY_COLUMNS kwargs["required"] = column.name in required_cols @@ -135,8 +139,6 @@ class VSyntheseForWebAppSchema(GeoAlchemyAutoSchema): class Meta: model = VSyntheseForWebApp - feature_geometry = "the_geom_4326" - sqla_session = db.session model_converter = CustomRequiredConverter @@ -175,6 +177,10 @@ def test_required_fields_and_format(self, app, users): {"prop": "count_min_max", "name": "Dénombrement"}, {"prop": "nom_vern_or_lb_nom", "name": "Taxon"}, ] + + app.config["SYNTHESE"]["ADDITIONAL_COLUMNS_FRONTEND"] += [ + {"prop": "lb_nom", "name": "Nom scientifique"} + ] url_ungrouped = url_for("gn_synthese.get_observations_for_web") set_logged_user(self.client, users["admin_user"]) resp = self.client.get(url_ungrouped) @@ -229,6 +235,7 @@ def test_get_observations_for_web(self, app, users, synthese_data, taxon_attribu "name": "Cdnom", } ] + # schema["properties"]["observations"]["items"]["required"] = # test on synonymy and taxref attrs filters = { "cd_ref": [taxon_attribut.bib_nom.cd_ref], diff --git a/backend/geonature/utils/config_schema.py b/backend/geonature/utils/config_schema.py index fd44323df4..b8ec596eba 100644 --- a/backend/geonature/utils/config_schema.py +++ b/backend/geonature/utils/config_schema.py @@ -355,15 +355,11 @@ class Synthese(Schema): # -------------------------------------------------------------------- # SYNTHESE - OBSERVATIONS LIST - # Listes des champs renvoyés par l'API synthese '/synthese' - # Si on veut afficher des champs personnalisés dans le frontend (paramètre LIST_COLUMNS_FRONTEND) il faut - # d'abbord s'assurer que ces champs sont bien renvoyé par l'API ! + # Colonnes affichées par défaut sur la liste des résultats de la synthese # Champs disponibles: tous ceux de la vue 'v_synthese_for_web_app - COLUMNS_API_SYNTHESE_WEB_APP = fields.List( - fields.String, load_default=DEFAULT_COLUMNS_API_SYNTHESE - ) - # Colonnes affichées sur la liste des résultats de la sytnthese LIST_COLUMNS_FRONTEND = fields.List(fields.Dict, load_default=DEFAULT_LIST_COLUMN) + # Colonnes affichables sur la liste des résultats de la synthese via la modale de selection des colonnes + ADDITIONAL_COLUMNS_FRONTEND = fields.List(fields.Dict, load_default=[]) # -------------------------------------------------------------------- # SYNTHESE - DOWNLOADS (AKA EXPORTS) diff --git a/config/default_config.toml.example b/config/default_config.toml.example index 68cf538b6c..3a6f1323b8 100644 --- a/config/default_config.toml.example +++ b/config/default_config.toml.example @@ -269,8 +269,9 @@ MEDIA_CLEAN_CRONTAB = "0 1 * * *" # Colonne à afficher par défaut sur la liste des résultats de la synthese # Choisir le champ 'prop' parmis les colonnes suivantes : - # id (=id_synthese), date_min, cd_nom, lb_nom, nom_vern_or_lb_nom, + # id_synthese, date_min, cd_nom, lb_nom, nom_vern_or_lb_nom, # observers, dataset_name, url_source, count_min_max + # La liste des colonnes affichables est celle de la vue `gn_synthese.v_synthese_for_export`+ `nom_vern_or_lb_nom` et `count_min_max` LIST_COLUMNS_FRONTEND = [ { prop = "nom_vern_or_lb_nom", name = "Taxon" }, { prop = "date_min", name = "Date début" }, @@ -278,6 +279,13 @@ MEDIA_CLEAN_CRONTAB = "0 1 * * *" { prop = "dataset_name", name = "Jeu de données" } ] + # Colonnes affichables dans la liste des résulats, mais masquées par default + # Possibilité de les ajouter en cliquant sur la route crantée en haut de la liste + # La liste des colonnes affichables est celle de la vue `gn_synthese.v_synthese_for_export` + ADDITIONAL_COLUMNS_FRONTEND = [ + { prop = "lb_nom", name = "Nom scientifique" }, + ] + # Nombre de résultats à afficher pour la recherche autocompletée de taxon TAXON_RESULT_NUMBER = 20 diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 573dfbcab7..be29b47200 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,6 +1,12 @@ CHANGELOG ========= +## 2.14.1 (unrealesed) + +🚀 Nouveautés + +- [Synthèse] Possibilité d'ajouter des champs supplémentaires à la liste de résultats via le paramètre `ADDITIONAL_COLUMNS_FRONTEND`. Ces champs sont masqués par défaut et controlables depuis l'interface (#2946) + 2.14.0 - Talpa europaea 👓 (2024-02-28) --------------------------------------- diff --git a/frontend/src/app/syntheseModule/synthese-results/synthese-list/synthese-list.component.html b/frontend/src/app/syntheseModule/synthese-results/synthese-list/synthese-list.component.html index cb5f9d89b4..6a73cc54f2 100644 --- a/frontend/src/app/syntheseModule/synthese-results/synthese-list/synthese-list.component.html +++ b/frontend/src/app/syntheseModule/synthese-results/synthese-list/synthese-list.component.html @@ -4,7 +4,7 @@ #table class="material striped margin-top-xs table-size expandable" [rows]="mapListService.tableData" - [columns]="SYNTHESE_CONFIG.LIST_COLUMNS_FRONTEND" + [columns]="defaultColumns" [columnMode]="'force'" [headerHeight]="50" [footerHeight]="35" @@ -19,6 +19,18 @@ (select)="mapListService.onRowSelect($event)" > + + + file_download - + + + + diff --git a/frontend/src/app/syntheseModule/synthese-results/synthese-list/synthese-list.component.ts b/frontend/src/app/syntheseModule/synthese-results/synthese-list/synthese-list.component.ts index 9f75aeb7c2..4cc0ebbf7b 100644 --- a/frontend/src/app/syntheseModule/synthese-results/synthese-list/synthese-list.component.ts +++ b/frontend/src/app/syntheseModule/synthese-results/synthese-list/synthese-list.component.ts @@ -23,6 +23,7 @@ import { ColumnMode } from '@swimlane/ngx-datatable'; import { CruvedStoreService } from '@geonature_common/service/cruved-store.service'; import { SyntheseInfoObsComponent } from '@geonature/shared/syntheseSharedModule/synthese-info-obs/synthese-info-obs.component'; import { ConfigService } from '@geonature/services/config.service'; +import { FormArray, FormControl } from '@angular/forms'; @Component({ selector: 'pnx-synthese-list', templateUrl: 'synthese-list.component.html', @@ -39,6 +40,8 @@ export class SyntheseListComponent implements OnInit, OnChanges, AfterContentChe public inpnMapUrl: string; public downloadMessage: string; public ColumnMode: ColumnMode; + public availableColumns: Array = []; + public defaultColumns: Array = []; //input to resize datatable on searchbar toggle @Input() searchBarHidden: boolean; @Input() inputSyntheseData: GeoJSON; @@ -60,6 +63,8 @@ export class SyntheseListComponent implements OnInit, OnChanges, AfterContentChe const h = document.documentElement.clientHeight; this.rowNumber = Math.trunc(h / 37); + this.initListColumns(); + // On map click, select on the list a change the page this.mapListService.onMapClik$.subscribe((ids) => { this.resetSorting(); @@ -104,6 +109,20 @@ export class SyntheseListComponent implements OnInit, OnChanges, AfterContentChe this.table.sorts = []; } + initListColumns() { + this.defaultColumns = this.SYNTHESE_CONFIG.LIST_COLUMNS_FRONTEND; + let allColumnsTemp = [ + ...this.SYNTHESE_CONFIG.LIST_COLUMNS_FRONTEND, + ...this.SYNTHESE_CONFIG.ADDITIONAL_COLUMNS_FRONTEND, + ]; + this.availableColumns = allColumnsTemp.map((col) => { + col['checked'] = this.defaultColumns.some((defcol) => { + return defcol.name == col.name; + }); + return col; + }); + } + /** * Restore previous selected rows when sort state return to 'undefined'. * With ngx-datable sortType must be 'multi' to use 3 states : asc, desc and undefined ! @@ -143,6 +162,10 @@ export class SyntheseListComponent implements OnInit, OnChanges, AfterContentChe modalRef.componentInstance.useFrom = 'synthese'; } + openModalCol($event, modal) { + this.ngbModal.open(modal); + } + openDownloadModal() { this.ngbModal.open(SyntheseModalDownloadComponent, { size: 'lg', @@ -161,6 +184,11 @@ export class SyntheseListComponent implements OnInit, OnChanges, AfterContentChe return [pad(d.getDate()), pad(d.getMonth() + 1), d.getFullYear()].join('-'); } + toggleColumnNames(col) { + col.checked = !col.checked; + this.defaultColumns = this.availableColumns.filter((col) => col.checked); + } + ngOnChanges(changes) { if (changes.inputSyntheseData && changes.inputSyntheseData.currentValue) { // reset page 0 when new data appear