Skip to content

Commit

Permalink
Feat/dynamic form/site (#42)
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: todo comments about refactoring code

Add todo comment in order to don't forget what
is to improve inside the code

* chore(front): add comment and remove console.log

* chore(api): put back old formatting

* chore(api): remove useless comment

* refactor(api): move and rename create_update fct

* fix: put back inventor/digitiser & fix typo

* chore: remove useless files

Since columns are now loaded from json, they are not needed
anymore in separate typescript files

* chore: remove definition from sites_group.json

* fix: column problem on sites

* chore: remove useless comment

* refactor(front): remove comment and use rxjs

* chore(front): remove useless file

Since class BtnMultiSelectChipClass is not used anymore

* chore(front): remove useless comments/imports

* style(front): applied prettier

* chore(front): remove useless code

* refactor(front): regroup types into an interface

* style(front): applied prettier

* refactor(front): inherit config-json from config

---------

Co-authored-by: Andria Capai <[email protected]>
  • Loading branch information
mvergez and andriacap committed Aug 31, 2023
1 parent a1e0469 commit 093ca41
Show file tree
Hide file tree
Showing 45 changed files with 1,265 additions and 615 deletions.
41 changes: 12 additions & 29 deletions backend/gn_module_monitoring/config/generic/site.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
"base_site_name",
"base_site_code",
"base_site_description",
"id_nomenclature_type_site",
"id_inventor",
"first_use_date",
"last_visit",
Expand All @@ -36,11 +35,6 @@
"attribut_label": "Id site",
"hidden": true
},
"id_module": {
"type_widget": "text",
"attribut_label": "ID Module",
"hidden": true
},
"base_site_code": {
"type_widget": "text",
"attribut_label": "Code",
Expand All @@ -55,29 +49,6 @@
"type_widget": "textarea",
"attribut_label": "Description"
},

"id_sites_group": {
"type_widget": "datalist",
"attribut_label": "Groupe de sites",
"type_util": "sites_group",
"keyValue": "id_sites_group",
"keyLabel": "sites_group_name",
"api": "__MONITORINGS_PATH/list/__MODULE.MODULE_CODE/sites_group?id_module=__MODULE.ID_MODULE&fields=id_sites_group&fields=sites_group_name",
"application": "GeoNature",
"required": false,
"hidden": true
},
"id_nomenclature_type_site": {
"type_widget": "datalist",
"attribut_label": "Type site",
"api": "nomenclatures/nomenclature/TYPE_SITE",
"application": "GeoNature",
"keyValue": "id_nomenclature",
"keyLabel": "label_fr",
"data_path": "values",
"type_util": "nomenclature",
"required": true
},
"id_inventor": {
"type_widget": "datalist",
"attribut_label": "Descripteur",
Expand Down Expand Up @@ -122,6 +93,18 @@
"altitude_max": {
"type_widget": "integer",
"attribut_label": "Altitude (max)"
},
"id_sites_group": {
"type_widget": "integer",
"attribut_label": "ID Sites Groups",
"hidden": true,
"schema_dot_table": "gn_monitoring.t_base_sites"
},
"types_site": {
"type_widget": "datalist",
"attribut_label": "ID Type sites",
"hidden": true,
"schema_dot_table": "gn_monitoring.t_base_sites"
}
}
}
5 changes: 5 additions & 0 deletions backend/gn_module_monitoring/config/generic/sites_group.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@
},
"nb_visits": {
"attribut_label": "Nombre de visites"
},
"medias": {
"type_widget": "medias",
"attribut_label": "Médias",
"schema_dot_table": "gn_monitoring.t_sites_groups"
}
}
}
Expand Down
47 changes: 30 additions & 17 deletions backend/gn_module_monitoring/config/repositories.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,21 @@
config_cache_name = "MONITORINGS_CONFIG"


def get_config_objects(module_code, config, tree=None, parent_type=None):
"""
recupere la config de chaque object present dans tree pour le module <module_code>
"""
def get_config_objects(module_code, config, tree=None, parent_type=None, customSpecConfig=None):
'''
recupere la config de chaque object present dans tree pour le module <module_code>
'''
if not tree:
# initial tree
tree = config["tree"]

for object_type in tree:
# config object
if not object_type in config:
config[object_type] = config_object_from_files(module_code, object_type)
if (object_type=="site"):
config[object_type] = config_object_from_files(module_code, object_type,customSpecConfig)
else:
config[object_type] = config_object_from_files(module_code, object_type)

# tree
children_types = tree[object_type] and list(tree[object_type].keys()) or []
Expand Down Expand Up @@ -77,25 +80,25 @@ def get_config_objects(module_code, config, tree=None, parent_type=None):

# recursif
if tree[object_type]:
get_config_objects(module_code, config, tree[object_type], object_type)
get_config_objects(module_code, config, tree[object_type], object_type,customSpecConfig)


def config_object_from_files(module_code, object_type):
"""
recupere la configuration d'un object de type <object_type> pour le module <module_code>
"""
generic_config_object = json_config_from_file("generic", object_type)
specific_config_object = (
{} if module_code == "generic" else json_config_from_file(module_code, object_type)
)
def config_object_from_files(module_code, object_type,custom=None):
'''
recupere la configuration d'un object de type <object_type> pour le module <module_code>
'''
generic_config_object = json_config_from_file('generic', object_type)
specific_config_object = {} if module_code == 'generic' else json_config_from_file(module_code, object_type)

if module_code == 'generic' and object_type == 'site' and custom is not None:
specific_config_object = custom
config_object = generic_config_object
config_object.update(specific_config_object)

return config_object


def get_config(module_code=None, force=False):
def get_config(module_code=None, force=False,customSpecConfig=None):
"""
recupere la configuration pour le module monitoring
Expand Down Expand Up @@ -133,8 +136,8 @@ def get_config(module_code=None, force=False):
# if config and config.get('last_modif', 0) >= last_modif:
# return config

config = config_from_files("config", module_code)
get_config_objects(module_code, config)
config = config_from_files('config', module_code)
get_config_objects(module_code, config,customSpecConfig=customSpecConfig)

# customize config
if module:
Expand Down Expand Up @@ -243,3 +246,13 @@ def config_schema(module_code, object_type, type_schema="all"):
def get_config_frontend(module_code=None, force=True):
config = dict(get_config(module_code, force))
return config



# def get_config_from_backend(module_code=None, force=False):

# module_code = 'generic'
# #TODO: voir la sortie de cette fonction
# config = config_from_backend('config', module_code)
# #TODO: voir également à quoi sert cette fonction
# get_config_objects(module_code, config)
2 changes: 1 addition & 1 deletion backend/gn_module_monitoring/monitoring/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ def MonitoringModel(self, object_type):


monitoring_definitions = MonitoringDefinitions()

monitoring_g_definitions = MonitoringDefinitions()

class MonitoringObjectBase:
_object_type = None
Expand Down
25 changes: 18 additions & 7 deletions backend/gn_module_monitoring/monitoring/definitions.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
)
from .objects import MonitoringModule, MonitoringSite

from .base import monitoring_definitions
from .base import monitoring_definitions, monitoring_g_definitions
from .repositories import MonitoringObject
from .geom import MonitoringObjectGeom

Expand All @@ -20,12 +20,12 @@
"""

MonitoringModels_dict = {
"module": TMonitoringModules,
"site": TMonitoringSites,
"visit": TMonitoringVisits,
"observation": TMonitoringObservations,
"observation_detail": TMonitoringObservationDetails,
"sites_group": TMonitoringSitesGroups,
'module': TMonitoringModules,
'site': TMonitoringSites,
'visit': TMonitoringVisits,
'observation': TMonitoringObservations,
'observation_detail': TMonitoringObservationDetails,
'sites_group': TMonitoringSitesGroups,
}

MonitoringObjects_dict = {
Expand Down Expand Up @@ -56,3 +56,14 @@


monitoring_definitions.set(MonitoringObjects_dict, MonitoringModels_dict)

# #####################""
MonitoringModelsG_dict = {
x: MonitoringModels_dict[x] for x in MonitoringModels_dict if x not in "module"
}

MonitoringObjectsG_dict = {
x: MonitoringObjects_dict[x] for x in MonitoringObjects_dict if x not in "module"
}

monitoring_g_definitions.set(MonitoringObjectsG_dict, MonitoringModelsG_dict)
13 changes: 8 additions & 5 deletions backend/gn_module_monitoring/monitoring/objects.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,12 @@ class MonitoringSite(MonitoringObjectGeom):
"""

def preprocess_data(self, data):
module_ids = [module.id_module for module in self._model.modules]
id_module = int(data["id_module"])
if id_module not in module_ids:
module_ids.append(id_module)
type_site_ids = [type_site.id_nomenclature_type_site for type_site in self._model.types_site]
if len(data['types_site']) >0 :
for id_type_site in data['types_site']:
if int(id_type_site) not in type_site_ids:
type_site_ids.append(id_type_site)
#TODO: A enlever une fois qu'on aura enelever le champ "id_nomenclature_type_site" du model et de la bdd
data["id_nomenclature_type_site"]=data["types_site"][0]

data["modules"] = module_ids
data['types_site'] = type_site_ids
2 changes: 1 addition & 1 deletion backend/gn_module_monitoring/monitoring/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class Meta:
exclude = ("geom_geojson",)
load_instance = True

medias = MA.Nested(MediaSchema)
medias = MA.Nested(MediaSchema,many=True)
pk = fields.Method("set_pk",dump_only=True)
geometry = fields.Method("serialize_geojson", dump_only=True)

Expand Down
30 changes: 22 additions & 8 deletions backend/gn_module_monitoring/monitoring/serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,32 @@
import datetime
import uuid
from flask import current_app
from .base import MonitoringObjectBase, monitoring_definitions
from .base import MonitoringObjectBase, monitoring_definitions, monitoring_g_definitions
from ..utils.utils import to_int
from ..routes.data_utils import id_field_name_dict
from geonature.utils.env import DB
from geonature.core.gn_permissions.tools import get_scopes_by_action


class MonitoringObjectSerializer(MonitoringObjectBase):


def get_parent(self):
monitoring_def = monitoring_g_definitions if self._module_code == "generic" else monitoring_definitions
parent_type = self.parent_type()
if not parent_type:
return

if not self._parent:
self._parent = monitoring_definitions.monitoring_object_instance(
self._module_code, parent_type, self.id_parent()
).get()
self._parent = (
monitoring_def
.monitoring_object_instance(
self._module_code,
parent_type,
self.id_parent()
)
.get()
)

return self._parent

Expand Down Expand Up @@ -57,7 +66,8 @@ def unflatten_specific_properties(self, properties):
properties["data"] = data

def serialize_children(self, depth):
children_types = self.config_param("children_types")
monitoring_def = monitoring_g_definitions if self._module_code == "generic" else monitoring_definitions
children_types = self.config_param('children_types')

if not children_types:
return
Expand All @@ -74,8 +84,9 @@ def serialize_children(self, depth):
children_of_type = []

for child_model in getattr(self._model, relation_name):
child = monitoring_definitions.monitoring_object_instance(
self._module_code, children_type, model=child_model
child = (
monitoring_def
.monitoring_object_instance(self._module_code, children_type, model=child_model)
)
children_of_type.append(child.serialize(depth))

Expand Down Expand Up @@ -169,7 +180,10 @@ def populate(self, post_data):
self.preprocess_data(properties)

# ajout des données en base
if hasattr(self._model, "from_geofeature"):
if hasattr(self._model, 'from_geofeature'):
for key in list(post_data):
if key not in ("properties","geometry","type"):
post_data.pop(key)
self._model.from_geofeature(post_data, True)
else:
self._model.from_dict(properties, True)
41 changes: 40 additions & 1 deletion backend/gn_module_monitoring/routes/site.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
from werkzeug.datastructures import MultiDict

from gn_module_monitoring.blueprint import blueprint
from gn_module_monitoring.monitoring.models import BibTypeSite, TMonitoringSites
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.utils.routes import (
create_or_update_object_api_sites_sites_group,
filter_params,
geojson_query,
get_limit_page,
Expand Down Expand Up @@ -34,6 +36,30 @@ def get_types_site():
)


@blueprint.route("/sites/types/label", methods=["GET"])
def get_types_site_by_label():
params = MultiDict(request.args)
limit, page = get_limit_page(params=params)
sort_label, sort_dir = get_sort(
params=params, default_sort="label_fr", default_direction="desc"
)
joinquery = BibTypeSite.query.join(BibTypeSite.nomenclature).filter(
TNomenclatures.label_fr.ilike(f"%{params['label_fr']}%")
)
if sort_dir == "asc":
joinquery = joinquery.order_by(TNomenclatures.label_fr.asc())

# See if there are not too much labels since they are used
# in select in the frontend side. And an infinite select is not
# implemented
return paginate(
query=joinquery,
schema=BibTypeSiteSchema,
limit=limit,
page=page,
)


@blueprint.route("/sites/types/<int:id_type_site>", methods=["GET"])
def get_type_site_by_id(id_type_site):
res = BibTypeSite.find_by_id(id_type_site)
Expand Down Expand Up @@ -83,3 +109,16 @@ def get_all_site_geometries():
def get_module_sites(module_code: str):
# TODO: load with site_categories.json API
return jsonify({"module_code": module_code})


@blueprint.route("/sites", methods=["POST"])
def post_sites():
module_code = "generic"
object_type = "site"
customConfig = dict()
post_data = dict(request.get_json())
for keys in post_data["dataComplement"].keys():
if "config" in post_data["dataComplement"][keys]:
customConfig.update(post_data["dataComplement"][keys]["config"])
get_config(module_code, force=True, customSpecConfig=customConfig)
return create_or_update_object_api_sites_sites_group(module_code, object_type), 201
Loading

0 comments on commit 093ca41

Please sign in to comment.