Skip to content

Commit

Permalink
Feat/prepare edit site component & some visits (#43)
Browse files Browse the repository at this point in the history
* refactor: object.service with observable obj

Change the objectType string to objectType obj
in order to subscribe to multilple properties link to this object

Reviewed-by: andriacap
[Refs_ticket]: #40

* feat: adapt service to get config json

Get fieldsName and fieldsLabel for display properties
(properties component)

Add logic to setItem in LocalStorage to keepLast Value of observable on reload page

TODO:
- [ ]  see if Localstorage is really necessary with the configService used inside api-geom.service )
- [ ] adapt monitoring-form-g.component with the new service

Reviewed-by: andriacap
[Refs ticket]: #4

* feat: column datatable from config json

Add logic into sitegroups service in order to use config json to diplay
column datatable (called "display_list")

Review-by: andriac
[Refs ticket]: #4

* feat: adjust backend to load config json

Adjust backend code to use existing code in order to load config from
file json and by starting with "sites_group"

Fix Media load and upload for site_group

TODO:
- [ ] check if config should be find from file or backend
- [ ] Optimize logic backend (use generic model with class method ?)

Reviewed-by: andriacap
[Refs ticket]: #4

* feat: add button multiselect with filter on backend

filter with params backend (new route -> see if it's possible to change
that)

Add button multiselect above form type site

TODO:
- improve Input / condition of use case of btn multiselect

Reviewed-by: andriac

* feat: btn multiselect option

-Add @input fn , placeholder, title, paramsToFilt
-Remove empty option
-prevent adding from keyboard input or not including in list
-Store config json into object

Reviewed-by: @AndriaC
[Refs-ticket]: #4

* refactor: change form-service and object-service

refactor form-service
add observable into object-service (WIP: futur use for refresh page ?)

Rieviewed-by: andriac

[Refs_ticket]: #40

* refactor: test refresh page and comportment obs

refresh page seems to works with localstorage of different obj in
object_service

Reviewed-by: andriac

* feat: dynamic form

- Add:
- pass config json to form.component-g.ts
- add config json to this.obj and refresh form into form.component-g.ts
- add css for form component to deal when long form selected

-fix :
- refresh page form component (this._configService.init is necessary
  ...)
- comportment different between add or edit into form component and
  form service

Reviewed-by: andriac
[Refs_ticket]: #4

* feat: dynamic form - Correction PR

Remove unused console.log
Rxjs : use concatMap to avoid subscribe inside subscribe
Apply: prettier and sort prettier for import ts file

Reviewed-by: @AndriaC
[Refs_PR]: #42

* feat: dynamic create site

- Add change current-object when click "Add <object>" from datatable
  component
- api-geom.service: change the way to init the api-geom
- WIP:  two subscribes in one .. (the subscription is call only once ..)
- WIP : get config json from backend

Reviewed-by: andriac

* feat: add relationship id into sendData form site

Add type_site and id_site_group to the form site

Reviewed-by: andriac
[Refs_ticket]: #5 et #6

* feat(back): add custom config when post site

Change backend files in order to choose specific config when site is
created.

Reviewed-by: andriac
[Refs_ticket]: #5 , #6

* fix: problem of type object when loading form

Add endPoint and objectType to the observable using by the formservice
in order to use _apiGeomService with good context (endPoint)

[Refs_ticket]: #5 et #6

* fix: tooltip and label inside datatable-component

Add a childLabel inside interface objObs.ts
in order to use this tooltip inside datatable-component

* feat: get back work visitcomponent

get back work from feat/visit to use visitcomponent
but adapted to current branch

[Refs_ticket]: #5 , #6

* fix: forgot 3 modifications needed from feat/site

Add conftest visit
Add test_get_visits
Add get_site_by_id

* fix: passing data between components and property

- Action button "consult..." is working for group_site and site
- Action with button "cancel" , "come back history routing" send to the
  right component
- Label and tooltip are according to the parent and child object loaded
  in the corresponding component

- Action "Add site" directly from datatable-g.component (WIP: need to
  remove send to sites_group/:id/create

[Refs_ticket]: #40 , #4 , #5 and #6

* fix: error of url relative when using form

Fix problem of id_module has ["id_module"] in
create_or_update_object_api function
Fix path "sites/:id" to "site/:id"
Add urlRelative when editing component

* fix: problem route navigation

Remove unused "urlRelative" create in last commit
Fix the action "add children" in datatable to redirect to the good
component
Change the way to redirect when cancel and delete on form component
based on last url

[Refs_ticket]: #40

* refactor: object.service with observable obj

Change the objectType string to objectType obj
in order to subscribe to multilple properties link to this object

Reviewed-by: andriacap
[Refs_ticket]: #40

* feat: adapt service to get config json

Get fieldsName and fieldsLabel for display properties
(properties component)

Add logic to setItem in LocalStorage to keepLast Value of observable on reload page

TODO:
- [ ]  see if Localstorage is really necessary with the configService used inside api-geom.service )
- [ ] adapt monitoring-form-g.component with the new service

Reviewed-by: andriacap
[Refs ticket]: #4

* feat: column datatable from config json

Add logic into sitegroups service in order to use config json to diplay
column datatable (called "display_list")

Review-by: andriac
[Refs ticket]: #4

* feat: adjust backend to load config json

Adjust backend code to use existing code in order to load config from
file json and by starting with "sites_group"

Fix Media load and upload for site_group

TODO:
- [ ] check if config should be find from file or backend
- [ ] Optimize logic backend (use generic model with class method ?)

Reviewed-by: andriacap
[Refs ticket]: #4

* feat: add button multiselect with filter on backend

filter with params backend (new route -> see if it's possible to change
that)

Add button multiselect above form type site

TODO:
- improve Input / condition of use case of btn multiselect

Reviewed-by: andriac

* feat: btn multiselect option

-Add @input fn , placeholder, title, paramsToFilt
-Remove empty option
-prevent adding from keyboard input or not including in list
-Store config json into object

Reviewed-by: @AndriaC
[Refs-ticket]: #4

* refactor: change form-service and object-service

refactor form-service
add observable into object-service (WIP: futur use for refresh page ?)

Rieviewed-by: andriac

[Refs_ticket]: #40

* refactor: test refresh page and comportment obs

refresh page seems to works with localstorage of different obj in
object_service

Reviewed-by: andriac

* feat: dynamic form

- Add:
- pass config json to form.component-g.ts
- add config json to this.obj and refresh form into form.component-g.ts
- add css for form component to deal when long form selected

-fix :
- refresh page form component (this._configService.init is necessary
  ...)
- comportment different between add or edit into form component and
  form service

Reviewed-by: andriac
[Refs_ticket]: #4

* feat: dynamic form - Correction PR

Remove unused console.log
Rxjs : use concatMap to avoid subscribe inside subscribe
Apply: prettier and sort prettier for import ts file

Reviewed-by: @AndriaC
[Refs_PR]: #42

* feat: dynamic create site

- Add change current-object when click "Add <object>" from datatable
  component
- api-geom.service: change the way to init the api-geom
- WIP:  two subscribes in one .. (the subscription is call only once ..)
- WIP : get config json from backend

Reviewed-by: andriac

* feat: add relationship id into sendData form site

Add type_site and id_site_group to the form site

Reviewed-by: andriac
[Refs_ticket]: #5 et #6

* feat(back): add custom config when post site

Change backend files in order to choose specific config when site is
created.

Reviewed-by: andriac
[Refs_ticket]: #5 , #6

* fix: tooltip and label inside datatable-component

Add a childLabel inside interface objObs.ts
in order to use this tooltip inside datatable-component

* feat: get back work visitcomponent

get back work from feat/visit to use visitcomponent
but adapted to current branch

[Refs_ticket]: #5 , #6

* fix: forgot 3 modifications needed from feat/site

Add conftest visit
Add test_get_visits
Add get_site_by_id

* fix: passing data between components and property

- Action button "consult..." is working for group_site and site
- Action with button "cancel" , "come back history routing" send to the
  right component
- Label and tooltip are according to the parent and child object loaded
  in the corresponding component

- Action "Add site" directly from datatable-g.component (WIP: need to
  remove send to sites_group/:id/create

[Refs_ticket]: #40 , #4 , #5 and #6

* fix: error of url relative when using form

Fix problem of id_module has ["id_module"] in
create_or_update_object_api function
Fix path "sites/:id" to "site/:id"
Add urlRelative when editing component

* fix: fix things broken by rebase

* chore: remove useless file

* chore(front): remove file/comment/console.log

---------

Co-authored-by: Andria Capai <[email protected]>
  • Loading branch information
mvergez and andriacap committed Sep 7, 2023
1 parent 9d22a90 commit b2c7d55
Show file tree
Hide file tree
Showing 34 changed files with 660 additions and 127 deletions.
11 changes: 2 additions & 9 deletions backend/gn_module_monitoring/config/generic/site.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,6 @@
"type_util": "user",
"required": true
},
"id_digitiser": {
"type_widget": "text",
"attribut_label": "Numérisateur",
"required": true,
"hidden": true,
"type_util": "user"
},
"first_use_date": {
"type_widget": "date",
"attribut_label": "Date description",
Expand All @@ -90,11 +83,11 @@
"type_widget": "integer",
"attribut_label": "Altitude (min)"
},
"altitude_max": {
"altitude_max": {
"type_widget": "integer",
"attribut_label": "Altitude (max)"
},
"id_sites_group": {
"id_sites_group": {
"type_widget": "integer",
"attribut_label": "ID Sites Groups",
"hidden": true,
Expand Down
20 changes: 15 additions & 5 deletions backend/gn_module_monitoring/monitoring/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,13 +171,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,
Expand Down Expand Up @@ -210,10 +210,20 @@ 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):
__tablename__ = "t_site_complements"
__table_args__ = {"schema": "gn_monitoring"}
class TMonitoringSites(TBaseSites, GenericModel):

__tablename__ = 't_site_complements'
__table_args__ = {'schema': 'gn_monitoring'}
__mapper_args__ = {
"polymorphic_identity": "monitoring_site",
}
Expand Down
17 changes: 15 additions & 2 deletions backend/gn_module_monitoring/monitoring/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
)


Expand Down Expand Up @@ -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")
Expand All @@ -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()
6 changes: 6 additions & 0 deletions backend/gn_module_monitoring/routes/site.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from gn_module_monitoring.config.repositories import get_config
from gn_module_monitoring.monitoring.models import BibTypeSite, TMonitoringSites, TNomenclatures
from gn_module_monitoring.monitoring.schemas import BibTypeSiteSchema, MonitoringSitesSchema
from gn_module_monitoring.routes.sites_groups import create_or_update_object_api
from gn_module_monitoring.utils.routes import (
create_or_update_object_api_sites_sites_group,
filter_params,
Expand Down Expand Up @@ -85,6 +86,11 @@ def get_sites():
page=page,
)

@blueprint.route("/sites/<int:id_base_site>", methods=["GET"])
def get_site_by_id(id_base_site):
site = TMonitoringSites.query.get_or_404(id_base_site)
schema = MonitoringSitesSchema()
return schema.dump(site)

@blueprint.route("/sites/geometries", methods=["GET"])
def get_all_site_geometries():
Expand Down
44 changes: 44 additions & 0 deletions backend/gn_module_monitoring/routes/sites_groups.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,3 +103,47 @@ def handle_validation_error(error):
status_code=422,
payload=error.data,
).to_dict()


# TODO: OPTIMIZE in order to adapt to new monitoring module (entry by sites_groups)


def create_or_update_object_api(module_code, object_type, id=None):
"""
route pour la création ou la modification d'un objet
si id est renseigné, c'est une création (PATCH)
sinon c'est une modification (POST)
:param module_code: reference le module concerne
:param object_type: le type d'object (site, visit, obervation)
:param id : l'identifiant de l'object (de id_base_site pour site)
:type module_code: str
:type object_type: str
:type id: int
:return: renvoie l'object crée ou modifié
:rtype: dict
"""
depth = to_int(request.args.get("depth", 1))

# recupération des données post
post_data = dict(request.get_json())
if module_code != "generic":
module = get_module("module_code", module_code)
else:
module = {"id_module": "generic"}
#TODO : A enlever une fois que le post_data contiendra geometry et type depuis le front
if object_type == "site":
post_data["geometry"]={'type':'Point', 'coordinates':[2.5,50]}
post_data["type"]='Feature'
# on rajoute id_module s'il n'est pas renseigné par défaut ??
if "id_module" not in post_data["properties"]:
module["id_module"] = "generic"
post_data["properties"]["id_module"] = module["id_module"]
else:
post_data["properties"]["id_module"] = module["id_module"]

return (
monitoring_g_definitions.monitoring_object_instance(module_code, object_type, id)
.create_or_update(post_data)
.serialize(depth)
)
35 changes: 35 additions & 0 deletions backend/gn_module_monitoring/routes/visit.py
Original file line number Diff line number Diff line change
@@ -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,
)
1 change: 1 addition & 0 deletions backend/gn_module_monitoring/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@
"gn_module_monitoring.tests.fixtures.site",
"gn_module_monitoring.tests.fixtures.sites_groups",
"gn_module_monitoring.tests.fixtures.type_site",
"gn_module_monitoring.tests.fixtures.visit",
]
27 changes: 27 additions & 0 deletions backend/gn_module_monitoring/tests/fixtures/visit.py
Original file line number Diff line number Diff line change
@@ -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
29 changes: 29 additions & 0 deletions backend/gn_module_monitoring/tests/test_routes/test_visit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import pytest
from flask import url_for


@pytest.mark.usefixtures("client_class", "temporary_transaction")
class TestVisits:
def test_get_visits(self, visits):
r = self.client.get(
url_for(
"monitorings.get_visits",
)
)

expected_visits = {visit.id_base_visit for visit in visits}
current_visits = {visit["id_base_visit"] for visit in r.json["items"]}
assert expected_visits.issubset(current_visits)
assert all(visit["module"] is not None for visit in r.json["items"])

def test_get_visits_with_site(self, visits, sites):
site = list(sites.values())[0]

r = self.client.get(url_for("monitorings.get_visits", id_base_site=site.id_base_site))

expected_visits = {
visit.id_base_visit for visit in visits if visit.id_base_site == site.id_base_site
}
current_visits = {visit["id_base_visit"] for visit in r.json["items"]}

assert expected_visits.issubset(current_visits)
6 changes: 6 additions & 0 deletions frontend/app/class/monitoring-visit.ts
Original file line number Diff line number Diff line change
@@ -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",
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,15 @@
<a
class="nav-link link cell-link"
(click)="navigateToDetail(row)"
matTooltip="Consulter le groupe de site"
matTooltip="Consulter le {{ objectType.label }}"
>
<i class="fa fa-eye" aria-hidden="true"></i>
</a>
<!-- TODO Action Column : Changer ngIf, click function, matTooltip // voir comment adapter à ce qu'il y avait avant : *ngIf="child0.child0()"" et dans la function `child0.child0().labelArtUndef(true)` -->
<a
class="nav-link link cell-link"
(click)="navigateToAddChildren(null, row.id)"
matTooltip="Ajouter un site "
(click)="navigateToAddChildren(null, row)"
matTooltip=" {{objectType.addChildLabel}}"
>
<i class="fa fa-plus" aria-hidden="true"></i>
</a>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,12 +186,20 @@ export class MonitoringDatatableGComponent implements OnInit {
}
}
}
navigateToAddChildren(_, rowId) {
this.addEvent.emit(rowId);
navigateToAddChildren(_, row) {
this.addEvent.emit(row);
this._objService.changeObjectType(this.objectType);
this.router.navigate(['create'], {
relativeTo: this._Activatedroute,
});
if (row){
row['id'] = row[row.pk];
this.router.navigate([row.id,'create'], {
relativeTo: this._Activatedroute,
});
} else {
this.router.navigate(['create'], {
relativeTo: this._Activatedroute,
});
}

}
navigateToDetail(row) {
row['id'] = row.pk;
Expand Down
Loading

0 comments on commit b2c7d55

Please sign in to comment.