From 23350af2daa95a87728ba025f04447736aebb7f5 Mon Sep 17 00:00:00 2001 From: Andria Capai Date: Tue, 11 Apr 2023 21:19:53 +0200 Subject: [PATCH] feat: get back work visitcomponent get back work from feat/visit to use visitcomponent but adapted to current branch [Refs_ticket]: #5 , #6 --- .../gn_module_monitoring/monitoring/models.py | 15 ++- .../monitoring/schemas.py | 17 ++- backend/gn_module_monitoring/routes/visit.py | 35 ++++++ .../tests/fixtures/visit.py | 27 +++++ frontend/app/class/monitoring-visit.ts | 6 + .../monitoring-sites-edit.component.css | 0 .../monitoring-sites-edit.component.html | 13 +++ .../monitoring-sites-edit.component.ts | 110 ++++++++++++++++++ .../monitoring-visits.component.css | 0 .../monitoring-visits.component.html | 8 ++ .../monitoring-visits.component.ts | 85 ++++++++++++++ frontend/app/enum/endpoints.ts | 1 + frontend/app/gnModule.module.ts | 19 ++- frontend/app/interfaces/objObs.ts | 6 +- frontend/app/interfaces/object.ts | 20 ++++ frontend/app/interfaces/visit.ts | 18 +++ frontend/app/services/api-geom.service.ts | 73 +++++++++++- 17 files changed, 440 insertions(+), 13 deletions(-) create mode 100644 backend/gn_module_monitoring/routes/visit.py create mode 100644 backend/gn_module_monitoring/tests/fixtures/visit.py create mode 100644 frontend/app/class/monitoring-visit.ts create mode 100644 frontend/app/components/monitoring-sites-edit/monitoring-sites-edit.component.css create mode 100644 frontend/app/components/monitoring-sites-edit/monitoring-sites-edit.component.html create mode 100644 frontend/app/components/monitoring-sites-edit/monitoring-sites-edit.component.ts create mode 100644 frontend/app/components/monitoring-visits/monitoring-visits.component.css create mode 100644 frontend/app/components/monitoring-visits/monitoring-visits.component.html create mode 100644 frontend/app/components/monitoring-visits/monitoring-visits.component.ts create mode 100644 frontend/app/interfaces/object.ts create mode 100644 frontend/app/interfaces/visit.ts diff --git a/backend/gn_module_monitoring/monitoring/models.py b/backend/gn_module_monitoring/monitoring/models.py index 84bd8067d..a04751e12 100644 --- a/backend/gn_module_monitoring/monitoring/models.py +++ b/backend/gn_module_monitoring/monitoring/models.py @@ -179,13 +179,13 @@ class TMonitoringObservations(TObservations): @serializable -class TMonitoringVisits(TBaseVisits): +class TMonitoringVisits(TBaseVisits, GenericModel): __tablename__ = "t_visit_complements" __table_args__ = {"schema": "gn_monitoring"} __mapper_args__ = { 'polymorphic_identity': 'monitoring_visit', } - + query_class = MonitoringQuery id_base_visit = DB.Column( DB.ForeignKey('gn_monitoring.t_base_visits.id_base_visit'), nullable=False, @@ -220,8 +220,17 @@ class TMonitoringVisits(TBaseVisits): ) + module = DB.relationship( + TModules, + lazy="select", + primaryjoin=(TModules.id_module == TBaseVisits.id_module), + foreign_keys=[TModules.id_module], + uselist=False, + ) + + @geoserializable(geoCol="geom", idCol="id_base_site") -class TMonitoringSites(TBaseSites): +class TMonitoringSites(TBaseSites, GenericModel): __tablename__ = 't_site_complements' __table_args__ = {'schema': 'gn_monitoring'} diff --git a/backend/gn_module_monitoring/monitoring/schemas.py b/backend/gn_module_monitoring/monitoring/schemas.py index fcbbc76d3..7dde989c3 100644 --- a/backend/gn_module_monitoring/monitoring/schemas.py +++ b/backend/gn_module_monitoring/monitoring/schemas.py @@ -3,12 +3,13 @@ import geojson from geonature.utils.env import MA from marshmallow import Schema, fields, validate -from geonature.core.gn_commons.schemas import MediaSchema +from geonature.core.gn_commons.schemas import MediaSchema, ModuleSchema from gn_module_monitoring.monitoring.models import ( BibTypeSite, TMonitoringSites, TMonitoringSitesGroups, + TMonitoringVisits ) @@ -51,11 +52,14 @@ class Meta: exclude = ("geom_geojson", "geom") geometry = fields.Method("serialize_geojson", dump_only=True) + pk = fields.Method("set_pk",dump_only=True) def serialize_geojson(self, obj): if obj.geom is not None: return geojson.dumps(obj.as_geofeature().get("geometry")) - + + def set_pk(self,obj): + return self.Meta.model.get_id() class BibTypeSiteSchema(MA.SQLAlchemyAutoSchema): label = fields.Method("get_label_from_type_site") @@ -69,3 +73,12 @@ class Meta: model = BibTypeSite include_fk = True load_instance = True + +class MonitoringVisitsSchema(MA.SQLAlchemyAutoSchema): + class Meta: + model = TMonitoringVisits + pk = fields.Method("set_pk",dump_only=True) + module = MA.Nested(ModuleSchema) + + def set_pk(self,obj): + return self.Meta.model.get_id() diff --git a/backend/gn_module_monitoring/routes/visit.py b/backend/gn_module_monitoring/routes/visit.py new file mode 100644 index 000000000..4303ba51e --- /dev/null +++ b/backend/gn_module_monitoring/routes/visit.py @@ -0,0 +1,35 @@ +from flask import request +from sqlalchemy.orm import joinedload +from werkzeug.datastructures import MultiDict + +from gn_module_monitoring.blueprint import blueprint +from gn_module_monitoring.monitoring.models import TMonitoringVisits +from gn_module_monitoring.monitoring.schemas import MonitoringVisitsSchema +from gn_module_monitoring.utils.routes import ( + filter_params, + get_limit_page, + get_sort, + paginate, + sort, +) + +# Retrieves visits that do not depend on modules + + +@blueprint.route("/visits", methods=["GET"]) +def get_visits(): + params = MultiDict(request.args) + limit, page = get_limit_page(params=params) + sort_label, sort_dir = get_sort( + params=params, default_sort="id_base_visit", default_direction="desc" + ) + query = TMonitoringVisits.query.options(joinedload(TMonitoringVisits.module)) + query = filter_params(query=TMonitoringVisits.query, params=params) + query = sort(query=query, sort=sort_label, sort_dir=sort_dir) + + return paginate( + query=query, + schema=MonitoringVisitsSchema, + limit=limit, + page=page, + ) diff --git a/backend/gn_module_monitoring/tests/fixtures/visit.py b/backend/gn_module_monitoring/tests/fixtures/visit.py new file mode 100644 index 000000000..cd7dc197d --- /dev/null +++ b/backend/gn_module_monitoring/tests/fixtures/visit.py @@ -0,0 +1,27 @@ +import datetime + +import pytest +from geonature.tests.fixtures import datasets +from geonature.utils.env import db + +from gn_module_monitoring.monitoring.models import TMonitoringVisits + + +@pytest.fixture +def visits(module, users, types_site, sites, datasets): + now = datetime.datetime.now() + dataset = datasets["orphan_dataset"] + db_visits = [] + for site in sites.values(): + db_visits.append( + TMonitoringVisits( + id_base_site=site.id_base_site, + id_module=module.id_module, + id_dataset=dataset.id_dataset, + visit_date_min=now, + ) + ) + with db.session.begin_nested(): + db.session.add_all(db_visits) + + return db_visits diff --git a/frontend/app/class/monitoring-visit.ts b/frontend/app/class/monitoring-visit.ts new file mode 100644 index 000000000..5f34dc629 --- /dev/null +++ b/frontend/app/class/monitoring-visit.ts @@ -0,0 +1,6 @@ +export enum columnNameVisit { + id_module = "Protocol ID", + visit_date_max = "Date max", + visit_date_min = "Date min", + nb_observations = "Nb. observations", +} diff --git a/frontend/app/components/monitoring-sites-edit/monitoring-sites-edit.component.css b/frontend/app/components/monitoring-sites-edit/monitoring-sites-edit.component.css new file mode 100644 index 000000000..e69de29bb diff --git a/frontend/app/components/monitoring-sites-edit/monitoring-sites-edit.component.html b/frontend/app/components/monitoring-sites-edit/monitoring-sites-edit.component.html new file mode 100644 index 000000000..cc95c530c --- /dev/null +++ b/frontend/app/components/monitoring-sites-edit/monitoring-sites-edit.component.html @@ -0,0 +1,13 @@ + +
+ +
diff --git a/frontend/app/components/monitoring-sites-edit/monitoring-sites-edit.component.ts b/frontend/app/components/monitoring-sites-edit/monitoring-sites-edit.component.ts new file mode 100644 index 000000000..5182cde7e --- /dev/null +++ b/frontend/app/components/monitoring-sites-edit/monitoring-sites-edit.component.ts @@ -0,0 +1,110 @@ +import { Component, OnInit, ViewChild } from "@angular/core"; +import { ActivatedRoute, Router } from "@angular/router"; +import { FormService } from "../../services/form.service"; +import { FormGroup, FormBuilder } from "@angular/forms"; +import { ISite } from "../../interfaces/geom"; +import { SitesService } from "../../services/api-geom.service"; +import { Observable, forkJoin, of } from "rxjs"; +import { concatMap, map, mergeMap } from "rxjs/operators"; +import { IobjObs, ObjDataType } from "../../interfaces/objObs"; +import { MonitoringFormComponentG } from "../monitoring-form-g/monitoring-form.component-g"; +import { ObjectService } from "../../services/object.service"; +import { JsonData } from "../../types/jsondata"; +import { endPoints } from "../../enum/endpoints"; + +@Component({ + selector: "monitoring-sites-edit", + templateUrl: "./monitoring-sites-edit.component.html", + styleUrls: ["./monitoring-sites-edit.component.css"], +}) +export class MonitoringSitesEditComponent implements OnInit { + site: ISite; + form: FormGroup; + paramToFilt: string = "label"; + funcToFilt: Function; + titleBtn: string = "Choix des types de sites"; + placeholderText: string = "Sélectionnez les types de site"; + id_sites_group:number; + types_site:string[]; + @ViewChild("subscritionObjConfig") + monitoringFormComponentG: MonitoringFormComponentG; + objToCreate: IobjObs; + + constructor( + private _formService: FormService, + private _formBuilder: FormBuilder, + private siteService: SitesService, + private _Activatedroute: ActivatedRoute, + private _objService:ObjectService + ) {} + + ngOnInit() { + + // let $obs1 = this._objService.currentObjSelected + // let $obs2 = this._objService.currentObjectType + // forkJoin([$obs1,$obs2]).subscribe( results =>{ + // console.log(results[0]) + // let objParent = results[0] + // let objChild = results[1] + // this.id_sites_group = objParent.id_sites_group + // this._formService.dataToCreate({ module: "generic", objectType: "site", id_sites_group : this.id_sites_group, id_relationship: ['id_sites_group','types_site'],endPoint:endPoints.sites,objSelected:objChild.objectType}); + // this.form = this._formBuilder.group({}); + // this.funcToFilt = this.partialfuncToFilt.bind(this); + // } + // ) + + + + this._objService.currentObjSelected.subscribe((objParent) => { + this.id_sites_group = objParent.id_sites_group + this._formService.dataToCreate({ module: "generic", objectType: "site", id_sites_group : this.id_sites_group, id_relationship: ['id_sites_group','types_site'],endPoint:endPoints.sites,objSelected:objParent.objectType}); + this.form = this._formBuilder.group({}); + this.funcToFilt = this.partialfuncToFilt.bind(this); + }) + + // this._Activatedroute.params + // .pipe( + // map((params) => params["id"] as number)) + // .subscribe( + // (id_site_group) => { + // console.log(id_site_group) + // this.id_sites_group = id_site_group + // this._formService.dataToCreate({ module: "generic", objectType: "site", id_sites_group : this.id_sites_group }); + // this.form = this._formBuilder.group({}); + // this.funcToFilt = this.partialfuncToFilt.bind(this); + // } + // ); + + } + + + + partialfuncToFilt( + pageNumber: number, + limit: number, + valueToFilter: string + ): Observable { + return this.siteService.getTypeSites(pageNumber, limit, { + label_fr: valueToFilter, + sort_dir: "desc", + }); + } + + onSendConfig(config: JsonData): void { + config = this.addTypeSiteListIds(config) + this.monitoringFormComponentG.getConfigFromBtnSelect(config); + } + + addTypeSiteListIds(config:JsonData):JsonData{ + if (config && config.length !=0){ + config["types_site"]=[] + for (const key in config ){ + if ('id_nomenclature_type_site' in config[key]) { + config["types_site"].push(config[key]['id_nomenclature_type_site']); + } + } + + } + return config + } +} diff --git a/frontend/app/components/monitoring-visits/monitoring-visits.component.css b/frontend/app/components/monitoring-visits/monitoring-visits.component.css new file mode 100644 index 000000000..e69de29bb diff --git a/frontend/app/components/monitoring-visits/monitoring-visits.component.html b/frontend/app/components/monitoring-visits/monitoring-visits.component.html new file mode 100644 index 000000000..51afd0bfe --- /dev/null +++ b/frontend/app/components/monitoring-visits/monitoring-visits.component.html @@ -0,0 +1,8 @@ + +
+ + + +
\ No newline at end of file diff --git a/frontend/app/components/monitoring-visits/monitoring-visits.component.ts b/frontend/app/components/monitoring-visits/monitoring-visits.component.ts new file mode 100644 index 000000000..6c83ea51e --- /dev/null +++ b/frontend/app/components/monitoring-visits/monitoring-visits.component.ts @@ -0,0 +1,85 @@ +import { Component, OnInit,Input } from "@angular/core"; +import { forkJoin } from "rxjs"; +import { map, mergeMap } from "rxjs/operators"; +import { Router, ActivatedRoute } from "@angular/router"; +import { GeoJSONService } from "../../services/geojson.service"; +import { MonitoringGeomComponent } from "../../class/monitoring-geom-component"; +import { SitesService, VisitsService } from "../../services/api-geom.service"; +import { ISite } from "../../interfaces/geom"; +import { IVisit } from "../../interfaces/visit"; +import { IPage, IPaginated } from "../../interfaces/page"; +import { columnNameVisit } from "../../class/monitoring-visit"; +import { JsonData } from "../../types/jsondata"; +import { ObjDataType } from "../../interfaces/objObs"; + +@Component({ + selector: "monitoring-visits", + templateUrl: "./monitoring-visits.component.html", + styleUrls: ["./monitoring-visits.component.css"], +}) +export class MonitoringVisitsComponent + extends MonitoringGeomComponent + implements OnInit +{ + site: ISite; + @Input() visits: IVisit[]; + @Input() page: IPage; + // colsname: typeof columnNameVisit = columnNameVisit; + objectType: string; + bEdit: boolean; + @Input() colsname; + + constructor( + private _sites_service: SitesService, + private _visits_service: VisitsService, + public geojsonService: GeoJSONService, + private router: Router, + private _Activatedroute: ActivatedRoute + ) { + super(); + this.getAllItemsCallback = this.getVisits; + this.objectType = "sites"; + } + + ngOnInit() { + this._Activatedroute.params + .pipe( + map((params) => params["id"] as number), + mergeMap((id: number) => + forkJoin({ + site: this._sites_service.getById(id), + visits: this._visits_service.get(1, this.limit, { + id_base_site: id, + }), + }) + ) + ) + .subscribe((data: { site: ISite; visits: IPaginated }) => { + this.site = data.site; + this.setVisits(data.visits); + this.baseFilters = { id_base_site: this.site.id_base_site }; + }); + } + + getVisits(page: number, filters: JsonData) { + this._visits_service + .get(page, this.limit, filters) + .subscribe((visits:IPaginated) => this.setVisits(visits)); + } + + setVisits(visits) { + this.visits = visits.items; + this.page = { + page: visits.page - 1, + count: visits.count, + limit: visits.limit, + }; + this.colsname = this._visits_service.objectObs.dataTable.colNameObj; + } + + seeDetails($event) { + this.router.navigate([ + `monitorings/object/${$event.module.module_code}/visit/${$event.id_base_visit}`, + ]); + } +} diff --git a/frontend/app/enum/endpoints.ts b/frontend/app/enum/endpoints.ts index fc7f634af..1c278d69b 100644 --- a/frontend/app/enum/endpoints.ts +++ b/frontend/app/enum/endpoints.ts @@ -2,4 +2,5 @@ export enum endPoints { sites_groups = "sites_groups", sites = "sites", + visits = "visits", } \ No newline at end of file diff --git a/frontend/app/gnModule.module.ts b/frontend/app/gnModule.module.ts index 72231de32..81b3fb32d 100644 --- a/frontend/app/gnModule.module.ts +++ b/frontend/app/gnModule.module.ts @@ -47,10 +47,13 @@ import { SitesGroupService, SitesService, ApiGeomService, + VisitsService, } from "./services/api-geom.service"; import { MonitoringSitesGroupsCreateComponent } from "./components/monitoring-sitesgroups-create/monitoring-sitesgroups-create.component"; import { MonitoringSitesCreateComponent } from "./components/monitoring-sites-create/monitoring-sites-create.component"; import { BtnSelectComponent } from "./components/btn-select/btn-select.component"; +import { MonitoringSitesEditComponent } from "./components/monitoring-sites-edit/monitoring-sites-edit.component"; +import { MonitoringVisitsComponent } from "./components/monitoring-visits/monitoring-visits.component"; // my module routing const routes: Routes = [ @@ -92,6 +95,17 @@ const routes: Routes = [ path: "create", component: MonitoringSitesCreateComponent, }, + { + path: "sites/:id", + component: MonitoringVisitsComponent, + children: [ + { + path: "edit", + component: MonitoringSitesEditComponent, + }, + ] + }, + ], }, ], @@ -118,7 +132,9 @@ const routes: Routes = [ MonitoringFormComponentG, MonitoringSitesGroupsCreateComponent, MonitoringSitesCreateComponent, - BtnSelectComponent + MonitoringSitesEditComponent, + BtnSelectComponent, + MonitoringVisitsComponent ], imports: [ GN2CommonModule, @@ -151,6 +167,7 @@ const routes: Routes = [ FormService, ObjectService, ApiGeomService, + VisitsService ], bootstrap: [ModulesComponent], schemas: [ diff --git a/frontend/app/interfaces/objObs.ts b/frontend/app/interfaces/objObs.ts index e836a9cd7..01f59d3e2 100644 --- a/frontend/app/interfaces/objObs.ts +++ b/frontend/app/interfaces/objObs.ts @@ -1,13 +1,13 @@ import { endPoints } from "../enum/endpoints"; import { JsonData } from "../types/jsondata"; import { ISite, ISitesGroup } from "./geom"; - -export type ObjDataType = ISite | ISitesGroup | JsonData; +import { IVisit } from "./visit"; +export type ObjDataType = ISite | ISitesGroup | IVisit | JsonData ; export interface IobjObs { properties: ObjDataType; endPoint: endPoints; - objectType: "site" | "sites_group"; + objectType: "site" | "sites_group" | "visits"; label: string; addObjLabel: string; editObjLabel: string; diff --git a/frontend/app/interfaces/object.ts b/frontend/app/interfaces/object.ts new file mode 100644 index 000000000..3e52f3935 --- /dev/null +++ b/frontend/app/interfaces/object.ts @@ -0,0 +1,20 @@ +import { JsonData } from "../types/jsondata"; +import { IPaginated } from "./page"; +import { GeoJSON } from "geojson"; +import { Observable } from "rxjs"; +import { Resp } from "../types/response"; + +export interface IObject { + data: JsonData; +} + +export interface IService { + get(limit: number, page: number, params: JsonData): Observable>; + create(postdata: T): Observable; + patch(id: number, updatedData: T): Observable; + // delete(obj: IGeomObject) +} + +export interface IGeomService extends IService { + get_geometries(params: JsonData): Observable; +} diff --git a/frontend/app/interfaces/visit.ts b/frontend/app/interfaces/visit.ts new file mode 100644 index 000000000..0404ee6e8 --- /dev/null +++ b/frontend/app/interfaces/visit.ts @@ -0,0 +1,18 @@ +import { JsonData } from "../types/jsondata"; +import { IGeomObject } from "./geom"; + +export interface IVisit extends IGeomObject { + pk:number; + comments: string; + data: JsonData; + id_base_visit: number; + id_module: number; + id_nomenclature_grp_typ: number; + id_nomenclature_tech_collect_campanule: number; + meta_create_date: Date; + meta_update_date: Date; + nb_observations: number; + uuid_base_visit: string; + visit_date_max: Date; + visit_date_min: Date; +} diff --git a/frontend/app/services/api-geom.service.ts b/frontend/app/services/api-geom.service.ts index 9db1c29a1..18b0b2638 100644 --- a/frontend/app/services/api-geom.service.ts +++ b/frontend/app/services/api-geom.service.ts @@ -10,6 +10,7 @@ import { Resp } from '../types/response'; import { Utils } from '../utils/utils'; import { CacheService } from './cache.service'; import { ConfigJsonService } from './config-json.service'; +import { IVisit } from '../interfaces/visit'; @Injectable() export class ApiGeomService implements IGeomService { @@ -50,8 +51,8 @@ export class ApiGeomService implements IGeomService { page: number = 1, limit: number = 10, params: JsonData = {} - ): Observable> { - return this._cacheService.request>>( + ): Observable> { + return this._cacheService.request>>( 'get', this.endPoint, { @@ -60,7 +61,7 @@ export class ApiGeomService implements IGeomService { ); } - getById(id: number): Observable { + getById(id: number): Observable { return this._cacheService.request>( 'get', `${this.endPoint}/${id}` @@ -77,7 +78,7 @@ export class ApiGeomService implements IGeomService { ); } - patch(id: number, updatedData: { properties: ISitesGroup | ISite }): Observable { + patch(id: number, updatedData: { properties: ISitesGroup | ISite | IVisit }): Observable { return this._cacheService.request('patch', `${this.endPoint}/${id}`, { postData: updatedData, }); @@ -258,3 +259,67 @@ export class SitesService extends ApiGeomService { return 'le site'; } } + +@Injectable() +export class VisitsService extends ApiGeomService { + constructor(_cacheService: CacheService, _configJsonService: ConfigJsonService) { + super(_cacheService, _configJsonService); + } + init(): void { + this.endPoint = endPoints.visits; + this.objectObs = { + properties: {}, + endPoint: endPoints.visits, + objectType: 'visits', + label: 'groupe de site', + addObjLabel: 'Ajouter une nouvelle visite', + editObjLabel: 'Editer la visite', + addChildLabel: 'Ajouter une observation', + id: null, + moduleCode: 'generic', + schema: {}, + template: { + fieldNames: [], + fieldLabels: {}, + fieldNamesList: [], + fieldDefinitions: {}, + }, + dataTable: { colNameObj: {} }, + }; + this._configJsonService + .init(this.objectObs.moduleCode) + .pipe() + .subscribe(() => { + const fieldNames = this._configJsonService.configModuleObjectParam( + this.objectObs.moduleCode, + this.objectObs.objectType, + 'display_properties' + ); + const fieldNamesList = this._configJsonService.configModuleObjectParam( + this.objectObs.moduleCode, + this.objectObs.objectType, + 'display_list' + ); + const schema = this._configJsonService.schema( + this.objectObs.moduleCode, + this.objectObs.objectType + ); + const fieldLabels = this._configJsonService.fieldLabels(schema); + const fieldDefinitions = this._configJsonService.fieldDefinitions(schema); + this.objectObs.template.fieldNames = fieldNames; + this.objectObs.template.fieldNamesList = fieldNamesList; + this.objectObs.schema = schema; + this.objectObs.template.fieldLabels = fieldLabels; + this.objectObs.template.fieldDefinitions = fieldDefinitions; + this.objectObs.template.fieldNamesList = fieldNamesList; + this.objectObs.dataTable.colNameObj = Utils.toObject(fieldNamesList, fieldLabels); + }); + } + addObjectType(): string { + return " une nouvelle visite"; + } + + editObjectType(): string { + return "la visite"; + } +} \ No newline at end of file