From 9b70253fc0e885ac296f044397f92ba5e1f67ddf Mon Sep 17 00:00:00 2001 From: Jean-Pascal MILCENT Date: Thu, 24 Nov 2022 08:31:56 +0100 Subject: [PATCH] Fix: visit form, site without municipality --- CHANGELOG.md | 15 + VERSION | 2 +- .../blueprint.py | 350 +++++++++--------- .../conf_schema_toml.py | 1 - .../migrations/data/schema.sql | 66 ++-- .../models.py | 143 +++---- config/conf_gn_module.sample.toml | 3 - .../detail-visit/detail-visit.component.html | 8 +- .../detail-visit/detail-visit.component.ts | 212 ++++++----- .../app/form-visit/form-visit.component.html | 2 +- .../app/form-visit/form-visit.component.ts | 60 +-- frontend/app/gnModule.module.ts | 3 +- .../app/list-visit/list-visit.component.html | 5 +- frontend/app/services/form.service.ts | 23 -- frontend/app/services/store.service.ts | 2 +- 15 files changed, 463 insertions(+), 432 deletions(-) delete mode 100644 frontend/app/services/form.service.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index ee2f964..c58d0fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,21 @@ et ce projet adhère à [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [NonPublié] +## [1.1.1] - 2022-11-22 + +### Changements + +* Changement du chemin du web service `/export_visit` pour `/visits/export` afin de mieux respecter les principes REST. +* Les paramètres du web service `/visits/export` peuvent maintenant être utilisé de manière combinés. +* ⚠️ La vue `pr_monitoring_flora_territory.export_visits` a été corrigé afin de supporter les sites sans commune. Nous n'avons pas utilisé de révision Alembic pour la mise à jour. Il est nécessaire de mettre à jour cette vue manuellement à l'aide de Psql par exemple. Voir le code SQL de la vue dans le fichier [schema.sql](backend/gn_module_monitoring_flora_territory/migrations/data/schema.sql). + + +### Corrections + +* Autorisé les sites a ne pas avoir de commune associé dans le cas des sites hors France. Corrige l'export des visites et l'affichage des informations du site. +* Suppression des avertissements liés à l'utilisation du mode récursif avec la bibliothèque `utils_flask_sqla`. +* Correction de la gestion des perturbations dans le formulaire d'édition d'une visite. + ## [1.1.0] - 2022-11-22 diff --git a/VERSION b/VERSION index 9084fa2..524cb55 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.1.0 +1.1.1 diff --git a/backend/gn_module_monitoring_flora_territory/blueprint.py b/backend/gn_module_monitoring_flora_territory/blueprint.py index 7b27708..69380b7 100644 --- a/backend/gn_module_monitoring_flora_territory/blueprint.py +++ b/backend/gn_module_monitoring_flora_territory/blueprint.py @@ -6,38 +6,39 @@ from geoalchemy2.shape import to_shape from geojson import FeatureCollection from sqlalchemy import and_, distinct, desc +from sqlalchemy.orm import joinedload from sqlalchemy.sql.expression import func from apptax.taxonomie.models import Taxref from geonature.core.gn_commons.models import TModules from geonature.core.gn_monitoring.models import ( - corVisitObserver, - TBaseVisits, corSiteModule, corSiteArea, + corVisitObserver, + TBaseVisits, ) from geonature.core.gn_permissions import decorators as permissions from geonature.core.gn_permissions.tools import get_or_fetch_user_cruved from geonature.core.ref_geo.models import LAreas, BibAreasTypes -from geonature.utils.env import DB, ROOT_DIR +from geonature.utils.env import db, ROOT_DIR from geonature.utils.utilsgeometry import FionaShapeService -from pypnnomenclature.models import TNomenclatures from pypnusershub.db.models import Organisme, User from utils_flask_sqla.response import json_resp, to_json_resp, to_csv_resp from .models import ( - TInfoSite, - TVisiteSFT, - CorVisitGrid, - ExportVisits, + VisitGrid, + VisitPerturbation, + VisitsExport, + SiteInfos, + Visit, ) from .repositories import check_user_cruved_visit, check_year_visit from .utils import prepare_output, prepare_input, fprint -blueprint = Blueprint("pr_suivi_flore_territoire", __name__) +blueprint = Blueprint("pr_monitoring_flora_territory", __name__) log = logging.getLogger(__name__) @@ -45,15 +46,14 @@ @json_resp def get_sites(): """ - Retourne la liste des ZP + Retourne la liste des sites. """ parameters = request.args - id_type_commune = blueprint.config["id_type_commune"] - # Query to get Sites id - query = DB.session.query(distinct(TInfoSite.id_infos_site)).select_from( - TInfoSite.__table__.outerjoin( - TBaseVisits, TBaseVisits.id_base_site == TInfoSite.id_base_site + # "From" shared between two queries + from_clause = ( + SiteInfos.__table__.outerjoin( + TBaseVisits, TBaseVisits.id_base_site == SiteInfos.id_base_site ) .outerjoin( corVisitObserver, @@ -61,66 +61,49 @@ def get_sites(): ) .outerjoin(User, User.id_role == corVisitObserver.c.id_role) .outerjoin(Organisme, Organisme.id_organisme == User.id_organisme) - .outerjoin(corSiteArea, corSiteArea.c.id_base_site == TInfoSite.id_base_site) + .outerjoin(corSiteArea, corSiteArea.c.id_base_site == SiteInfos.id_base_site) + .outerjoin(LAreas, LAreas.id_area == corSiteArea.c.id_area) .outerjoin( - LAreas, - and_( - LAreas.id_area == corSiteArea.c.id_area, - LAreas.id_type == id_type_commune, - ), + BibAreasTypes, + and_(BibAreasTypes.id_type == LAreas.id_type, BibAreasTypes.type_code == "COM"), ) ) + # Query to get Sites id + query = db.session.query(distinct(SiteInfos.id_infos_site)).select_from(from_clause) + if "id_base_site" in parameters: - query = query.filter(TInfoSite.id_base_site == parameters["id_base_site"]) + query = query.filter(SiteInfos.id_base_site == parameters["id_base_site"]) if "cd_nom" in parameters: - query = query.filter(TInfoSite.cd_nom == parameters["cd_nom"]) + query = query.filter(SiteInfos.cd_nom == parameters["cd_nom"]) - if "organism" in parameters: + if "organism" in parameters and parameters["organism"] != "null": query = query.filter(Organisme.id_organisme == parameters["organism"]) - if "municipality" in parameters: + if "municipality" in parameters and parameters["municipality"] != "null": query = query.filter(LAreas.id_area == parameters["municipality"]) - if "year" in parameters: + if "year" in parameters and parameters["year"] != "null": query = query.filter( func.date_part("year", TBaseVisits.visit_date_min) == parameters["year"] ) - idSiteList = query.all() + + id_site_list = query.all() # Query to get Sites data with previous id query = ( - DB.session.query( - TInfoSite, + db.session.query( + SiteInfos, func.max(TBaseVisits.visit_date_min), Taxref.nom_complet, func.count(distinct(TBaseVisits.id_base_visit)), func.string_agg(distinct(Organisme.nom_organisme), ", "), func.string_agg(distinct(LAreas.area_name), ", "), ) - .select_from( - TInfoSite.__table__.outerjoin( - TBaseVisits, TBaseVisits.id_base_site == TInfoSite.id_base_site - ) - .outerjoin( - corVisitObserver, - corVisitObserver.c.id_base_visit == TBaseVisits.id_base_visit, - ) - .outerjoin(User, User.id_role == corVisitObserver.c.id_role) - .outerjoin(Organisme, Organisme.id_organisme == User.id_organisme) - .outerjoin(Taxref, TInfoSite.cd_nom == Taxref.cd_nom) - .outerjoin(corSiteArea, corSiteArea.c.id_base_site == TInfoSite.id_base_site) - .outerjoin( - LAreas, - and_( - LAreas.id_area == corSiteArea.c.id_area, - LAreas.id_type == id_type_commune, - ), - ) - ) - .filter(TInfoSite.id_infos_site.in_(idSiteList)) - .group_by(TInfoSite, Taxref.nom_complet) + .select_from(from_clause.outerjoin(Taxref, SiteInfos.cd_nom == Taxref.cd_nom)) + .filter(SiteInfos.id_infos_site.in_(id_site_list)) + .group_by(SiteInfos, Taxref.nom_complet) ) data = query.all() @@ -150,32 +133,37 @@ def get_sites(): return FeatureCollection(features) -@blueprint.route("/sites/", methods=["GET"]) +@blueprint.route("/sites/", methods=["GET"]) @json_resp -def get_one_site(id_info_site): +def get_one_site(id_base_site): """ - Retourne les infos d'un site à partir de l'id_info_site + Retourne les infos d'un site à partir de l'id_base_site """ - (base_site_infos, municipalities) = ( - DB.session.query( - TInfoSite, + base_site_infos = ( + db.session.query(SiteInfos) + .filter(SiteInfos.id_base_site == id_base_site) + .first() + ) + municipalities = ( + db.session.query( func.string_agg( distinct(func.concat(LAreas.area_name, " (", LAreas.area_code, ")")), ", " ).filter(LAreas.area_name != None), ) - .outerjoin(corSiteArea, corSiteArea.c.id_base_site == TInfoSite.id_base_site) - .outerjoin(LAreas, LAreas.id_area == corSiteArea.c.id_area) - .join( - BibAreasTypes, - and_(BibAreasTypes.id_type == LAreas.id_type, BibAreasTypes.type_code == "COM"), + .select_from( + SiteInfos.__table__ + .outerjoin(corSiteArea, corSiteArea.c.id_base_site == SiteInfos.id_base_site) + .outerjoin(LAreas, LAreas.id_area == corSiteArea.c.id_area) + .join( + BibAreasTypes, + and_(BibAreasTypes.id_type == LAreas.id_type, BibAreasTypes.type_code == "COM"), + ) ) - .filter(TInfoSite.id_base_site == id_info_site) - .group_by(TInfoSite.id_infos_site) - .first() + .filter(SiteInfos.id_base_site == id_base_site) + .scalar() ) infos_site = base_site_infos.as_dict(fields=["base_site", "sciname"]) - fprint(infos_site) output = infos_site["base_site"] output["sciname"] = { "code": infos_site["cd_nom"], @@ -192,27 +180,51 @@ def get_visits(): Retourne toutes les visites du module """ parameters = request.args - q = DB.session.query(TVisiteSFT) + query = db.session.query(Visit) if "id_base_site" in parameters: - q = q.filter(TVisiteSFT.id_base_site == parameters["id_base_site"]) - data = q.all() - return [d.as_dict(True) for d in data] + query = query.filter(Visit.id_base_site == parameters["id_base_site"]) + data = query.all() + fields = get_visit_fields() + return [d.as_dict(fields=fields) for d in data] -@blueprint.route("/visits/", methods=["GET"]) +@blueprint.route("/visits/", methods=["GET"]) @json_resp def get_one_visit(id_visit): """ Retourne une visite """ - data = DB.session.query(TVisiteSFT).get(id_visit) - return data.as_dict(recursif=True) + return get_visit_details(id_visit) + + +def get_visit_details(id_visit): + data = ( + db.session.query(Visit) + .options(joinedload(Visit.cor_visit_perturbation)) + .filter(Visit.id_base_visit == id_visit) + .first() + ) + fields = get_visit_fields() + return data.as_dict(fields=fields) + +def get_visit_fields(): + return [ + "id_base_visit", + "id_base_site", + "visit_date_min", + "comments", + "cor_visit_grid", + "cor_visit_perturbation.nomenclature", + "observers", + ] -@blueprint.route("/visits", methods=["POST"]) + +# TODO: split this web into one for POST and one other for PATCH. See SHT module. +@blueprint.route("/visits", methods=["POST", "PATCH"]) @permissions.check_cruved_scope("R", True) @json_resp -def add_visit(info_role): +def edit_visit(info_role): """ Poste une nouvelle visite ou édite une ancienne """ @@ -225,134 +237,130 @@ def add_visit(info_role): # Set generic infos got from config data["id_dataset"] = blueprint.config["id_dataset"] data["id_module"] = ( - DB.session.query(TModules.id_module) + db.session.query(TModules.id_module) .filter(TModules.module_code == blueprint.config["MODULE_CODE"]) .scalar() ) - try: - tab_perturbation = data.pop("cor_visit_perturbation") - except: - pass - + # Extract data + perturbations_ids = [] + if "cor_visit_perturbation" in data: + perturbations_ids = data.pop("cor_visit_perturbation") + visit_grids = [] if "cor_visit_grid" in data: - tab_visit_grid = data.pop("cor_visit_grid") - else: - tab_visit_grid = None - - tab_observer = data.pop("cor_visit_observer") - - visit = TVisiteSFT(**data) - visit.as_dict(True) - # pour que visit prenne en compte des relations - # sinon elle prend pas en compte le fait qu'on efface toutes les perturbations quand on édite par ex. - try: - perturs = ( - DB.session.query(TNomenclatures) - .filter(TNomenclatures.id_nomenclature.in_(tab_perturbation)) - .all() - ) - for per in perturs: - visit.cor_visit_perturbation.append(per) - except: - pass + visit_grids = data.pop("cor_visit_grid") + observers_ids = [] + if "cor_visit_observer" in data: + observers_ids = data.pop("cor_visit_observer") - if tab_visit_grid is not None: - for v in tab_visit_grid: - visit_grid = CorVisitGrid(**v) - visit.cor_visit_grid.append(visit_grid) - - observers = DB.session.query(User).filter(User.id_role.in_(tab_observer)).all() - for o in observers: - visit.observers.append(o) + # Create visit object + visit = Visit(**data) + # Manage mode (update/insert) + idv = None if visit.id_base_visit: + idv = visit.id_base_visit + + # Add/Update perturbations + if idv: + db.session.query(VisitPerturbation).filter_by(id_base_visit=idv).delete() + for perturbation_id in perturbations_ids: + perturbation = { "id_nomenclature_perturbation": perturbation_id } + if idv: + perturbation["id_base_visit"] = idv + fprint(perturbation) + visit_perturbation = VisitPerturbation(**perturbation) + visit.cor_visit_perturbation.append(visit_perturbation) + + # Add/Update grids + if idv: + db.session.query(VisitGrid).filter_by(id_base_visit=idv).delete() + for grid in visit_grids: + visit_grid = VisitGrid(**grid) + visit.cor_visit_grid.append(visit_grid) + + # Add/Update observers + observers = db.session.query(User).filter(User.id_role.in_(observers_ids)).all() + for observer in observers: + visit.observers.append(observer) + + # Add/Update database + if idv: user_cruved = get_or_fetch_user_cruved( session=session, id_role=info_role.id_role, module_code=blueprint.config["MODULE_CODE"] ) update_cruved = user_cruved["U"] check_user_cruved_visit(info_role, visit, update_cruved) - DB.session.merge(visit) + print(visit) + visit = db.session.merge(visit) else: - DB.session.add(visit) + db.session.add(visit) - DB.session.commit() + db.session.commit() + if not idv: + db.session.refresh(visit) - return visit.as_dict(recursif=True) + return get_visit_details(visit.id_base_visit) -@blueprint.route("/export_visit", methods=["GET"]) +@blueprint.route("/visits/export", methods=["GET"]) def export_visits(): """ Télécharge les données d'une visite (ou des visites ) """ - parameters = request.args - # q = q.filter(TInfoSite.id_base_site == parameters['id_base_site']) - export_format = parameters["export_format"] if "export_format" in request.args else "shapefile" - file_name = datetime.datetime.now().strftime("%Y_%m_%d_%Hh%Mm%S") - q = DB.session.query(ExportVisits) - + query = db.session.query(VisitsExport) if "id_base_visit" in parameters: - q = DB.session.query(ExportVisits).filter( - ExportVisits.id_base_visit == parameters["id_base_visit"] - ) - elif "id_base_site" in parameters: - q = DB.session.query(ExportVisits).filter( - ExportVisits.id_base_site == parameters["id_base_site"] - ) - elif "organisme" in parameters: - q = DB.session.query(ExportVisits).filter(ExportVisits.organisme == parameters["organisme"]) - elif "commune" in parameters: - q = DB.session.query(ExportVisits).filter(ExportVisits.area_name == parameters["commune"]) - elif "year" in parameters: - q = DB.session.query(ExportVisits).filter( - func.date_part("year", ExportVisits.visit_date) == parameters["year"] - ) - elif "cd_nom" in parameters: - q = DB.session.query(ExportVisits).filter(ExportVisits.cd_nom == parameters["cd_nom"]) + query = query.filter(VisitsExport.id_base_visit == parameters["id_base_visit"]) - data = q.all() - features = [] + if "id_base_site" in parameters: + query = query.filter(VisitsExport.id_base_site == parameters["id_base_site"]) - if export_format == "geojson": - for d in data: - feature = d.as_geofeature("geom", "id_area", False) - features.append(feature) - result = FeatureCollection(features) + if "organisme" in parameters: + query = query.filter(VisitsExport.organisme == parameters["organisme"]) - return to_json_resp(result, as_file=True, filename=file_name, indent=4, extension="geojson") + if "commune" in parameters: + query = query.filter(VisitsExport.area_name == parameters["commune"]) - elif export_format == "csv": - tab_visit = [] + if "year" in parameters: + query = query.filter(func.date_part("year", VisitsExport.visit_date) == parameters["year"]) - for d in data: - visit = d.as_dict() - geom_wkt = to_shape(d.geom) - visit["geom"] = geom_wkt + if "cd_nom" in parameters: + query = query.filter(VisitsExport.cd_nom == parameters["cd_nom"]) - tab_visit.append(visit) + results = query.all() - return to_csv_resp(file_name, tab_visit, tab_visit[0].keys(), ";") + file_name = datetime.datetime.now().strftime("%Y_%m_%d_%Hh%Mm%S") + if export_format == "geojson": + features = [] + for data in results: + feature = data.as_geofeature("geom", "id_area", False) + features.append(feature) + geojson = FeatureCollection(features) + return to_json_resp(geojson, as_file=True, filename=file_name, indent=4, extension="geojson") + elif export_format == "csv": + visits = [] + for data in results: + visit = data.as_dict() + geom_wkt = to_shape(data.geom) + visit["geom"] = geom_wkt + visits.append(visit) + headers = visits[0].keys() + return to_csv_resp(file_name, visits, headers, ";") else: - dir_path = str(ROOT_DIR / "backend/static/shapefiles") - FionaShapeService.create_shapes_struct( - db_cols=ExportVisits.__mapper__.c, + db_cols=VisitsExport.__mapper__.c, srid=2154, dir_path=dir_path, file_name=file_name, ) - - for row in data: - FionaShapeService.create_feature(row.as_dict(), row.geom) - + for data in results: + FionaShapeService.create_feature(data.as_dict(), data.geom) FionaShapeService.save_and_zip_shapefiles() - return send_from_directory(dir_path, file_name + ".zip", as_attachment=True) @@ -360,13 +368,13 @@ def export_visits(): @json_resp def get_visits_years(): """ - Retourne toutes les années de visites du module + Retourne toutes les années de visites du module. """ query = ( - DB.session.query(func.to_char(TVisiteSFT.visit_date_min, "YYYY")) - .join(TInfoSite, TInfoSite.id_base_site == TVisiteSFT.id_base_site) - .order_by(desc(func.to_char(TVisiteSFT.visit_date_min, "YYYY"))) - .group_by(func.to_char(TVisiteSFT.visit_date_min, "YYYY")) + db.session.query(func.to_char(Visit.visit_date_min, "YYYY")) + .join(SiteInfos, SiteInfos.id_base_site == Visit.id_base_site) + .order_by(desc(func.to_char(Visit.visit_date_min, "YYYY"))) + .group_by(func.to_char(Visit.visit_date_min, "YYYY")) ) results = query.all() @@ -380,10 +388,10 @@ def get_visits_years(): @json_resp def get_municipalities(): """ - Retourne toutes les communes présents dans le module + Retourne toutes les communes liées au module. """ query = ( - DB.session.query(LAreas) + db.session.query(LAreas) .join(BibAreasTypes, BibAreasTypes.id_type == LAreas.id_type) .join(corSiteArea, LAreas.id_area == corSiteArea.c.id_area) .join(corSiteModule, corSiteModule.c.id_base_site == corSiteArea.c.id_base_site) @@ -404,13 +412,13 @@ def get_municipalities(): @json_resp def get_organisms(): """ - Retourne la liste de tous les organismes présents + Retourne la liste de tous les organismes liés au module. """ query = ( - DB.session.query(Organisme) + db.session.query(Organisme) .join(User, User.id_organisme == Organisme.id_organisme) .join(corVisitObserver, corVisitObserver.c.id_role == User.id_role) - .join(TVisiteSFT, TVisiteSFT.id_base_visit == corVisitObserver.c.id_base_visit) + .join(Visit, Visit.id_base_visit == corVisitObserver.c.id_base_visit) ) results = query.all() diff --git a/backend/gn_module_monitoring_flora_territory/conf_schema_toml.py b/backend/gn_module_monitoring_flora_territory/conf_schema_toml.py index 49bedd5..1b98a3f 100644 --- a/backend/gn_module_monitoring_flora_territory/conf_schema_toml.py +++ b/backend/gn_module_monitoring_flora_territory/conf_schema_toml.py @@ -57,7 +57,6 @@ class GnModuleSchemaConf(Schema): default_list_visit_columns = fields.List(fields.Dict(), load_default=default_list_visit_columns) id_dataset = fields.Integer(load_default=1) id_type_maille = fields.Integer(load_default=33) - id_type_commune = fields.Integer(load_default=25) id_menu_list_user = fields.Integer(load_default=1) id_list_taxon = fields.Integer(load_default=30) export_srid = fields.Integer(load_default=2154) diff --git a/backend/gn_module_monitoring_flora_territory/migrations/data/schema.sql b/backend/gn_module_monitoring_flora_territory/migrations/data/schema.sql index 2870ce2..b9f44b5 100644 --- a/backend/gn_module_monitoring_flora_territory/migrations/data/schema.sql +++ b/backend/gn_module_monitoring_flora_territory/migrations/data/schema.sql @@ -104,34 +104,40 @@ ALTER TABLE ONLY cor_visit_perturbation -- Create view to export visits CREATE OR REPLACE VIEW pr_monitoring_flora_territory.export_visits AS WITH - observers AS ( - SELECT - v.id_base_visit, - string_agg(roles.nom_role::text || ' ' || roles.prenom_role::text, ',') AS observateurs, - org.nom_organisme AS organisme - FROM gn_monitoring.t_base_visits v - JOIN gn_monitoring.cor_visit_observer observer ON observer.id_base_visit = v.id_base_visit - JOIN utilisateurs.t_roles roles ON roles.id_role = observer.id_role - JOIN utilisateurs.bib_organismes org ON roles.id_organisme = org.id_organisme - GROUP BY v.id_base_visit, org.nom_organisme - ), - perturbations AS ( - SELECT - v.id_base_visit, - string_agg(n.label_default, ',') AS label_perturbation - FROM gn_monitoring.t_base_visits v - JOIN pr_monitoring_flora_territory.cor_visit_perturbation p ON v.id_base_visit = p.id_base_visit - JOIN ref_nomenclatures.t_nomenclatures n ON p.id_nomenclature_perturbation = n.id_nomenclature - GROUP BY v.id_base_visit - ), - area AS ( - SELECT bs.id_base_site, - a.id_area, - a.area_name - FROM ref_geo.l_areas a - JOIN gn_monitoring.t_base_sites bs ON ST_intersects(ST_TRANSFORM(a.geom, 4326), bs.geom) - WHERE a.id_type=ref_geo.get_id_area_type('COM') - ) +observers AS ( + SELECT + v.id_base_visit, + string_agg(roles.nom_role::text || ' ' || roles.prenom_role::text, ',') AS observateurs, + org.nom_organisme AS organisme + FROM gn_monitoring.t_base_visits AS v + JOIN gn_monitoring.cor_visit_observer AS observer + ON observer.id_base_visit = v.id_base_visit + JOIN utilisateurs.t_roles AS roles + ON roles.id_role = observer.id_role + JOIN utilisateurs.bib_organismes AS org + ON roles.id_organisme = org.id_organisme + GROUP BY v.id_base_visit, org.nom_organisme +), +perturbations AS ( + SELECT + v.id_base_visit, + string_agg(n.label_default, ',') AS label_perturbation + FROM gn_monitoring.t_base_visits AS v + JOIN pr_monitoring_flora_territory.cor_visit_perturbation AS p + ON v.id_base_visit = p.id_base_visit + JOIN ref_nomenclatures.t_nomenclatures AS n + ON p.id_nomenclature_perturbation = n.id_nomenclature + GROUP BY v.id_base_visit +), +area AS ( + SELECT bs.id_base_site, + a.id_area, + a.area_name + FROM ref_geo.l_areas AS a + JOIN gn_monitoring.t_base_sites AS bs + ON ST_intersects(ST_Transform(a.geom, 4326), bs.geom) + WHERE a.id_type = ref_geo.get_id_area_type('COM') +) -- All the meshes of a site and their visits SELECT sites.id_base_site, @@ -151,7 +157,7 @@ SELECT taxon.cd_nom, area.area_name, ar.id_type -FROM gn_monitoring.t_base_sites sites +FROM gn_monitoring.t_base_sites AS sites JOIN gn_monitoring.cor_site_area AS cor ON (cor.id_base_site = sites.id_base_site) JOIN gn_monitoring.t_base_visits AS visits @@ -162,7 +168,7 @@ FROM gn_monitoring.t_base_sites sites ON (obs.id_base_visit = visits.id_base_visit) LEFT JOIN perturbations AS per ON (per.id_base_visit = visits.id_base_visit) - JOIN area + LEFT JOIN area ON (area.id_base_site = sites.id_base_site) JOIN pr_monitoring_flora_territory.t_infos_site AS info ON (info.id_base_site = sites.id_base_site) diff --git a/backend/gn_module_monitoring_flora_territory/models.py b/backend/gn_module_monitoring_flora_territory/models.py index b36ea30..8e2379f 100644 --- a/backend/gn_module_monitoring_flora_territory/models.py +++ b/backend/gn_module_monitoring_flora_territory/models.py @@ -7,11 +7,10 @@ from geonature.core.gn_monitoring.models import ( TBaseSites, TBaseVisits, - corSiteArea, corVisitObserver, ) from geonature.core.ref_geo.models import LAreas -from geonature.utils.env import DB +from geonature.utils.env import db from geonature.utils.utilsgeometry import shapeserializable from pypnnomenclature.models import TNomenclatures from pypnusershub.db.models import User @@ -19,92 +18,94 @@ from utils_flask_sqla_geo.serializers import geoserializable -@serializable -@geoserializable -class TInfoSite(DB.Model): +class MonitoringFloraTerritory(db.Model): """ - Modèle d'une ZP + Module db master parent abstract class. + Debug is more easy. """ + __abstract__ = True + + def __repr__(self): + return str(self.__class__) + ": " + str(self.__dict__) + + def __str__(self): + return str(self.__class__) + ": " + str(self.__dict__) + + + +@serializable +@geoserializable +class SiteInfos(MonitoringFloraTerritory): __tablename__ = "t_infos_site" __table_args__ = {"schema": "pr_monitoring_flora_territory"} + id_infos_site = db.Column(db.Integer, primary_key=True) + id_base_site = db.Column(db.Integer, ForeignKey(TBaseSites.id_base_site)) + cd_nom = db.Column(db.Integer, ForeignKey(Taxref.cd_nom)) - id_infos_site = DB.Column(DB.Integer, primary_key=True) - id_base_site = DB.Column(DB.Integer, ForeignKey(TBaseSites.id_base_site)) - cd_nom = DB.Column(DB.Integer, ForeignKey(Taxref.cd_nom)) - - base_site = DB.relationship(TBaseSites) - sciname = DB.relationship(Taxref) + base_site = db.relationship(TBaseSites) + sciname = db.relationship(Taxref) geom = association_proxy("base_site", "geom") def get_geofeature(self): return self.as_geofeature("geom", "id_infos_site") - -class corVisitPerturbation(DB.Model): +@serializable +class VisitPerturbation(MonitoringFloraTerritory): __tablename__ = "cor_visit_perturbation" __table_args__ = {"schema": "pr_monitoring_flora_territory"} - id_base_visit = DB.Column( + id_base_visit = db.Column( "id_base_visit", - DB.Integer, - ForeignKey("gn_monitoring.t_base_visits.id_base_visit"), + db.Integer, + ForeignKey(TBaseVisits.id_base_visit), primary_key=True, ) - id_nomenclature_perturbation = DB.Column( + id_nomenclature_perturbation = db.Column( "id_nomenclature_perturbation", - DB.Integer, + db.Integer, ForeignKey(TNomenclatures.id_nomenclature), primary_key=True, ) + nomenclature = db.relationship( + TNomenclatures, + primaryjoin=(id_nomenclature_perturbation == TNomenclatures.id_nomenclature), + foreign_keys=[id_nomenclature_perturbation], + lazy="joined", + ) -@serializable -class CorVisitGrid(DB.Model): - """ - Corespondance entre une maille et une visite - """ +@serializable +class VisitGrid(MonitoringFloraTerritory): __tablename__ = "cor_visit_grid" __table_args__ = {"schema": "pr_monitoring_flora_territory"} - - id_area = DB.Column(DB.Integer, ForeignKey(LAreas.id_area), primary_key=True) - id_base_visit = DB.Column(DB.Integer, ForeignKey(TBaseVisits.id_base_visit), primary_key=True) - presence = DB.Column(DB.Boolean) - uuid_base_visit = DB.Column(UUID(as_uuid=True)) + id_area = db.Column(db.Integer, ForeignKey(LAreas.id_area), primary_key=True) + id_base_visit = db.Column(db.Integer, ForeignKey(TBaseVisits.id_base_visit), primary_key=True) + presence = db.Column(db.Boolean) + uuid_base_visit = db.Column(UUID(as_uuid=True)) @serializable @shapeserializable -class TVisiteSFT(TBaseVisits): - """ - Visite sur une ZP - et corespondance avec ses mailles - """ - +class Visit(TBaseVisits): __tablename__ = "t_base_visits" __table_args__ = {"schema": "gn_monitoring", "extend_existing": True} - cor_visit_grid = DB.relationship( - "CorVisitGrid", - primaryjoin=(CorVisitGrid.id_base_visit == TBaseVisits.id_base_visit), - foreign_keys=[CorVisitGrid.id_base_visit], - ) - cor_visit_perturbation = DB.relationship( - TNomenclatures, - secondary=corVisitPerturbation.__table__, - primaryjoin=(corVisitPerturbation.id_base_visit == TBaseVisits.id_base_visit), - secondaryjoin=( - corVisitPerturbation.id_nomenclature_perturbation == TNomenclatures.id_nomenclature - ), - foreign_keys=[ - corVisitPerturbation.id_base_visit, - corVisitPerturbation.id_nomenclature_perturbation, - ], - viewonly=True, - ) + def __repr__(self): + return str(self.__class__) + ": " + str(self.__dict__) + + def __str__(self): + return str(self.__class__) + ": " + str(self.__dict__) - observers = DB.relationship( + #id_base_visit = db.Column(db.Integer, primary_key=True) + cor_visit_grid = db.relationship( + VisitGrid, + primaryjoin=(VisitGrid.id_base_visit == TBaseVisits.id_base_visit), + foreign_keys=[VisitGrid.id_base_visit], + ) + cor_visit_perturbation = db.relationship(VisitPerturbation, lazy="joined") + observers = db.relationship( "User", secondary=corVisitObserver, primaryjoin=(corVisitObserver.c.id_base_visit == TBaseVisits.id_base_visit), @@ -116,22 +117,22 @@ class TVisiteSFT(TBaseVisits): @serializable @geoserializable @shapeserializable -class ExportVisits(DB.Model): +class VisitsExport(MonitoringFloraTerritory): __tablename__ = "export_visits" __table_args__ = {"schema": "pr_monitoring_flora_territory"} - id_area = DB.Column(DB.Integer, primary_key=True) - id_base_visit = DB.Column(DB.Integer, primary_key=True) - id_base_site = DB.Column(DB.Integer) - uuid_base_visit = DB.Column(UUID(as_uuid=True)) - visit_date_min = DB.Column(DB.DateTime) - comments = DB.Column(DB.Unicode) - geom = DB.Column(Geometry("GEOMETRY", 2154)) - presence = DB.Column(DB.Boolean) - label_perturbation = DB.Column(DB.Unicode) - observateurs = DB.Column(DB.Unicode) - organisme = DB.Column(DB.Unicode) - base_site_name = DB.Column(DB.Unicode) - nom_valide = DB.Column(DB.Unicode) - cd_nom = DB.Column(DB.Integer) - area_name = DB.Column(DB.Unicode) - id_type = DB.Column(DB.Integer) + id_area = db.Column(db.Integer, primary_key=True) + id_base_visit = db.Column(db.Integer, primary_key=True) + id_base_site = db.Column(db.Integer) + uuid_base_visit = db.Column(UUID(as_uuid=True)) + visit_date_min = db.Column(db.DateTime) + comments = db.Column(db.Unicode) + geom = db.Column(Geometry("GEOMETRY", 2154)) + presence = db.Column(db.Boolean) + label_perturbation = db.Column(db.Unicode) + observateurs = db.Column(db.Unicode) + organisme = db.Column(db.Unicode) + base_site_name = db.Column(db.Unicode) + nom_valide = db.Column(db.Unicode) + cd_nom = db.Column(db.Integer) + area_name = db.Column(db.Unicode) + id_type = db.Column(db.Integer) diff --git a/config/conf_gn_module.sample.toml b/config/conf_gn_module.sample.toml index 8592870..8d225fd 100644 --- a/config/conf_gn_module.sample.toml +++ b/config/conf_gn_module.sample.toml @@ -15,9 +15,6 @@ id_dataset = 1 # Par défaut, id pour l'entrée avec type_name="Mailles25*25m" et type_code="M25m" # Sauf si modifié via les paramètre "meshes_code" et "meshes_name" du fichier config/settings.ini id_type_maille = 33 -# Valeur du champ `id_type` dans la table `ref_geo.bib_areas_types`. -# Par défaut, id pour l'entrée avec type_name="Communes" et type_code="COM" -id_type_commune = 25 # Identifiant de la liste d'utilisateurs réalisant des visites pour SFT. # Valeur du champ `id_liste` dans la table `utilisateurs.t_listes`. # Devrait correspondre au paramètre "observers_list_id" du fichier config/settings.ini diff --git a/frontend/app/detail-visit/detail-visit.component.html b/frontend/app/detail-visit/detail-visit.component.html index 3d5fb8b..548aee7 100644 --- a/frontend/app/detail-visit/detail-visit.component.html +++ b/frontend/app/detail-visit/detail-visit.component.html @@ -2,7 +2,7 @@
- +
@@ -26,12 +26,12 @@

  • Espèce prospectée : - +
  • Observateur(s) : - {{ observers }} + {{ observersDisplay }}
  • @@ -41,7 +41,7 @@

  • Perturbation(s) : - {{ per }} + {{ perturbationsDisplay }}
  • diff --git a/frontend/app/detail-visit/detail-visit.component.ts b/frontend/app/detail-visit/detail-visit.component.ts index 63c153b..7d746c4 100644 --- a/frontend/app/detail-visit/detail-visit.component.ts +++ b/frontend/app/detail-visit/detail-visit.component.ts @@ -2,7 +2,6 @@ import { Component, OnInit, ViewChild, AfterViewInit, TemplateRef } from '@angul import { Router, ActivatedRoute } from '@angular/router'; import { MapService } from '@geonature_common/map/map.service'; -import { DataFormService } from '@geonature_common/form/data-form.service'; import { GeojsonComponent } from '@geonature_common/map/geojson/geojson.component'; import { StoreService } from '../services/store.service'; @@ -16,19 +15,17 @@ import { ObserversService } from '../services/observers.service'; styleUrls: ['./detail-visit.component.scss'], }) export class DetailVisitComponent implements OnInit, AfterViewInit { - public zps; - public taxonName; - public date; public idVisit; public idSite; - public tabPertur = []; + public sciname; + public date; + public perturbationsDisplay: string = ''; public visitGrid = []; - public observers = ''; - + public observersDisplay: string = ''; public rows = []; - public dataListVisit = []; public comments; + public meshes; @ViewChild('geojson') geojson: GeojsonComponent; @@ -36,117 +33,134 @@ export class DetailVisitComponent implements OnInit, AfterViewInit { observersCellTpl: TemplateRef; constructor( - public mapService: MapService, - private _api: DataService, public activatedRoute: ActivatedRoute, - public storeService: StoreService, + private api: DataService, + public mapService: MapService, + private observersService: ObserversService, public router: Router, - public dataFormService: DataFormService, - private observersService: ObserversService + public storeService: StoreService ) {} ngOnInit() { - this.idVisit = this.activatedRoute.snapshot.params['idVisit']; + this.activatedRoute.params.subscribe(params => { + this.idSite = params.idSite; + this.idVisit = params.idVisit; + + this.initializeQueryString(); + this.initializeDatatableCols(); + this.loadSite(); + this.loadVisit(); + this.loadOthersVisits(); + }); } - ngAfterViewInit() { - this.mapService.map.doubleClickZoom.disable(); + private initializeQueryString() { + this.storeService.queryString = this.storeService.queryString.set( + 'id_base_visit', + this.idVisit + ); + } - this.activatedRoute.params.subscribe(params => { - this.storeService.queryString = this.storeService.queryString.set( - 'id_base_visit', - params.idVisit - ); - this._api.getOneVisit(params.idVisit).subscribe(element => { - this.comments = element.comments; - this.visitGrid = element.cor_visit_grid; - this.storeService.presence = 0; - this.storeService.absence = 0; - if (this.visitGrid !== undefined) { - this.visitGrid.forEach(grid => { - if (grid.presence == true) { - this.storeService.presence += 1; - } else { - this.storeService.absence += 1; - } - }); + private initializeDatatableCols() { + this.storeService.sftConfig.default_list_visit_columns.forEach(col => { + if (col.prop === 'observers') { + col.cellTemplate = this.observersCellTpl; + } + }); + } + + private loadSite() { + this.api.getInfoSite(this.idSite).subscribe(info => { + this.sciname = info.sciname.label; + }); + } + + private loadVisit() { + this.api.getOneVisit(this.idVisit).subscribe(visit => { + this.date = visit.visit_date_min; + this.idSite = visit.id_base_site; + this.comments = visit.comments; + this.visitGrid = visit.cor_visit_grid; + this.countGridTypes(); + this.buildPertubationsDisplay(visit.cor_visit_perturbation); + this.buildObserversDisplay(visit.observers); + }); + } + + private countGridTypes() { + this.storeService.presence = 0; + this.storeService.absence = 0; + if (this.visitGrid !== undefined) { + this.visitGrid.forEach(grid => { + if (grid.presence == true) { + this.storeService.presence += 1; + } else { + this.storeService.absence += 1; } + }); + } + } + + private buildPertubationsDisplay(visitPerturbations) { + let perturbationsLabels = visitPerturbations.map( + visitPerturbation => visitPerturbation.nomenclature.label_default + ); + this.perturbationsDisplay = perturbationsLabels.join(', ') + '.'; + } + + private buildObserversDisplay(observers) { + this.observersDisplay = this.observersService + .initialize() + .addObservers(observers) + .getObserversFull(); + } - let tabVisitPerturb = element.cor_visit_perturbation; - this.tabPertur = []; - if (tabVisitPerturb !== undefined) { - tabVisitPerturb.forEach(per => { - let typePer; - if (per == tabVisitPerturb[tabVisitPerturb.length - 1]) { - typePer = per.label_fr + '. '; + private loadOthersVisits() { + this.api.getVisits({ id_base_site: this.idSite }).subscribe(data => { + data.forEach(visit => { + visit.observers = this.observersService + .initialize() + .addObservers(visit.observers) + .getObserversAbbr(); + visit.observersFull = this.observersService.getObserversFull(); + + let pres = 0; + let abs = 0; + if (visit.cor_visit_grid !== undefined) { + visit.cor_visit_grid.forEach(maille => { + if (maille.presence) { + pres += 1; } else { - typePer = per.label_fr + ', '; + abs += 1; } - this.tabPertur.push(typePer); }); } + visit.state = pres + 'P / ' + abs + 'A '; + }); - this.observers = this.observersService - .initialize() - .addObservers(element.observers) - .getObserversFull(); - - this.date = element.visit_date_min; - this.idSite = element.id_base_site; - - this._api - .getMaille(this.idSite, { - id_area_type: this.storeService.sftConfig.id_type_maille, - }) - .subscribe(data => { - this.zps = data; - this.storeService.total = data.features.length; - this.storeService.getMailleNoVisit(); - this.geojson.currentGeoJson$.subscribe(currentLayer => { - this.mapService.map.fitBounds(currentLayer.getBounds()); - }); - }); - - this._api.getInfoSite(this.idSite).subscribe(info => { - this.taxonName = info.sciname.label; - }); - - this.storeService.sftConfig.default_list_visit_columns.forEach(col => { - if (col.prop === 'observers') { - col.cellTemplate = this.observersCellTpl; - } - }); + this.dataListVisit = data; - this._api.getVisits({ id_base_site: this.idSite }).subscribe(donnee => { - donnee.forEach(visit => { - visit.observers = this.observersService - .initialize() - .addObservers(visit.observers) - .getObserversAbbr(); - visit.observersFull = this.observersService.getObserversFull(); - - let pres = 0; - let abs = 0; - if (visit.cor_visit_grid !== undefined) { - visit.cor_visit_grid.forEach(maille => { - if (maille.presence) { - pres += 1; - } else { - abs += 1; - } - }); - } - visit.state = pres + 'P / ' + abs + 'A '; - }); + this.rows = this.dataListVisit.filter(data => { + return data.id_base_visit.toString() !== this.idVisit; + }); + }); + } - this.dataListVisit = donnee; + ngAfterViewInit() { + this.mapService.map.doubleClickZoom.disable(); - this.rows = this.dataListVisit.filter(dataa => { - return dataa.id_base_visit.toString() !== params.idVisit; - }); + this.api + .getMaille(this.idSite, { + id_area_type: this.storeService.sftConfig.id_type_maille, + }) + .subscribe(data => { + this.meshes = data; + this.storeService.total = data.features.length; + this.storeService.getMailleNoVisit(); + this.geojson.currentGeoJson$.subscribe(currentLayer => { + this.mapService.map.fitBounds(currentLayer.getBounds()); }); }); - }); } onEachFeature(feature, layer) { diff --git a/frontend/app/form-visit/form-visit.component.html b/frontend/app/form-visit/form-visit.component.html index 86e39f6..5689ebd 100644 --- a/frontend/app/form-visit/form-visit.component.html +++ b/frontend/app/form-visit/form-visit.component.html @@ -32,7 +32,7 @@
    Espèce prospectée : - {{ nomTaxon }} +
    diff --git a/frontend/app/form-visit/form-visit.component.ts b/frontend/app/form-visit/form-visit.component.ts index 7921dee..4dc453e 100644 --- a/frontend/app/form-visit/form-visit.component.ts +++ b/frontend/app/form-visit/form-visit.component.ts @@ -1,4 +1,5 @@ import { Component, OnInit, AfterViewInit, ViewChild } from '@angular/core'; +import { FormGroup, FormBuilder, Validators } from '@angular/forms'; import { Router, ActivatedRoute } from '@angular/router'; import { NgbDateParserFormatter } from '@ng-bootstrap/ng-bootstrap'; @@ -7,11 +8,9 @@ import { ToastrService } from 'ngx-toastr'; import { CommonService } from '@geonature_common/service/common.service'; import { MapService } from '@geonature_common/map/map.service'; import { GeojsonComponent } from '@geonature_common/map/geojson/geojson.component'; -import { DataFormService } from '@geonature_common/form/data-form.service'; import { DataService } from '../services/data.service'; import { StoreService } from '../services/store.service'; -import { FormService } from '../services/form.service'; import { ModuleConfig } from '../module.config'; @Component({ @@ -21,9 +20,8 @@ import { ModuleConfig } from '../module.config'; }) export class FormVisitComponent implements OnInit, AfterViewInit { public zps; - public modifGrid; - public nomTaxon; + public sciname; public date; public idVisit; public idSite; @@ -38,36 +36,33 @@ export class FormVisitComponent implements OnInit, AfterViewInit { geojson: GeojsonComponent; constructor( - public mapService: MapService, - public _api: DataService, + public api: DataService, public activatedRoute: ActivatedRoute, - public storeService: StoreService, - public router: Router, - public dataFormService: DataFormService, public dateParser: NgbDateParserFormatter, + public mapService: MapService, + public router: Router, + public storeService: StoreService, private toastr: ToastrService, - private _commonService: CommonService, - public formService: FormService + private commonService: CommonService, + public formBuilder: FormBuilder ) {} ngOnInit() { this.idSite = this.activatedRoute.snapshot.params['idSite']; this.idVisit = this.activatedRoute.snapshot.params['idVisit']; - // Get Taxon name - this._api.getInfoSite(this.idSite).subscribe(info => { - this.dataFormService.getTaxonInfo(info.cd_nom).subscribe(taxon => { - this.nomTaxon = taxon.nom_valide; - }); + // Get Taxon name from site + this.api.getInfoSite(this.idSite).subscribe(info => { + this.sciname = info.sciname.label; }); // Initialize - this.modifGrid = this.formService.initFormSFT(); + this.modifGrid = this.initializeVisitForm(); this.storeService.initialize(); // Check if is an update or an insert if (this.idVisit !== undefined) { - this._api.getOneVisit(this.idVisit).subscribe(element => { + this.api.getOneVisit(this.idVisit).subscribe(element => { if (element.cor_visit_grid !== undefined) { this.visitGrid = element.cor_visit_grid; } @@ -86,6 +81,7 @@ export class FormVisitComponent implements OnInit, AfterViewInit { // Date this.date = element.visit_date_min; + console.log("Perturbations:", element.cor_visit_perturbation) // Update data binded object this.modifGrid.patchValue({ id_base_site: this.idSite, @@ -93,7 +89,9 @@ export class FormVisitComponent implements OnInit, AfterViewInit { visit_date_min: this.dateParser.parse(this.date), visit_date_max: this.dateParser.parse(this.date), cor_visit_observer: element.observers, - cor_visit_perturbation: element.cor_visit_perturbation, + cor_visit_perturbation: element.cor_visit_perturbation.map( + visitPerturbation => visitPerturbation.nomenclature + ), cor_visit_grid: this.visitGrid, comments: element.comments, }); @@ -102,7 +100,7 @@ export class FormVisitComponent implements OnInit, AfterViewInit { this.visitGrid = []; } - this._api + this.api .getMaille(this.idSite, { id_area_type: ModuleConfig.id_type_maille }) .subscribe(data => { this.zps = data; @@ -114,6 +112,20 @@ export class FormVisitComponent implements OnInit, AfterViewInit { }); } + private initializeVisitForm(): FormGroup { + const formSuivi = this.formBuilder.group({ + id_base_site: null, + id_base_visit: null, + visit_date_min: [null, Validators.required], + visit_date_max: null, + cor_visit_observer: [null, Validators.required], + cor_visit_perturbation: new Array(), + cor_visit_grid: new Array(), + comments: null, + }); + return formSuivi; + } + ngAfterViewInit() { this.mapService.map.doubleClickZoom.disable(); } @@ -184,7 +196,7 @@ export class FormVisitComponent implements OnInit, AfterViewInit { // display help toaster for filelayer displayFileLayerInfoMessage() { if (this.firstFileLayerMessage) { - this._commonService.translateToaster('info', 'Map.FileLayerInfoSynthese'); + this.commonService.translateToaster('info', 'Map.FileLayerInfoSynthese'); } this.firstFileLayerMessage = false; } @@ -239,7 +251,7 @@ export class FormVisitComponent implements OnInit, AfterViewInit { formModif['comments'] = this.modifGrid.controls.comments.value; - this._api.postVisit(formModif).subscribe( + this.api.postVisit(formModif).subscribe( data => { this.toastr.success('Visite enregistrée', '', { positionClass: 'toast-top-center', @@ -259,10 +271,10 @@ export class FormVisitComponent implements OnInit, AfterViewInit { } ); } else { - this._commonService.translateToaster('error', 'NotAllowed'); + this.commonService.translateToaster('error', 'NotAllowed'); } } else { - this._commonService.translateToaster('error', 'ErrorMessage'); + this.commonService.translateToaster('error', 'ErrorMessage'); } } ); diff --git a/frontend/app/gnModule.module.ts b/frontend/app/gnModule.module.ts index 67a64ba..7600434 100644 --- a/frontend/app/gnModule.module.ts +++ b/frontend/app/gnModule.module.ts @@ -8,7 +8,6 @@ import { GN2CommonModule } from '@geonature_common/GN2Common.module'; import { DataService } from './services/data.service'; import { StoreService } from './services/store.service'; -import { FormService } from './services/form.service'; import { BreadcrumbsComponent } from './components/breadcrumbs/breadcrumbs.component'; import { ZpMapListComponent } from './zp-map-list/zp-map-list.component'; import { ListVisitComponent } from './list-visit/list-visit.component'; @@ -111,7 +110,7 @@ const routes: Routes = [ RouterModule.forChild(routes), CommonModule, ], - providers: [HttpClient, DataService, StoreService, FormService], + providers: [HttpClient, DataService, StoreService], bootstrap: [], }) export class GeonatureModule {} diff --git a/frontend/app/list-visit/list-visit.component.html b/frontend/app/list-visit/list-visit.component.html index 0381186..4959285 100644 --- a/frontend/app/list-visit/list-visit.component.html +++ b/frontend/app/list-visit/list-visit.component.html @@ -53,7 +53,10 @@

    Commune : - {{ site?.municipalities }} + + {{ site?.municipalities }} + + inconnue

    diff --git a/frontend/app/services/form.service.ts b/frontend/app/services/form.service.ts deleted file mode 100644 index c16746b..0000000 --- a/frontend/app/services/form.service.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Injectable } from '@angular/core'; -import { FormGroup, FormBuilder, Validators } from '@angular/forms'; - -@Injectable() -export class FormService { - public disabled = true; - - constructor(private _fb: FormBuilder) {} - - initFormSFT(): FormGroup { - const formSuivi = this._fb.group({ - id_base_site: null, - id_base_visit: null, - visit_date_min: [null, Validators.required], - visit_date_max: null, - cor_visit_observer: [null, Validators.required], - cor_visit_perturbation: new Array(), - cor_visit_grid: new Array(), - comments: null, - }); - return formSuivi; - } -} diff --git a/frontend/app/services/store.service.ts b/frontend/app/services/store.service.ts index c33cb88..e6685ea 100644 --- a/frontend/app/services/store.service.ts +++ b/frontend/app/services/store.service.ts @@ -15,7 +15,7 @@ export class StoreService { public absence; public total; public rest; - public urlLoad = `${AppConfig.API_ENDPOINT}/${ModuleConfig.MODULE_URL}/export_visit`; + public urlLoad = `${AppConfig.API_ENDPOINT}/${ModuleConfig.MODULE_URL}/visits/export`; public queryString = new HttpParams(); constructor(private _modalService: NgbModal) {