diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 000000000..98effff4b --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,26 @@ +name: Lint + +on: [push, pull_request] + +jobs: + backend: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: Backend code formatting check (Black) + uses: psf/black@stable + with: + src: "setup.py ./backend/gn_module_monitoring" + frontend: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: Frontend code formatting check (Prettier) + uses: creyD/prettier_action@v4.3 + with: + dry: True + prettier_options: --config frontend/.prettierrc --ignore-path frontend/.prettierignore --check frontend/**/*.ts diff --git a/README.md b/README.md index ae47a933e..184531a78 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,4 @@ -# Module GeoNature de suivis génériques - +# Module GeoNature générique de suivis ## Module générique de gestion des données de protocoles de type suivis @@ -70,6 +69,8 @@ Créer le dossier suivant dans le dossier `media` de GeoNature mkdir ~/geonature/backend/media/monitorings ``` +Il vous faut désormais attribuer des permissions aux groupes ou utilisateurs que vous souhaitez, pour qu'ils puissent accéder et utiliser le module (voir https://docs.geonature.fr/admin-manual.html#gestion-des-droits). Si besoin une commande permet d'attribuer automatiquement toutes les permissions dans tous les modules à un groupe ou utilisateur administrateur. + ### Installation d'un sous-module #### Récupérer le répertoire de configuration d'un sous-module de suivi @@ -84,16 +85,16 @@ source ~/geonature/backend/venv/bin/activate #### Copie du dossier de configuration -Copier le dossier du sous module dans le dossier `media` de geonature +Copier le dossier du sous-module dans le dossier `media` de GeoNature ```sh -cp -R ~/geonature/backend/media/monitorings/ +cp -R ~/geonature/backend/media/monitorings/ ``` -Si l'on souhaite développer un sous-module il peut parfois être plus pratique de faire un lien symbolique +PS : Si l'on souhaite développer un sous-module il peut être pratique de plutôt faire un lien symbolique vers le dossier du sous-module, plutôt que de le copier dans le dossier `media` de GeoNature : ```sh -ln -s ~/geonature/backend/monitorings/ +ln -s ~/geonature/backend/media/monitorings/ ``` #### Lancer la commande d'installation du sous-module @@ -102,7 +103,7 @@ ln -s ~/geonature/backend/monitorings/ geonature monitorings install ``` -Si le code du module n'est pas renseigné ou si le dossier du module n'existe pas, la commande va afficher la liste des modules installés et disponibles. +Si le code du sous-module n'est pas renseigné ou si le dossier du sous-module n'existe pas, la commande va afficher la liste des sous-modules installés et disponibles. ```sh geonature monitorings install @@ -124,6 +125,8 @@ Modules installés : - module2: Module 2 (Deuxième exemple de module) ``` +Il vous faut désormais attribuer des permissions aux groupes ou utilisateurs que vous souhaitez, pour qu'ils puissent accéder et utiliser le sous-module (voir https://docs.geonature.fr/admin-manual.html#gestion-des-droits). Si besoin une commande permet d'attribuer automatiquement toutes les permissions dans tous les modules à un groupe ou utilisateur administrateur. + ### Configurer le sous-module #### Dans le menu de droite de GeoNature, cliquer sur le module "Monitorings" @@ -132,16 +135,16 @@ Le sous-module installé précedemment doit s'afficher dans la liste des sous-mo #### Cliquez sur le sous-module -Vous êtes désormais sur la page du sous-module. Un message apparaît pour vous indiquer de configurer le module. +Vous êtes désormais sur la page du sous-module. Un message apparaît pour vous indiquer de configurer celui-ci. #### Cliquez sur le bouton `Éditer` -Le formulaire d'édition du module s'affiche et vous pouvez choisir les variables suivantes : +Le formulaire d'édition du sous-module s'affiche et vous pouvez choisir les variables suivantes : * Jeux de données *(obligatoire)* : * Un module peut concerner plusieurs jeux de données, le choix sera ensuite proposé au niveau de chaque visite. -* Liste des observateurs *(obligatoire)*: - * La liste d'observateurs définit l'ensemble de observateurs possible pour le module (et de descripteurs de site). +* Liste des observateurs *(obligatoire)* : + * La liste d'observateurs définit l'ensemble des observateurs possibles pour le module (et de descripteurs de site). * Cette liste peut être définie dans l'application `UsersHub`. * Liste des taxons *(obligatoire selon le module)* : * Cette liste définit l'ensemble des taxons concernés par ce module. Elle est gérée dans l'application `TaxHub`. @@ -152,7 +155,7 @@ Le formulaire d'édition du module s'affiche et vous pouvez choisir les variable * `lb_nom` : Nom latin, * `nom_vern,lb_nom` : Nom vernaculaire par defaut s'il existe, sinon nom latin. * Afficher dans le menu ? *(non obligatoire, non affiché par défaut)* : - * On peut décider que le sous-module soit accessible directement depuis le menu de droite de GeoNature. + * On peut décider que le sous-module soit accessible directement depuis le menu de gauche de GeoNature. * `active_frontend` * Options spécifiques du sous-module : * Un sous-module peut présenter des options qui lui sont propres et définies dans les paramètres spécifiques du sous-module. @@ -174,8 +177,8 @@ Les permissions ne sont implémentées que partiellement. La notion de portée ( La gestion des permissions pour les rôles (utilisateur ou groupe) se réalise au niveau de l'interface d'administration des permissions de GeoNature. -Il est possible de spéficier les permissions pour chaque type d'objet (groupes de sites, sites, visites et observations). +Il est possible de spécifier les permissions pour chaque type d'objet (groupes de sites, sites, visites et observations). -Si aucune permission n'est associée à l'objet, les permissions auront comme valeur celles associées aux sous-module qui lui-même hérite des permissions du module Monitoring qui lui-même hérite de GeoNature. +Si aucune permission n'est associée à l'objet, les permissions auront comme valeur celles associées au sous-module. -Par défaut, dès qu'un utilisateur a un droit supérieur à 0 pour une action (c-a-d aucune portée) il peut réaliser cette action. Il est possible de surcharger les paramètres au niveau des fichiers de configuration des objets du module. (cf configuration des sous-modules). +Par défaut, dès qu'un utilisateur a un droit supérieur à 0 pour une action (c-a-d aucune portée) il peut réaliser cette action. Il est possible de surcharger les paramètres au niveau des fichiers de configuration des objets du module. (cf doc de configuration des sous-modules). diff --git a/VERSION b/VERSION index 09a3acfa1..faef31a43 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.6.0 \ No newline at end of file +0.7.0 diff --git a/backend/gn_module_monitoring/__init__.py b/backend/gn_module_monitoring/__init__.py index c95e1aa67..b54844747 100644 --- a/backend/gn_module_monitoring/__init__.py +++ b/backend/gn_module_monitoring/__init__.py @@ -1,2 +1,2 @@ MODULE_CODE = "MONITORINGS" -MODULE_PICTO = "fa-eye" \ No newline at end of file +MODULE_PICTO = "fa-eye" diff --git a/backend/gn_module_monitoring/blueprint.py b/backend/gn_module_monitoring/blueprint.py index 13d92600c..2e56e3ce7 100644 --- a/backend/gn_module_monitoring/blueprint.py +++ b/backend/gn_module_monitoring/blueprint.py @@ -6,7 +6,9 @@ from flask import Blueprint, current_app from .command.cmd import commands -blueprint = Blueprint("monitorings", __name__, template_folder=current_app.config["MEDIA_FOLDER"] + '/monitorings') +blueprint = Blueprint( + "monitorings", __name__, template_folder=current_app.config["MEDIA_FOLDER"] + "/monitorings" +) from .routes import * # noqa blueprint.cli.short_help = "Commandes pour l" "administration du module MONITORINGS" diff --git a/backend/gn_module_monitoring/command/cmd.py b/backend/gn_module_monitoring/command/cmd.py index cd1ed30d5..5a5a82909 100644 --- a/backend/gn_module_monitoring/command/cmd.py +++ b/backend/gn_module_monitoring/command/cmd.py @@ -19,11 +19,11 @@ from .utils import ( process_export_csv, - insert_permission_object, + process_available_permissions, remove_monitoring_module, add_nomenclature, available_modules, - installed_modules + installed_modules, ) @@ -37,6 +37,7 @@ def cmd_process_all(module_code): # process export csv process_export_csv(module_code) + @click.command("process_export_csv") @click.argument("module_code", type=str, required=False, default="") @with_appcontext @@ -66,15 +67,22 @@ def cmd_install_monitoring_module(module_code): module_config_dir_path = monitoring_module_config_path(module_code) - if not (module_code and (module_config_dir_path / 'module.json').is_file()): + if not (module_code and (module_config_dir_path / "module.json").is_file()): if module_code: - click.secho(f"Le module {module_code} n'est pas présent dans le dossier {module_config_dir_path}", fg="red") - click.secho(f'\nModules disponibles :\n') + click.secho( + f"Le module {module_code} n'est pas présent dans le dossier {module_config_dir_path}", + fg="red", + ) + click.secho(f"\nModules disponibles :\n") for module in available_modules(): - click.secho(f"- {module['module_code']}: {module['module_label']} ({module['module_desc']})\n") + click.secho( + f"- {module['module_code']}: {module['module_label']} ({module['module_desc']})\n" + ) click.secho(f"\nModules installés :\n") for module in installed_modules(): - click.secho(f"- {module['module_code']}: {module['module_label']} ({module['module_desc']})\n") + click.secho( + f"- {module['module_code']}: {module['module_label']} ({module['module_desc']})\n" + ) return click.secho(f"Installation du sous-module monitoring {module_code}") @@ -96,10 +104,7 @@ def cmd_install_monitoring_module(module_code): config = get_config(module_code, force=True) if not config: - click.secho( - f"config directory for module {module_code} does not exist", - fg="red" - ) + click.secho(f"config directory for module {module_code} does not exist", fg="red") return None module_desc = config["module"].get("module_desc") @@ -112,7 +117,7 @@ def cmd_install_monitoring_module(module_code): click.secho( f"Veuillez renseigner les valeurs des champs module_label \ et module_desc dans le fichier {module_config_dir_path}/module.json", - fg="red" + fg="red", ) return @@ -120,12 +125,10 @@ def cmd_install_monitoring_module(module_code): "module_picto": "fa-puzzle-piece", **config["module"], "module_code": module_code, - "module_path": "{}/module/{}".format( - module_monitoring.module_path, module_code - ), + "module_path": "{}/module/{}".format(module_monitoring.module_path, module_code), "active_frontend": False, "active_backend": False, - "type": "monitoring_module" + "type": "monitoring_module", } click.secho("ajout du module {} en base".format(module_code)) @@ -134,10 +137,8 @@ def cmd_install_monitoring_module(module_code): DB.session.add(module) DB.session.commit() - # Insert permission object - if config["module"].get("permission_objects"): - id_module = module.id_module - insert_permission_object(id_module, config["module"].get("permission_objects")) + # Ajouter les permissions disponibles + process_available_permissions(module_code) #  run specific sql if (module_config_dir_path / "synthese.sql").exists: @@ -177,14 +178,14 @@ def cmd_install_monitoring_module(module_code): DB.session.commit() # TODO ++++ create specific tables - click.secho(f"Sous-module monitoring '{module_code}' installé", fg='green') + click.secho(f"Sous-module monitoring '{module_code}' installé", fg="green") return -@click.command("update_permission_objects") -@click.argument("module_code") +@click.command("update_module_available_permissions") +@click.argument("module_code", required=False, default="") @with_appcontext -def cmd_update_perm_module_cmd(module_code): +def cmd_process_available_permission_module(module_code): """ Mise à jour (uniquement insertion) des objets permissions associés au module Défini par le paramètre permission_objects du fichier module.json @@ -193,28 +194,12 @@ def cmd_update_perm_module_cmd(module_code): module_code ([string]): code du sous module """ - try: - module = get_module("module_code", module_code) - except Exception: - print("le module n'existe pas") - return - path_module = monitoring_module_config_path(module_code) / "module.json" - if not path_module.is_file(): - print(f"Il n'y a pas de fichier {path_module} pour ce module") - return - config_module = json_from_file(path_module, None) - if not config_module: - print("Il y a un problème avec le fichier {}".format(path_module)) - return + if module_code: + return process_available_permissions(module_code) - print(f"Insertion des objets de permissions pour le module {module_code}") - # Insert permission object - if "permission_objects" in config_module: - id_module = module.id_module - insert_permission_object(id_module, config_module["permission_objects"]) - else: - print("no permission") + for module in installed_modules(): + process_available_permissions(module["module_code"]) @click.command("remove") @@ -264,7 +249,7 @@ def synchronize_synthese(module_code, offset): commands = [ cmd_process_export_csv, cmd_install_monitoring_module, - cmd_update_perm_module_cmd, + cmd_process_available_permission_module, cmd_remove_monitoring_module_cmd, cmd_add_module_nomenclature_cli, cmd_process_all, diff --git a/backend/gn_module_monitoring/command/utils.py b/backend/gn_module_monitoring/command/utils.py index bbc64cac8..437cde8a9 100644 --- a/backend/gn_module_monitoring/command/utils.py +++ b/backend/gn_module_monitoring/command/utils.py @@ -5,149 +5,192 @@ from sqlalchemy.orm.exc import NoResultFound from geonature.utils.env import DB, BACKEND_DIR -from geonature.core.gn_permissions.models import TObjects +from geonature.core.gn_permissions.models import PermObject +from geonature.core.gn_commons.models import TModules from pypnnomenclature.models import TNomenclatures, BibNomenclaturesTypes -from ..config.utils import ( - json_from_file, - monitoring_module_config_path, - SUB_MODULE_CONFIG_DIR -) +from ..config.utils import json_from_file, monitoring_module_config_path, SUB_MODULE_CONFIG_DIR -from ..modules.repositories import get_module, get_source_by_code, get_modules +from ..config.repositories import get_config +from ..modules.repositories import get_module, get_source_by_code, get_modules -''' +""" utils.py fonctions pour les commandes du module monitoring -''' +""" + def process_for_all_module(process_func): - ''' - boucle sur les répertoire des module - et exécute la fonction pour chacun - (sauf generic) - ''' + """ + boucle sur les répertoire des module + et exécute la fonction pour chacun + (sauf generic) + """ for module in get_modules(): process_func(module.module_code) return +def getMonitoringPermissionObjectLabel_dict(): + return __import__( + "gn_module_monitoring" + ).monitoring.definitions.MonitoringPermissionObjectLabel_dict + def process_export_csv(module_code=None): - ''' - fonction qui va chercher les fichier sql de exports/csv et qui les joue - ''' + """ + fonction qui va chercher les fichier sql de exports/csv et qui les joue + """ if not module_code: - ''' - pour tous les modules - ''' + """ + pour tous les modules + """ return process_for_all_module(process_export_csv) - export_csv_dir = Path(monitoring_module_config_path(module_code)) / 'exports/csv' + export_csv_dir = Path(monitoring_module_config_path(module_code)) / "exports/csv" if not export_csv_dir.is_dir(): return for root, dirs, files in os.walk(export_csv_dir, followlinks=True): for f in files: - if not f.endswith('.sql'): + if not f.endswith(".sql"): continue try: DB.engine.execute( - text( - open(Path(root) / f, 'r') - .read() - ).execution_options(autocommit=True) + text(open(Path(root) / f, "r").read()) + .execution_options(autocommit=True) .bindparams(module_code=module_code) ) - print('{} - export csv file : {}'.format(module_code, f)) + print("{} - export csv file : {}".format(module_code, f)) except Exception as e: - print('{} - export csv erreur dans le script {} : {}'.format(module_code, f ,e)) + print("{} - export csv erreur dans le script {} : {}".format(module_code, f, e)) + + +def process_available_permissions(module_code): + try: + module = get_module("module_code", module_code) + except Exception: + print("le module n'existe pas") + return + + config = get_config(module_code, force=True) + if not config: + print(f"Il y a un problème de configuration pour le module {module_code}") + return + + insert_module_available_permissions(module_code, "ALL") + # Insert permission object + for permission_object_code in config.get("permission_objects", []): + insert_module_available_permissions(module_code, permission_object_code) -def insert_permission_object(id_module, permissions): - """ Insertion de l'association permission object - Args: - id_module ([type]): id du module - permissions ([type]): liste des permissions à associer au module +def insert_module_available_permissions(module_code, perm_object_code): + print( + f"process permissions for (module_code, perm_object)= ({module_code},{perm_object_code})" + ) - Raises: - e: [description] + object_label = getMonitoringPermissionObjectLabel_dict().get(perm_object_code) + + if not object_label: + print(f"L'object {perm_object_code} n'est pas traité") + + try: + module = TModules.query.filter_by(module_code=module_code).one() + except NoResultFound: + print("Le module {module_code} n'est pas présent") + return + + try: + perm_object = PermObject.query.filter_by(code_object=perm_object_code).one() + except NoResultFound: + print("L'object de permission {module_code} n'est pas présent") + return + + txt_cor_object_module = f""" + INSERT INTO gn_permissions.cor_object_module( + id_module, + id_object + ) + VALUES({module.id_module}, {perm_object.id_object}) + ON CONFLICT DO NOTHING """ - for perm in permissions: - print(f"Insert perm {perm}") - # load object - try: - object = DB.session.query(TObjects).filter(TObjects.code_object == perm).one() - # save - txt = (""" - INSERT INTO gn_permissions.cor_object_module (id_module, id_object) - VALUES ({id_module}, {id_object}) - """.format( - id_module=id_module, - id_object=object.id_object - ) - ) - try: - DB.engine.execution_options(autocommit=True).execute(txt) - print(f" Ok") - except IntegrityError: - DB.session.rollback() - print(f" Impossible d'insérer la permission {perm} pour des raisons d'intégrités") - except NoResultFound as e: - print(f" Permission {perm} does'nt exists") - except Exception as e: - print(f" Impossible d'insérer la permission {perm} :{e}") + DB.engine.execution_options(autocommit=True).execute(txt_cor_object_module) + + txt_perm_available = f""" + INSERT INTO gn_permissions.t_permissions_available ( + id_module, + id_object, + id_action, + label, + scope_filter) + SELECT + {module.id_module}, + {perm_object.id_object}, + a.id_action, + v.label, + true + FROM + ( VALUES + ('C', 'Créer des {object_label}'), + ('R', 'Voir les {object_label}'), + ('U', 'Modifier les {object_label}'), + ('D', 'Supprimer des {object_label}'), + ('E', 'Exporter les {object_label}') + ) AS v (action_code, label) + JOIN gn_permissions.bib_actions a ON v.action_code = a.code_action + ON CONFLICT DO NOTHING + """ + DB.engine.execution_options(autocommit=True).execute(txt_perm_available) def remove_monitoring_module(module_code): try: - module = get_module('module_code', module_code) + module = get_module("module_code", module_code) except Exception: print("le module n'existe pas") return # remove module in db try: - # HACK pour le moment suprresion avec un sql direct - # Car il y a un soucis de delete cascade dans les modèles sqlalchemy - txt = (""" - DELETE FROM gn_commons.t_modules WHERE id_module ={} - """.format( - module.id_module - ) - ) + # suppression des permissions disponibles pour ce module + txt = f"DELETE FROM gn_permissions.t_permissions_available WHERE id_module = {module.id_module}" + DB.engine.execution_options(autocommit=True).execute(txt) + # HACK pour le moment suppresion avec un sql direct + # Car il y a un soucis de delete cascade dans les modèles sqlalchemy + txt = f"""DELETE FROM gn_commons.t_modules WHERE id_module ={module.id_module}""" DB.engine.execution_options(autocommit=True).execute(txt) except IntegrityError: print("Impossible de supprimer le module car il y a des données associées") return - except Exception: + except Exception as e: print("Impossible de supprimer le module") - raise(e) + raise (e) # suppression source pour la synthese try: - print('Remove source {}'.format("MONITORING_" + module_code.upper())) + print("Remove source {}".format("MONITORING_" + module_code.upper())) source = get_source_by_code("MONITORING_" + module_code.upper()) DB.session.delete(source) DB.session.commit() except Exception as e: print("Impossible de supprimer la source {}".format(str(e))) - return + # return # run specific sql TODO # remove nomenclature TODO return + def add_nomenclature(module_code): - path_nomenclature = monitoring_module_config_path(module_code) / 'nomenclature.json' + path_nomenclature = monitoring_module_config_path(module_code) / "nomenclature.json" if not path_nomenclature.is_file(): print("Il n'y a pas de nomenclature à insérer pour ce module") @@ -155,17 +198,15 @@ def add_nomenclature(module_code): nomenclature = json_from_file(path_nomenclature, None) if not nomenclature: - print('Il y a un problème avec le fichier {}'.format(path_nomenclature)) + print("Il y a un problème avec le fichier {}".format(path_nomenclature)) return - for data in nomenclature.get('types', []): + for data in nomenclature.get("types", []): nomenclature_type = None try: nomenclature_type = ( DB.session.query(BibNomenclaturesTypes) - .filter( - data.get('mnemonique') == BibNomenclaturesTypes.mnemonique - ) + .filter(data.get("mnemonique") == BibNomenclaturesTypes.mnemonique) .one() ) @@ -173,31 +214,30 @@ def add_nomenclature(module_code): pass if nomenclature_type: - print('no insert type', nomenclature_type) + print("no insert type", nomenclature_type) continue - data['label_fr'] = data.get('label_fr') or data['label_default'] - data['definition_fr'] = data.get('definition_fr') or data['definition_default'] - data['source'] = data.get('source') or 'monitoring' - data['statut'] = data.get('statut') or 'Validation en cours' + data["label_fr"] = data.get("label_fr") or data["label_default"] + data["definition_fr"] = data.get("definition_fr") or data["definition_default"] + data["source"] = data.get("source") or "monitoring" + data["statut"] = data.get("statut") or "Validation en cours" nomenclature_type = BibNomenclaturesTypes(**data) DB.session.add(nomenclature_type) DB.session.commit() - for data in nomenclature['nomenclatures']: + for data in nomenclature["nomenclatures"]: nomenclature = None try: nomenclature = ( DB.session.query(TNomenclatures) .join( - BibNomenclaturesTypes, - BibNomenclaturesTypes.id_type == TNomenclatures.id_type + BibNomenclaturesTypes, BibNomenclaturesTypes.id_type == TNomenclatures.id_type ) .filter( and_( - data.get('cd_nomenclature') == TNomenclatures.cd_nomenclature, - data.get('type') == BibNomenclaturesTypes.mnemonique + data.get("cd_nomenclature") == TNomenclatures.cd_nomenclature, + data.get("type") == BibNomenclaturesTypes.mnemonique, ) ) .one() @@ -209,9 +249,8 @@ def add_nomenclature(module_code): if nomenclature: # TODO make update print( - 'nomenclature {} - {} already exist'.format( - nomenclature.cd_nomenclature, - nomenclature.label_default + "nomenclature {} - {} already exist".format( + nomenclature.cd_nomenclature, nomenclature.label_default ) ) continue @@ -221,45 +260,56 @@ def add_nomenclature(module_code): try: id_type = ( DB.session.query(BibNomenclaturesTypes.id_type) - .filter( - BibNomenclaturesTypes.mnemonique == data['type'] - ) + .filter(BibNomenclaturesTypes.mnemonique == data["type"]) .one() )[0] except Exception: pass if not id_type: - print('probleme de type avec mnemonique="{}" pour la nomenclature {}'.format(data['type'], data)) + print( + 'probleme de type avec mnemonique="{}" pour la nomenclature {}'.format( + data["type"], data + ) + ) continue - data['label_fr'] = data.get('label_fr') or data['label_default'] - data['definition_fr'] = data.get('definition_fr') or data['definition_default'] - data['source'] = data.get('source') or 'monitoring' - data['statut'] = data.get('statut') or 'Validation en cours' - data['active'] = True - data['id_type'] = id_type - data.pop('type') + data["label_fr"] = data.get("label_fr") or data["label_default"] + data["definition_fr"] = data.get("definition_fr") or data["definition_default"] + data["source"] = data.get("source") or "monitoring" + data["statut"] = data.get("statut") or "Validation en cours" + data["active"] = True + data["id_type"] = id_type + data.pop("type") nomenclature = TNomenclatures(**data) DB.session.add(nomenclature) DB.session.commit() + def installed_modules(): - return [ { 'module_code': module.module_code, 'module_label': module.module_label, 'module_desc': module.module_desc} for module in get_modules()] + return [ + { + "module_code": module.module_code, + "module_label": module.module_label, + "module_desc": module.module_desc, + } + for module in get_modules() + ] + def available_modules(): - ''' - renvoie la liste des modules disponibles non encore installés - ''' - installed_module_codes = list(map(lambda x: x['module_code'], installed_modules())) + """ + renvoie la liste des modules disponibles non encore installés + """ + installed_module_codes = list(map(lambda x: x["module_code"], installed_modules())) available_modules_ = [] for root, dirs, files in os.walk(SUB_MODULE_CONFIG_DIR, followlinks=True): for d in dirs: - module_file = Path(root) / d / 'module.json' + module_file = Path(root) / d / "module.json" if d in installed_module_codes or not module_file.exists(): continue module = json_from_file(module_file) - available_modules_.append({**module, 'module_code': d}) + available_modules_.append({**module, "module_code": d}) break - return available_modules_ \ No newline at end of file + return available_modules_ diff --git a/backend/gn_module_monitoring/conf_schema_toml.py b/backend/gn_module_monitoring/conf_schema_toml.py index e89328a69..2ab17632f 100644 --- a/backend/gn_module_monitoring/conf_schema_toml.py +++ b/backend/gn_module_monitoring/conf_schema_toml.py @@ -9,6 +9,8 @@ class GnModuleSchemaConf(Schema): pass + + # AREA_TYPE = fields.List(fields.String(), missing=["COM", "M1", "M5", "M10"]) # BORNE_OBS = fields.List(fields.Integer(), missing=[1, 20, 40, 60, 80, 100, 120]) # BORNE_TAXON = fields.List(fields.Integer(), missing=[1, 5, 10, 15]) diff --git a/backend/gn_module_monitoring/config/data_utils.py b/backend/gn_module_monitoring/config/data_utils.py index 8b4b20378..792ee525f 100644 --- a/backend/gn_module_monitoring/config/data_utils.py +++ b/backend/gn_module_monitoring/config/data_utils.py @@ -1,63 +1,49 @@ # from pypnusershub.db.models import User from apptax.taxonomie.models import Taxref, CorNomListe -from pypnnomenclature.models import ( - TNomenclatures, - BibNomenclaturesTypes -) +from pypnnomenclature.models import TNomenclatures, BibNomenclaturesTypes from geonature.utils.env import DB from sqlalchemy import and_ -from .utils import ( - config_from_files_customized -) +from .utils import config_from_files_customized def config_data(module_code): - - return config_from_files_customized('data', module_code) + return config_from_files_customized("data", module_code) def get_data_utils(module_code): return { - 'nomenclature': get_nomenclature(module_code), - 'taxonomy': get_taxonomy(module_code), - 'users': {} + "nomenclature": get_nomenclature(module_code), + "taxonomy": get_taxonomy(module_code), + "users": {}, } def get_nomenclature(module_code): - nomenclature_types = config_data(module_code).get('nomenclature') + nomenclature_types = config_data(module_code).get("nomenclature") if not nomenclature_types: return {} q = ( DB.session.query(TNomenclatures) - .join( - BibNomenclaturesTypes, - BibNomenclaturesTypes.id_type == TNomenclatures.id_type - ) - .filter( - BibNomenclaturesTypes.mnemonique.in_(nomenclature_types) - ) + .join(BibNomenclaturesTypes, BibNomenclaturesTypes.id_type == TNomenclatures.id_type) + .filter(BibNomenclaturesTypes.mnemonique.in_(nomenclature_types)) .all() ) - return { - d.id_nomenclature: d.as_dict() - for d in q - } + return {d.id_nomenclature: d.as_dict() for d in q} + def get_taxonomy(module_code): - id_list = config_data(module_code)['taxonomy'].get('id_list') + id_list = config_data(module_code)["taxonomy"].get("id_list") taxonomy = get_taxonomy_from_id_list(id_list) return taxonomy def get_taxonomy_from_id_list(id_list): - if not id_list: return {} @@ -65,20 +51,13 @@ def get_taxonomy_from_id_list(id_list): q = ( DB.session.query(Taxref) .join( - CorNomListe, - and_( - CorNomListe.id_liste == id_list, - CorNomListe.id_nom == Taxref.cd_nom - ) + CorNomListe, and_(CorNomListe.id_liste == id_list, CorNomListe.id_nom == Taxref.cd_nom) ) .join() .all() ) - return { - (d.cd_nom): (d.nom_complet) - for d in q - } + return {(d.cd_nom): (d.nom_complet) for d in q} def get_users(module_code): diff --git a/backend/gn_module_monitoring/config/repositories.py b/backend/gn_module_monitoring/config/repositories.py index 29c5b9c42..b7526848d 100644 --- a/backend/gn_module_monitoring/config/repositories.py +++ b/backend/gn_module_monitoring/config/repositories.py @@ -17,21 +17,21 @@ get_monitoring_module, get_monitorings_path, get_data_preload, - monitoring_module_config_path + monitoring_module_config_path, ) # pour stocker la config dans current_app.config -config_cache_name = 'MONITORINGS_CONFIG' +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 - ''' + """ + recupere la config de chaque object present dans tree pour le module + """ if not tree: # initial tree - tree = config['tree'] + tree = config["tree"] for object_type in tree: # config object @@ -41,18 +41,21 @@ def get_config_objects(module_code, config, tree=None, parent_type=None): # tree children_types = tree[object_type] and list(tree[object_type].keys()) or [] - if not 'children_types' in config[object_type]: - config[object_type]['children_types'] = [] - config[object_type]['children_types'] += children_types - config[object_type]['children_types'] = list(dict.fromkeys(config[object_type]['children_types'])) + if not "children_types" in config[object_type]: + config[object_type]["children_types"] = [] + config[object_type]["children_types"] += children_types + config[object_type]["children_types"] = list( + dict.fromkeys(config[object_type]["children_types"]) + ) - if not 'parent_types' in config[object_type]: - config[object_type]['parent_types'] = [] + if not "parent_types" in config[object_type]: + config[object_type]["parent_types"] = [] if parent_type: - config[object_type]['parent_types'].append(parent_type) - config[object_type]['parent_types'] = list(dict.fromkeys(config[object_type]['parent_types'])) - + config[object_type]["parent_types"].append(parent_type) + config[object_type]["parent_types"] = list( + dict.fromkeys(config[object_type]["parent_types"]) + ) # config[object_type]['parent_type'] = parent_type @@ -64,13 +67,13 @@ def get_config_objects(module_code, config, tree=None, parent_type=None): # root if (not parent_type) and children_types: - config[object_type]['root_object'] = True + config[object_type]["root_object"] = True # id_table_location - config[object_type]['id_table_location'] = get_id_table_location(object_type) + config[object_type]["id_table_location"] = get_id_table_location(object_type) # filters - config[object_type]['filters'] = config[object_type].get('filters') or {} + config[object_type]["filters"] = config[object_type].get("filters") or {} # recursif if tree[object_type]: @@ -78,11 +81,13 @@ def get_config_objects(module_code, config, tree=None, parent_type=None): def config_object_from_files(module_code, object_type): - ''' - recupere la configuration d'un object de type pour le module - ''' - 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) + """ + recupere la configuration d'un object de type pour le module + """ + 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) + ) config_object = generic_config_object config_object.update(specific_config_object) @@ -91,21 +96,21 @@ def config_object_from_files(module_code, object_type): def get_config(module_code=None, force=False): - ''' - recupere la configuration pour le module monitoring + """ + recupere la configuration pour le module monitoring - si la configuration en presente dans le dictionnaire current_app.config - et si aucun fichier du dossier de configuration n'a été modifié depuis le dernier appel de cette fonction - alors la configuration est récupéré depuis current_app.config - sinon la config est recupérée depuis les fichiers du dossier de configuration et stockée dans current_app.config + si la configuration en presente dans le dictionnaire current_app.config + et si aucun fichier du dossier de configuration n'a été modifié depuis le dernier appel de cette fonction + alors la configuration est récupéré depuis current_app.config + sinon la config est recupérée depuis les fichiers du dossier de configuration et stockée dans current_app.config - ''' - module_code = module_code if module_code else 'generic' + """ + module_code = module_code if module_code else "generic" module_confg_dir_path = monitoring_module_config_path(module_code) # test si le repertoire existe - if module_code != 'generic' and not os.path.exists(module_confg_dir_path): + if module_code != "generic" and not os.path.exists(module_confg_dir_path): return None config = current_app.config.get(config_cache_name, {}).get(module_code) @@ -115,7 +120,6 @@ def get_config(module_code=None, force=False): if config and not force: return config - module = get_monitoring_module(module_code) # derniere modification @@ -127,39 +131,39 @@ def get_config(module_code=None, force=False): # test si present dans cache et pas modifée depuis le dernier appel # if config and config.get('last_modif', 0) >= last_modif: - # return config + # return config - config = config_from_files('config', module_code) + config = config_from_files("config", module_code) get_config_objects(module_code, config) # customize config if module: custom = {} - config['custom'] = {} + config["custom"] = {} for field_name in [ - 'module_code', - 'id_list_observer', - 'id_list_taxonomy', - 'b_synthese', - 'b_draw_sites_group', - 'taxonomy_display_field_name', - 'id_module' + "module_code", + "id_list_observer", + "id_list_taxonomy", + "b_synthese", + "b_draw_sites_group", + "taxonomy_display_field_name", + "id_module", ]: - var_name = '__MODULE.{}'.format(field_name.upper()) - config['custom'][var_name] = getattr(module, field_name) - config['module'][field_name] = getattr(module, field_name) + var_name = "__MODULE.{}".format(field_name.upper()) + config["custom"][var_name] = getattr(module, field_name) + config["module"][field_name] = getattr(module, field_name) - config['custom']['__MONITORINGS_PATH'] = get_monitorings_path() + config["custom"]["__MONITORINGS_PATH"] = get_monitorings_path() - config['default_display_field_names'].update(config.get('display_field_names', {})) - config['display_field_names'] = config['default_display_field_names'] + config["default_display_field_names"].update(config.get("display_field_names", {})) + config["display_field_names"] = config["default_display_field_names"] # Remplacement des variables __MODULE.XXX # par les valeurs spécifiées en base - customize_config(config, config['custom']) + customize_config(config, config["custom"]) # preload data # TODO auto from schemas && config recup tax users nomenclatures etc.... - config['data'] = get_data_preload(config, module) + config["data"] = get_data_preload(config, module) # mise en cache dans current_app.config[config_cache_name][module_code] if not current_app.config.get(config_cache_name, {}): @@ -170,27 +174,27 @@ def get_config(module_code=None, force=False): def config_param(module_code, object_type, param_name): - ''' - revoie un parametre de la configuration des objets + """ + revoie un parametre de la configuration des objets - :param module_code: reference le module concerne - :param object_type: le type d'object (site, visit, obervation) - :param param_name: le parametre voulu (id_field_name, label) - :type module_code: str - :type object_type: str - :type param_name: str - :return: valeur du paramètre requis - :rtype: str + :param module_code: reference le module concerne + :param object_type: le type d'object (site, visit, obervation) + :param param_name: le parametre voulu (id_field_name, label) + :type module_code: str + :type object_type: str + :type param_name: str + :return: valeur du paramètre requis + :rtype: str - :Exemple: + :Exemple: - config_param('oedic', 'site', 'id_field_name') - renverra 'id_base_site' + config_param('oedic', 'site', 'id_field_name') + renverra 'id_base_site' - config_param('oedic', 'site', 'label') - renverra 'Site' + config_param('oedic', 'site', 'label') + renverra 'Site' - ''' + """ config = get_config(module_code) @@ -198,31 +202,31 @@ def config_param(module_code, object_type, param_name): def config_schema(module_code, object_type, type_schema="all"): - ''' - renvoie une liste d'éléments de configuration de formulaire - - pour type_schema: - 'generic' : renvoie le schema générique - 'specific' : renvoie le schema spécifique - 'all': par defaut renvoie tout le schema - - Un élément est un dictionaire de type - { - "attribut_name": "id_base_site", - "Label": "Id du site", - "type_widget": "integer", - "required": "true", - } - - :param module_code: reference le module concerne - :param object_type: le type d'object (site, visit, obervation) - :param type_schema: le type de schema requis ('all', 'generic', 'specific') - :type module_code: str - :type object_type: str - :type type_schema: str, optional - :return: tableau d'élément de configuration de formulaire - :rtype: list - ''' + """ + renvoie une liste d'éléments de configuration de formulaire + + pour type_schema: + 'generic' : renvoie le schema générique + 'specific' : renvoie le schema spécifique + 'all': par defaut renvoie tout le schema + + Un élément est un dictionaire de type + { + "attribut_name": "id_base_site", + "Label": "Id du site", + "type_widget": "integer", + "required": "true", + } + + :param module_code: reference le module concerne + :param object_type: le type d'object (site, visit, obervation) + :param type_schema: le type de schema requis ('all', 'generic', 'specific') + :type module_code: str + :type object_type: str + :type type_schema: str, optional + :return: tableau d'élément de configuration de formulaire + :rtype: list + """ # recuperation de la configuration config = get_config(module_code) @@ -230,13 +234,12 @@ def config_schema(module_code, object_type, type_schema="all"): return config[object_type][type_schema] # renvoie le schema complet si type_schema == 'all' ou par defaut - schema = dict(config[object_type]['generic']) - schema.update(config[object_type]['specific']) + schema = dict(config[object_type]["generic"]) + schema.update(config[object_type]["specific"]) return schema def get_config_frontend(module_code=None, force=True): - config = dict(get_config(module_code, force)) return config diff --git a/backend/gn_module_monitoring/config/utils.py b/backend/gn_module_monitoring/config/utils.py index c2ffb3005..a23e26bf3 100644 --- a/backend/gn_module_monitoring/config/utils.py +++ b/backend/gn_module_monitoring/config/utils.py @@ -1,28 +1,29 @@ import os, datetime, time import importlib from pathlib import Path -from flask import current_app import json from sqlalchemy import and_ from geonature.core.gn_commons.models import BibTablesLocation, TModules from geonature.utils.errors import GeoNatureError -from geonature.utils.env import DB, DEFAULT_CONFIG_FILE, BACKEND_DIR - +from geonature.utils.env import DB +from geonature.utils.config import config as gn_config from ..monitoring.models import TMonitoringModules -SUB_MODULE_CONFIG_DIR = Path(current_app.config['MEDIA_FOLDER']) / 'monitorings/' +SUB_MODULE_CONFIG_DIR = Path(gn_config["MEDIA_FOLDER"]) / "monitorings/" + def monitoring_module_config_path(module_code): return SUB_MODULE_CONFIG_DIR / module_code + def get_monitoring_module(module_code): - ''' - ne doit pas lancer d'exception sinon plante l'install - -> all()[0] - ''' - if module_code == 'generic': + """ + ne doit pas lancer d'exception sinon plante l'install + -> all()[0] + """ + if module_code == "generic": return None res = ( @@ -33,42 +34,43 @@ def get_monitoring_module(module_code): return res[0] if len(res) else None + def get_monitorings_path(): return ( DB.session.query(TModules.module_path) - .filter(TModules.module_code == 'MONITORINGS') + .filter(TModules.module_code == "MONITORINGS") .one()[0] ) + def get_base_last_modif(module): - ''' - renvoie (en seconde depuis le 1 1 1970) la date de modif du module - (i. e. de la ligne de gn_monitoring.t_module_complement ) - ''' + """ + renvoie (en seconde depuis le 1 1 1970) la date de modif du module + (i. e. de la ligne de gn_monitoring.t_module_complement ) + """ if not module: return 0 now_timestamp = time.time() # prise en compte du offset pour etre raccord avec la date des fichiers offset = ( - datetime.datetime.fromtimestamp(now_timestamp) - datetime.datetime.utcfromtimestamp(now_timestamp) + datetime.datetime.fromtimestamp(now_timestamp) + - datetime.datetime.utcfromtimestamp(now_timestamp) ).total_seconds() - date_module = getattr(module, 'meta_update_date') or getattr(module, 'meta_create_date') + date_module = getattr(module, "meta_update_date") or getattr(module, "meta_create_date") return (date_module - datetime.datetime(1970, 1, 1)).total_seconds() - offset - def get_id_table_location(object_type): - table_names = { - 'module': 't_module_complements', - 'site': 't_base_sites', - 'visit': 't_base_visits', - 'observation': 't_observations' + "module": "t_module_complements", + "site": "t_base_sites", + "visit": "t_base_visits", + "observation": "t_observations", } - schema_name = 'gn_monitoring' + schema_name = "gn_monitoring" table_name = table_names.get(object_type) if not table_name: @@ -82,7 +84,7 @@ def get_id_table_location(object_type): .filter( and_( BibTablesLocation.schema_name == schema_name, - BibTablesLocation.table_name == table_name + BibTablesLocation.table_name == table_name, ) ) .one() @@ -103,43 +105,45 @@ def keys_remove_doublons(dict1, dict2): def json_from_file(file_path, result_default={}): - ''' - get json content from a json file - - :param file_path: chemin absolu vers le fichier - :param result_default: resultat par defaut, renvoyé si le fichier n'existe pas ([], None, {}) - :type file_path : str - :type result_default: dict | list - :return: default result if file does not exists - :rtype: dict | list - ''' + """ + get json content from a json file + + :param file_path: chemin absolu vers le fichier + :param result_default: resultat par defaut, renvoyé si le fichier n'existe pas ([], None, {}) + :type file_path : str + :type result_default: dict | list + :return: default result if file does not exists + :rtype: dict | list + """ out = result_default try: if os.path.exists(file_path): - with open(file_path, 'r') as f: + with open(file_path, "r") as f: out = json.load(f) except Exception as e: pass raise GeoNatureError( - "Module monitoring - Config - error in file {} : {}" - .format(file_path, str(e)) + "Module monitoring - Config - error in file {} : {}".format(file_path, str(e)) ) return out def json_config_from_file(module_code, type_config): - if module_code == "generic": - config_txt = importlib.resources.read_text('gn_module_monitoring.config.generic', f'{type_config}.json') + config_txt = importlib.resources.read_text( + "gn_module_monitoring.config.generic", f"{type_config}.json" + ) return json.loads(config_txt) file_path = monitoring_module_config_path(module_code) / f"{type_config}.json" return json_from_file(file_path, {}) -def config_from_files(config_type, module_code): - generic_config_custom = json_config_from_file('generic', config_type) - specific_config_custom = {} if module_code == 'generic' else json_config_from_file(module_code, config_type) +def config_from_files(config_type, module_code): + generic_config_custom = json_config_from_file("generic", config_type) + specific_config_custom = ( + {} if module_code == "generic" else json_config_from_file(module_code, config_type) + ) generic_config_custom.update(specific_config_custom) @@ -147,18 +151,18 @@ def config_from_files(config_type, module_code): def get_directory_last_modif(dir_path): - ''' - get the last modification time among all file in a directory - - :param dir_path: absolute path to the directory - :type dir_path: str - :return: last modification time - :rtype: float - ''' + """ + get the last modification time among all file in a directory + + :param dir_path: absolute path to the directory + :type dir_path: str + :return: last modification time + :rtype: float + """ modification_time_max = 0 - for (repertoire, _, fichiers) in os.walk(dir_path, followlinks=True): + for repertoire, _, fichiers in os.walk(dir_path, followlinks=True): for fichier in fichiers: - modification_time = os.path.getmtime(repertoire + '/' + fichier) + modification_time = os.path.getmtime(repertoire + "/" + fichier) if modification_time > modification_time_max: modification_time_max = modification_time @@ -166,25 +170,23 @@ def get_directory_last_modif(dir_path): def schema_dict_to_array(schema_dict): - schema_array = [] for key in schema_dict: elem = schema_dict[key] - elem['attribut_name'] = key + elem["attribut_name"] = key schema_array.append(elem) return schema_array def process_schema(object_type, config): - - generic = config[object_type]['generic'] + generic = config[object_type]["generic"] # specific n'est pas toujours defini - if not config[object_type].get('specific'): - config[object_type]['specific'] = {} - specific = config[object_type]['specific'] + if not config[object_type].get("specific"): + config[object_type]["specific"] = {} + specific = config[object_type]["specific"] # generic redef in specific # cas ou un element de generic est redefini dans specific @@ -195,8 +197,8 @@ def process_schema(object_type, config): if key_s == key_g: key = key_s - type_widget_s = specific[key].get('type_widget') - type_widget_g = generic[key].get('type_widget') + type_widget_s = specific[key].get("type_widget") + type_widget_g = generic[key].get("type_widget") if type_widget_s and type_widget_s == type_widget_g: generic[key] = copy_dict(specific[key]) @@ -207,37 +209,28 @@ def process_schema(object_type, config): def process_config_display(object_type, config): - config_object = config[object_type] - schema = dict(config_object['generic']) - schema.update(config_object['specific']) + schema = dict(config_object["generic"]) + schema.update(config_object["specific"]) properties_keys = list(schema.keys()) display_properties = config_object.get( - 'display_properties', - [key for key in schema if not schema[key].get('hidden')] + "display_properties", [key for key in schema if not schema[key].get("hidden")] ) - display_list = config_object.get( - 'display_list', - display_properties - ) + display_list = config_object.get("display_list", display_properties) - display_form = config_object.get( - 'display_form', - [] - ) + display_form = config_object.get("display_form", []) - config_object['display_properties'] = display_properties - config_object['display_list'] = display_list - config_object['properties_keys'] = properties_keys - config_object['display_form'] = display_form + config_object["display_properties"] = display_properties + config_object["display_list"] = display_list + config_object["properties_keys"] = properties_keys + config_object["display_form"] = display_form def customize_config(elem, custom): - if isinstance(elem, list): elem = [customize_config(e, custom) for e in elem] # patch remove doublons @@ -259,50 +252,49 @@ def customize_config(elem, custom): def get_data_preload(config, module): - out = { - 'nomenclature': ['TYPE_MEDIA'] - } + out = {"nomenclature": ["TYPE_MEDIA"]} if module.id_list_observer: - out['user'] = module.id_list_observer + out["user"] = module.id_list_observer for object_type in config: - if object_type in ['tree', 'data'] or not isinstance(config[object_type], dict): + if object_type in ["tree", "data"] or not isinstance(config[object_type], dict): continue - schema = dict(config[object_type].get('generic', {})) - schema.update(config[object_type].get('specific', {})) + schema = dict(config[object_type].get("generic", {})) + schema.update(config[object_type].get("specific", {})) for name in schema: form = schema[name] - type_util = form.get('type_util') - type_widget = form.get('type_widget') - value = form.get('value') + type_util = form.get("type_util") + type_widget = form.get("type_widget") + value = form.get("value") # composant nomenclature - if type_widget == 'nomenclature': - out['nomenclature'].append(form['code_nomenclature_type']) - + if type_widget == "nomenclature": + out["nomenclature"].append(form["code_nomenclature_type"]) + # composant datalist - if type_widget == 'datalist': + if type_widget == "datalist": if type_util == "nomenclature": # on récupère le code de nomenclature depuis l'api - nomenclature_type = form.get('api').split('/')[-1] - out['nomenclature'].append(nomenclature_type) + nomenclature_type = form.get("api").split("/")[-1] + out["nomenclature"].append(nomenclature_type) # if type_util == "sites_group": - # out['sites_group'] = True + # out['sites_group'] = True - if type_widget == 'text': - if type_util == 'nomenclature' and value: - code_type = (value or {}).get('code_nomenclature_type') + if type_widget == "text": + if type_util == "nomenclature" and value: + code_type = (value or {}).get("code_nomenclature_type") if code_type: - out['nomenclature'].append(code_type) + out["nomenclature"].append(code_type) # remove doublons - out['nomenclature'] = list(dict.fromkeys(out['nomenclature'])) + out["nomenclature"] = list(dict.fromkeys(out["nomenclature"])) return out + def config_from_files_customized(type_config, module_code): config_type = config_from_files(type_config, module_code) - custom = config_from_files('custom', module_code) + custom = config_from_files("custom", module_code) return customize_config(config_type, custom) diff --git a/backend/gn_module_monitoring/migrations/e78003460441_correction_t_observation_detail.py b/backend/gn_module_monitoring/migrations/e78003460441_correction_t_observation_detail.py index 5d8b9d227..a111c88f3 100644 --- a/backend/gn_module_monitoring/migrations/e78003460441_correction_t_observation_detail.py +++ b/backend/gn_module_monitoring/migrations/e78003460441_correction_t_observation_detail.py @@ -10,14 +10,15 @@ # revision identifiers, used by Alembic. -revision = 'e78003460441' -down_revision = '2003e18f248a' +revision = "e78003460441" +down_revision = "2003e18f248a" branch_labels = None depends_on = None def upgrade(): - op.execute(""" + op.execute( + """ ALTER TABLE gn_monitoring.t_observation_details DROP CONSTRAINT pk_t_observation_details; ALTER TABLE gn_monitoring.t_observation_details @@ -25,11 +26,13 @@ def upgrade(): ALTER TABLE gn_monitoring.t_observation_details ADD uuid_observation_detail UUID DEFAULT uuid_generate_v4() NOT NULL; - """) + """ + ) def downgrade(): - op.execute(""" + op.execute( + """ ALTER TABLE gn_monitoring.t_observation_details DROP CONSTRAINT pk_t_observation_details; ALTER TABLE gn_monitoring.t_observation_details @@ -37,4 +40,5 @@ def downgrade(): ALTER TABLE gn_monitoring.t_observation_details DROP COLUMN uuid_observation_detail; - """) + """ + ) diff --git a/backend/gn_module_monitoring/migrations/fc90d31c677f_declare_available_permissions.py b/backend/gn_module_monitoring/migrations/fc90d31c677f_declare_available_permissions.py new file mode 100644 index 000000000..eba68daea --- /dev/null +++ b/backend/gn_module_monitoring/migrations/fc90d31c677f_declare_available_permissions.py @@ -0,0 +1,102 @@ +"""declare available permissions + +Revision ID: fc90d31c677f +Revises: e78003460441 +Create Date: 2023-06-09 10:32:21.008918 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = "fc90d31c677f" +down_revision = "e78003460441" +branch_labels = None +depends_on = ("f051b88a57fd",) + + +def upgrade(): + op.execute( + """ + INSERT INTO + gn_permissions.t_permissions_available ( + id_module, + id_object, + id_action, + label, + scope_filter + ) + SELECT + m.id_module, + o.id_object, + a.id_action, + v.label, + v.scope_filter + FROM + ( + VALUES + ('MONITORINGS', 'ALL', 'R', False, 'Accéder au module') + ) AS v (module_code, object_code, action_code, scope_filter, label) + JOIN + gn_commons.t_modules m ON m.module_code = v.module_code + JOIN + gn_permissions.t_objects o ON o.code_object = v.object_code + JOIN + gn_permissions.bib_actions a ON a.code_action = v.action_code + """ + ) + op.execute( + """ + WITH bad_permissions AS ( + SELECT + p.id_permission + FROM + gn_permissions.t_permissions p + JOIN gn_commons.t_modules m + USING (id_module) + WHERE + m.module_code = 'MONITORINGS' + EXCEPT + SELECT + p.id_permission + FROM + gn_permissions.t_permissions p + JOIN gn_permissions.t_permissions_available pa ON + (p.id_module = pa.id_module + AND p.id_object = pa.id_object + AND p.id_action = pa.id_action) + ) + DELETE + FROM + gn_permissions.t_permissions p + USING bad_permissions bp + WHERE + bp.id_permission = p.id_permission; + """ + ) + + op.execute( + """ + INSERT INTO gn_permissions.t_objects (code_object, description_object) + VALUES + ('GNM_MODULES', 'Permissions sur les modules') + ON CONFLICT DO NOTHING + ; + """ + ) + + +def downgrade(): + op.execute( + """ + DELETE FROM + gn_permissions.t_permissions_available pa + USING + gn_commons.t_modules m + WHERE + pa.id_module = m.id_module + AND + module_code = 'MONITORINGS' + """ + ) diff --git a/backend/gn_module_monitoring/modules/repositories.py b/backend/gn_module_monitoring/modules/repositories.py index 303c6d73d..c186495de 100644 --- a/backend/gn_module_monitoring/modules/repositories.py +++ b/backend/gn_module_monitoring/modules/repositories.py @@ -11,12 +11,11 @@ from geonature.core.gn_commons.models import TModules from geonature.core.gn_synthese.models import TSources -from ..monitoring.models import ( - TMonitoringModules -) +from ..monitoring.models import TMonitoringModules + def get_simple_module(field_name, value): - ''' + """ récupere un module a partir d'un paramètre le paramètre pour la recherche par défaut est 'id_module' @@ -29,12 +28,12 @@ def get_simple_module(field_name, value): :return module as dict :rtype : dict - ''' + """ return get_module(field_name, value, TModules) def get_module(field_name, value, moduleCls=TMonitoringModules): - ''' + """ récupere un module de protocole de suivi a partir d'un paramètre le paramètre pour la recherche par défaut est 'id_module' @@ -47,49 +46,42 @@ def get_module(field_name, value, moduleCls=TMonitoringModules): :return module as dict :rtype : dict - ''' + """ if not hasattr(moduleCls, field_name): - raise GeoNatureError('get_module : TMonitoringModules ne possède pas de champs {}'.format(field_name)) + raise GeoNatureError( + "get_module : TMonitoringModules ne possède pas de champs {}".format(field_name) + ) try: - module = ( - DB.session.query(moduleCls).filter( - getattr(moduleCls, field_name) == value - ).one() - ) + module = DB.session.query(moduleCls).filter(getattr(moduleCls, field_name) == value).one() return module except MultipleResultsFound: raise GeoNatureError( - 'get_module : multiple results found for field_name {} and value {}' - .format(field_name, value) + "get_module : multiple results found for field_name {} and value {}".format( + field_name, value + ) ) except Exception as e: - raise GeoNatureError( - 'get_module : {}' - .format(str(e)) - ) + raise GeoNatureError("get_module : {}".format(str(e))) # dans ce case on renvoie None pass def get_modules(): - ''' + """ récupère les modules de protocole de suivi renvoie un tableau de dictionnaires :return: - ''' + """ modules_out = [] try: - res = ( - DB.session.query(TMonitoringModules).order_by(TMonitoringModules.module_label) - .all() - ) + res = DB.session.query(TMonitoringModules).order_by(TMonitoringModules.module_label).all() return res @@ -103,29 +95,15 @@ def get_modules(): def get_source_by_code(value): try: - source = ( - DB.session.query(TSources).filter( - TSources.name_source == value - ).one() - ) + source = DB.session.query(TSources).filter(TSources.name_source == value).one() return source except MultipleResultsFound: - raise GeoNatureError( - 'get_source : multiple results found for {}' - .format(value) - ) + raise GeoNatureError("get_source : multiple results found for {}".format(value)) except NoResultFound: - raise GeoNatureError( - 'get_source : no results found for {}' - .format(value) - ) + raise GeoNatureError("get_source : no results found for {}".format(value)) except Exception as e: - raise GeoNatureError( - 'get_source : {}' - .format(str(e)) - ) + raise GeoNatureError("get_source : {}".format(str(e))) # dans ce case on renvoie None pass - diff --git a/backend/gn_module_monitoring/monitoring/base.py b/backend/gn_module_monitoring/monitoring/base.py index cf3f45ab1..ab331e005 100644 --- a/backend/gn_module_monitoring/monitoring/base.py +++ b/backend/gn_module_monitoring/monitoring/base.py @@ -8,11 +8,11 @@ class MonitoringDefinitions: - ''' - class pour pouvoir obtenir les classes filles de MonitoringObjectBase - fonction du type d'objet - _MonitoringObjectTypes_dict sera initialisé ultérieurement - ''' + """ + class pour pouvoir obtenir les classes filles de MonitoringObjectBase + fonction du type d'objet + _MonitoringObjectTypes_dict sera initialisé ultérieurement + """ _MonitoringObjects_dict = {} _MonitoringModels_dict = {} @@ -34,8 +34,9 @@ def MonitoringObject(self, object_type): return self._MonitoringObjects_dict[object_type] except Exception as e: raise GeoNatureError( - "MONITORING, il n'y a pas de monitoring_object pour le type {} : {}" - .format(object_type, str(e)) + "MONITORING, il n'y a pas de monitoring_object pour le type {} : {}".format( + object_type, str(e) + ) ) def monitoring_object_instance(self, module_code, object_type, id=None, model=None): @@ -46,16 +47,16 @@ def MonitoringModel(self, object_type): return self._MonitoringModels_dict[object_type] except Exception as e: raise GeoNatureError( - "MONITORING, il n'y a pas de modele pour le type {} : {}" - .format(object_type, str(e)) + "MONITORING, il n'y a pas de modele pour le type {} : {}".format( + object_type, str(e) + ) ) monitoring_definitions = MonitoringDefinitions() -class MonitoringObjectBase(): - +class MonitoringObjectBase: _object_type = None _module_code = None _id = None @@ -65,13 +66,12 @@ class MonitoringObjectBase(): _parent = None def __init__(self, module_code, object_type, id=None, model=None): - self._module_code = module_code self._object_type = object_type self._id = id if not self._id and model: - self._id = getattr(model, self.config_param('id_field_name')) + self._id = getattr(model, self.config_param("id_field_name")) self.set_model_from(model) @@ -83,27 +83,18 @@ def set_model_from(self, model): self._model = Model() def __str__(self): - return ( - 'monitoringobject {}, {}, {}' - .format(self._module_code, self._object_type, self._id) - ) + return "monitoringobject {}, {}, {}".format(self._module_code, self._object_type, self._id) def MonitoringModel(self): - try: - Model = ( - monitoring_definitions - .MonitoringModel( - self._object_type - ) - ) + Model = monitoring_definitions.MonitoringModel(self._object_type) return Model pass except Exception: pass - inherit_type = self.config_param('inherit_type') + inherit_type = self.config_param("inherit_type") new_object_type = inherit_type or self._object_type Model = monitoring_definitions.MonitoringModel(new_object_type) @@ -124,7 +115,7 @@ def get_value_specific(self, param_name): return self._model.data and self._model.data.get(param_name) def get_value(self, param_name): - schema_generic = self.config_schema('generic') + schema_generic = self.config_schema("generic") if param_name in schema_generic: return self.get_value_generic(param_name) return self.get_value_specific(param_name) @@ -134,18 +125,17 @@ def config_value(self, param_name): return self.get_value(field_name) def parent_type(self): - ''' - on renvoie le premier de la liste - ''' - return self.config_param('parent_types') and self.config_param('parent_types')[0] + """ + on renvoie le premier de la liste + """ + return self.config_param("parent_types") and self.config_param("parent_types")[0] def parent_config_param(self, param_name): - parent_type = self.parent_type() if parent_type: return repositories_config_param(self._module_code, parent_type, param_name) - def config_schema(self, type_schema='all'): + def config_schema(self, type_schema="all"): return repositories_config_schema(self._module_code, self._object_type, type_schema) pass @@ -175,20 +165,20 @@ def config_schema(self, type_schema='all'): # return base_object_type == base_parent_type def id_parent_fied_name(self): - return self.parent_config_param('id_field_name') + return self.parent_config_param("id_field_name") def id_parent(self): parent_type = self.parent_type() if not parent_type: return - if 'module' in parent_type: + if "module" in parent_type: return self._module_code return getattr(self._model, self.id_parent_fied_name()) def cond_filters(self): - filters = self.config_param('filters') + filters = self.config_param("filters") if not filters: return True diff --git a/backend/gn_module_monitoring/monitoring/definitions.py b/backend/gn_module_monitoring/monitoring/definitions.py index f61ceb538..da018295d 100644 --- a/backend/gn_module_monitoring/monitoring/definitions.py +++ b/backend/gn_module_monitoring/monitoring/definitions.py @@ -6,45 +6,53 @@ TMonitoringObservationDetails, TMonitoringSitesGroups, ) -from .objects import ( - MonitoringModule, - MonitoringSite -) +from .objects import MonitoringModule, MonitoringSite from .base import monitoring_definitions from .repositories import MonitoringObject from .geom import MonitoringObjectGeom -''' +""" MonitoringModels_dict : Fait le lien entre les monitoring_objects et les modèles sqlalchemy -''' +""" 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 = { - 'module': MonitoringModule, # besoin pour retrouver le module depuis module_code à voir si on peux faire sans - 'site': MonitoringSite, - 'visit': MonitoringObject, - 'observation': MonitoringObject, - 'observation_detail': MonitoringObject, - 'sites_group': MonitoringObjectGeom, + "module": MonitoringModule, # besoin pour retrouver le module depuis module_code à voir si on peux faire sans + "site": MonitoringSite, + "visit": MonitoringObject, + "observation": MonitoringObject, + "observation_detail": MonitoringObject, + "sites_group": MonitoringObjectGeom, } MonitoringPermissions_dict = { - 'site': "GNM_SITES", - 'sites_group': "GNM_GRP_SITES", - 'visite': "GNM_VISITES", - 'observation': "GNM_OBSERVATIONS" + "site": "GNM_SITES", + "sites_group": "GNM_GRP_SITES", + "visite": "GNM_VISITES", + "observation": "GNM_OBSERVATIONS", + "module": "GNM_MODULES", +} + +MonitoringPermissionObjectLabel_dict = { + "ALL": "objets (sites, visites, observations, etc...)", + "GNM_SITES": "sites", + "GNM_OBSERVATIONS": "observations", + "GNM_VISITES": "visites", + "GNM_GRP_SITES": "groupes de sites", + "GNM_MODULES": "modules", } + monitoring_definitions.set(MonitoringObjects_dict, MonitoringModels_dict) diff --git a/backend/gn_module_monitoring/monitoring/geom.py b/backend/gn_module_monitoring/monitoring/geom.py index 0ee5b5025..b520c1226 100644 --- a/backend/gn_module_monitoring/monitoring/geom.py +++ b/backend/gn_module_monitoring/monitoring/geom.py @@ -4,27 +4,32 @@ class MonitoringObjectGeom(MonitoringObject): - def as_geofeature(self, depth=None, columns=()): # TODO refaire - id_field_name = self.config_param('id_field_name') - geom_field_name = self.config_param('geom_field_name') - return self._model.as_geofeature(geom_field_name, id_field_name, depth=depth, columns=columns) + id_field_name = self.config_param("id_field_name") + geom_field_name = self.config_param("geom_field_name") + return self._model.as_geofeature( + geom_field_name, id_field_name, depth=depth, columns=columns + ) def serialize(self, depth): monitoring_object_dict = MonitoringObject.serialize(self, depth) geometry = {} - if hasattr(self._model, 'geom_geojson'): - geom_geojson = getattr(self._model, 'geom_geojson') - - geometry = json.loads( - # self._model.__dict__.get('geom_geojson') - getattr(self._model, 'geom_geojson') - ) if geom_geojson else None + if hasattr(self._model, "geom_geojson"): + geom_geojson = getattr(self._model, "geom_geojson") + + geometry = ( + json.loads( + # self._model.__dict__.get('geom_geojson') + getattr(self._model, "geom_geojson") + ) + if geom_geojson + else None + ) if not self._id: geometry = None - monitoring_object_dict['geometry'] = geometry + monitoring_object_dict["geometry"] = geometry return monitoring_object_dict diff --git a/backend/gn_module_monitoring/monitoring/models.py b/backend/gn_module_monitoring/monitoring/models.py index 3eae53aa6..96d11c994 100644 --- a/backend/gn_module_monitoring/monitoring/models.py +++ b/backend/gn_module_monitoring/monitoring/models.py @@ -20,27 +20,24 @@ from pypnusershub.db.models import User from geonature.core.gn_monitoring.models import corVisitObserver + @serializable class TMonitoringObservationDetails(DB.Model): __tablename__ = "t_observation_details" __table_args__ = {"schema": "gn_monitoring"} - id_observation_detail = DB.Column( - DB.Integer, - primary_key=True, - nullable=False, - unique=True - ) + id_observation_detail = DB.Column(DB.Integer, primary_key=True, nullable=False, unique=True) - id_observation = DB.Column(DB.ForeignKey('gn_monitoring.t_observations.id_observation')) + id_observation = DB.Column(DB.ForeignKey("gn_monitoring.t_observations.id_observation")) data = DB.Column(JSONB) uuid_observation_detail = DB.Column(UUID(as_uuid=True), default=uuid4) medias = DB.relationship( TMedias, - lazy='joined', + lazy="joined", primaryjoin=(TMedias.uuid_attached_row == uuid_observation_detail), - foreign_keys=[TMedias.uuid_attached_row]) + foreign_keys=[TMedias.uuid_attached_row], + ) @serializable @@ -48,28 +45,24 @@ class TObservations(DB.Model): __tablename__ = "t_observations" __table_args__ = {"schema": "gn_monitoring"} - id_observation = DB.Column( - DB.Integer, - primary_key=True, - nullable=False, - unique=True) - id_base_visit = DB.Column( - DB.ForeignKey('gn_monitoring.t_base_visits.id_base_visit')) + id_observation = DB.Column(DB.Integer, primary_key=True, nullable=False, unique=True) + id_base_visit = DB.Column(DB.ForeignKey("gn_monitoring.t_base_visits.id_base_visit")) cd_nom = DB.Column(DB.Integer) comments = DB.Column(DB.String) uuid_observation = DB.Column(UUID(as_uuid=True), default=uuid4) medias = DB.relationship( TMedias, - lazy='joined', + lazy="joined", primaryjoin=(TMedias.uuid_attached_row == uuid_observation), - foreign_keys=[TMedias.uuid_attached_row]) + foreign_keys=[TMedias.uuid_attached_row], + ) observation_details = DB.relation( TMonitoringObservationDetails, primaryjoin=(id_observation == TMonitoringObservationDetails.id_observation), foreign_keys=[TMonitoringObservationDetails.id_observation], - cascade="all,delete" + cascade="all,delete", ) @@ -78,16 +71,16 @@ class TMonitoringObservations(TObservations): __tablename__ = "t_observation_complements" __table_args__ = {"schema": "gn_monitoring"} __mapper_args__ = { - 'polymorphic_identity': 'monitoring_observation', + "polymorphic_identity": "monitoring_observation", } data = DB.Column(JSONB) id_observation = DB.Column( - DB.ForeignKey('gn_monitoring.t_observations.id_observation'), + DB.ForeignKey("gn_monitoring.t_observations.id_observation"), primary_key=True, nullable=False, - ) + ) TBaseVisits.dataset = DB.relationship(TDatasets) @@ -98,66 +91,62 @@ class TMonitoringVisits(TBaseVisits): __tablename__ = "t_visit_complements" __table_args__ = {"schema": "gn_monitoring"} __mapper_args__ = { - 'polymorphic_identity': 'monitoring_visit', + "polymorphic_identity": "monitoring_visit", } id_base_visit = DB.Column( - DB.ForeignKey('gn_monitoring.t_base_visits.id_base_visit'), + DB.ForeignKey("gn_monitoring.t_base_visits.id_base_visit"), nullable=False, - primary_key=True - ) + primary_key=True, + ) data = DB.Column(JSONB) medias = DB.relationship( TMedias, - lazy='joined', + lazy="joined", primaryjoin=(TMedias.uuid_attached_row == TBaseVisits.uuid_base_visit), - foreign_keys=[TMedias.uuid_attached_row]) - - observers = DB.relationship( - User, - lazy='joined', - secondary=corVisitObserver + foreign_keys=[TMedias.uuid_attached_row], ) + observers = DB.relationship(User, lazy="joined", secondary=corVisitObserver) + observations = DB.relation( "TMonitoringObservations", - lazy='select', + lazy="select", primaryjoin=(TObservations.id_base_visit == TBaseVisits.id_base_visit), foreign_keys=[TObservations.id_base_visit], - cascade="all,delete" + cascade="all,delete", ) nb_observations = column_property( - select([func.count(TObservations.id_base_visit)]).\ - where(TObservations.id_base_visit==id_base_visit) + select([func.count(TObservations.id_base_visit)]).where( + TObservations.id_base_visit == id_base_visit + ) ) @geoserializable class TMonitoringSites(TBaseSites): - - __tablename__ = 't_site_complements' - __table_args__ = {'schema': 'gn_monitoring'} + __tablename__ = "t_site_complements" + __table_args__ = {"schema": "gn_monitoring"} __mapper_args__ = { - 'polymorphic_identity': 'monitoring_site', + "polymorphic_identity": "monitoring_site", } id_base_site = DB.Column( - DB.ForeignKey('gn_monitoring.t_base_sites.id_base_site'), - nullable=False, - primary_key=True + DB.ForeignKey("gn_monitoring.t_base_sites.id_base_site"), nullable=False, primary_key=True ) id_module = DB.Column( - DB.ForeignKey('gn_commons.t_modules.id_module'), + DB.ForeignKey("gn_commons.t_modules.id_module"), nullable=False, ) id_sites_group = DB.Column( - DB.ForeignKey('gn_monitoring.t_sites_groups.id_sites_group', - # ondelete='SET NULL' + DB.ForeignKey( + "gn_monitoring.t_sites_groups.id_sites_group", + # ondelete='SET NULL' ), nullable=False, ) @@ -169,49 +158,45 @@ class TMonitoringSites(TBaseSites): lazy="select", primaryjoin=(TBaseSites.id_base_site == TBaseVisits.id_base_site), foreign_keys=[TBaseVisits.id_base_site], - cascade="all,delete" + cascade="all,delete", ) medias = DB.relationship( TMedias, - lazy='joined', + lazy="joined", primaryjoin=(TMedias.uuid_attached_row == TBaseSites.uuid_base_site), foreign_keys=[TMedias.uuid_attached_row], cascade="all", ) last_visit = column_property( - select([func.max(TBaseVisits.visit_date_min)]).\ - where(TBaseVisits.id_base_site==id_base_site) + select([func.max(TBaseVisits.visit_date_min)]).where( + TBaseVisits.id_base_site == id_base_site + ) ) nb_visits = column_property( - select([func.count(TBaseVisits.id_base_site)]).\ - where(TBaseVisits.id_base_site==id_base_site) + select([func.count(TBaseVisits.id_base_site)]).where( + TBaseVisits.id_base_site == id_base_site + ) ) geom_geojson = column_property( - select([func.st_asgeojson(TBaseSites.geom)]).\ - where(TBaseSites.id_base_site==id_base_site).\ - correlate_except(TBaseSites) + select([func.st_asgeojson(TBaseSites.geom)]) + .where(TBaseSites.id_base_site == id_base_site) + .correlate_except(TBaseSites) ) + @serializable class TMonitoringSitesGroups(DB.Model): - __tablename__ = 't_sites_groups' - __table_args__ = {'schema': 'gn_monitoring'} + __tablename__ = "t_sites_groups" + __table_args__ = {"schema": "gn_monitoring"} - id_sites_group = DB.Column( - DB.Integer, - primary_key=True, - nullable=False, - unique=True - ) + id_sites_group = DB.Column(DB.Integer, primary_key=True, nullable=False, unique=True) id_module = DB.Column( - DB.ForeignKey('gn_commons.t_modules.id_module'), - nullable=False, - unique=True + DB.ForeignKey("gn_commons.t_modules.id_module"), nullable=False, unique=True ) uuid_sites_group = DB.Column(UUID(as_uuid=True), default=uuid4) @@ -240,35 +225,34 @@ class TMonitoringSitesGroups(DB.Model): ) nb_sites = column_property( - select([func.count(TMonitoringSites.id_sites_group)]).\ - where(TMonitoringSites.id_sites_group==id_sites_group) + select([func.count(TMonitoringSites.id_sites_group)]).where( + TMonitoringSites.id_sites_group == id_sites_group + ) ) nb_visits = column_property( - select([func.count(TMonitoringVisits.id_base_site)]).\ - where(and_( + select([func.count(TMonitoringVisits.id_base_site)]).where( + and_( TMonitoringVisits.id_base_site == TMonitoringSites.id_base_site, - TMonitoringSites.id_sites_group == id_sites_group - ) + TMonitoringSites.id_sites_group == id_sites_group, + ) ) ) - - @serializable class TMonitoringModules(TModules): - __tablename__ = 't_module_complements' - __table_args__ = {'schema': 'gn_monitoring'} + __tablename__ = "t_module_complements" + __table_args__ = {"schema": "gn_monitoring"} __mapper_args__ = { - 'polymorphic_identity': 'monitoring_module', + "polymorphic_identity": "monitoring_module", } id_module = DB.Column( - DB.ForeignKey('gn_commons.t_modules.id_module'), + DB.ForeignKey("gn_commons.t_modules.id_module"), primary_key=True, nullable=False, - unique=True + unique=True, ) uuid_module_complement = DB.Column(UUID(as_uuid=True), default=uuid4) @@ -284,11 +268,11 @@ class TMonitoringModules(TModules): TMedias, primaryjoin=(TMedias.uuid_attached_row == uuid_module_complement), foreign_keys=[TMedias.uuid_attached_row], - lazy='joined' + lazy="joined", ) sites = DB.relationship( - 'TMonitoringSites', + "TMonitoringSites", uselist=True, # pourquoi pas par defaut ? primaryjoin=TMonitoringSites.id_module == id_module, foreign_keys=[id_module], @@ -296,7 +280,7 @@ class TMonitoringModules(TModules): ) sites_groups = DB.relationship( - 'TMonitoringSitesGroups', + "TMonitoringSitesGroups", uselist=True, # pourquoi pas par defaut ? primaryjoin=TMonitoringSitesGroups.id_module == id_module, foreign_keys=[id_module], @@ -304,13 +288,12 @@ class TMonitoringModules(TModules): ) datasets = DB.relationship( - 'TDatasets', + "TDatasets", secondary=cor_module_dataset, join_depth=0, lazy="joined", ) - data = DB.Column(JSONB) # visits = DB.relationship( @@ -322,55 +305,52 @@ class TMonitoringModules(TModules): # ) - TMonitoringModules.visits = DB.relationship( - TMonitoringVisits, - lazy="select", - primaryjoin=(TMonitoringModules.id_module == TMonitoringVisits.id_module), - foreign_keys=[TMonitoringVisits.id_module], - cascade="all" - ) + TMonitoringVisits, + lazy="select", + primaryjoin=(TMonitoringModules.id_module == TMonitoringVisits.id_module), + foreign_keys=[TMonitoringVisits.id_module], + cascade="all", +) # add sites_group relationship to TMonitoringSites -TMonitoringSites.sites_group = ( - DB.relationship( - TMonitoringSitesGroups, - primaryjoin=( - TMonitoringSitesGroups.id_sites_group == TMonitoringSites.id_sites_group - ), - cascade="all", - lazy="select", - uselist=False - ) +TMonitoringSites.sites_group = DB.relationship( + TMonitoringSitesGroups, + primaryjoin=(TMonitoringSitesGroups.id_sites_group == TMonitoringSites.id_sites_group), + cascade="all", + lazy="select", + uselist=False, ) TMonitoringSitesGroups.visits = DB.relationship( - TMonitoringVisits, - primaryjoin=(TMonitoringSites.id_sites_group == TMonitoringSitesGroups.id_sites_group), - secondaryjoin=(TMonitoringVisits.id_base_site == TMonitoringSites.id_base_site), - secondary='gn_monitoring.t_site_complements', - ) + TMonitoringVisits, + primaryjoin=(TMonitoringSites.id_sites_group == TMonitoringSitesGroups.id_sites_group), + secondaryjoin=(TMonitoringVisits.id_base_site == TMonitoringSites.id_base_site), + secondary="gn_monitoring.t_site_complements", +) TMonitoringSitesGroups.nb_visits = column_property( - select([func.count(TMonitoringVisits.id_base_site)]).\ - where(and_( - TMonitoringVisits.id_base_site == TMonitoringSites.id_base_site, - TMonitoringSites.id_sites_group == TMonitoringSitesGroups.id_sites_group - ) + select([func.count(TMonitoringVisits.id_base_site)]).where( + and_( + TMonitoringVisits.id_base_site == TMonitoringSites.id_base_site, + TMonitoringSites.id_sites_group == TMonitoringSitesGroups.id_sites_group, + ) ) ) # note the alias is mandotory otherwise the where is done on the subquery table # and not the global TMonitoring table TMonitoringSitesGroups.geom_geojson = column_property( - select( - [func.st_asgeojson(func.st_convexHull(func.st_collect(TBaseSites.geom)))] - ).select_from(TMonitoringSitesGroups.__table__.alias("subquery").join( + select([func.st_asgeojson(func.st_convexHull(func.st_collect(TBaseSites.geom)))]) + .select_from( + TMonitoringSitesGroups.__table__.alias("subquery").join( TMonitoringSites, - TMonitoringSites.id_sites_group == TMonitoringSitesGroups.id_sites_group - )).where( TMonitoringSites.id_sites_group == TMonitoringSitesGroups.id_sites_group, ) ) + .where( + TMonitoringSites.id_sites_group == TMonitoringSitesGroups.id_sites_group, + ) +) diff --git a/backend/gn_module_monitoring/monitoring/objects.py b/backend/gn_module_monitoring/monitoring/objects.py index 235c0c7ce..edd51870e 100644 --- a/backend/gn_module_monitoring/monitoring/objects.py +++ b/backend/gn_module_monitoring/monitoring/objects.py @@ -3,14 +3,14 @@ from geonature.utils.env import DB from geonature.core.gn_commons.models import TModules -class MonitoringModule(MonitoringObject): +class MonitoringModule(MonitoringObject): def get(self, param_value=None, param_name=None, depth=0): - ''' - pour récupérer le module sans l'id_module mais avec le module_code - ''' + """ + pour récupérer le module sans l'id_module mais avec le module_code + """ if not param_name: - param_name = 'module_code' + param_name = "module_code" if not param_value: param_value = self._module_code MonitoringObject.get(self, param_value, param_name, depth) @@ -19,17 +19,16 @@ def get(self, param_value=None, param_name=None, depth=0): class MonitoringSite(MonitoringObjectGeom): - ''' - PATCH - pour pouvoir renseigner la table cor_site_module - avec la méthode from_dict - ''' + """ + PATCH + pour pouvoir renseigner la table cor_site_module + avec la méthode from_dict + """ def preprocess_data(self, data): - module_ids = [module.id_module for module in self._model.modules] - id_module = int(data['id_module']) + id_module = int(data["id_module"]) if id_module not in module_ids: module_ids.append(id_module) - data['modules'] = module_ids + data["modules"] = module_ids diff --git a/backend/gn_module_monitoring/monitoring/repositories.py b/backend/gn_module_monitoring/monitoring/repositories.py index 8c14ee719..fa536efa2 100644 --- a/backend/gn_module_monitoring/monitoring/repositories.py +++ b/backend/gn_module_monitoring/monitoring/repositories.py @@ -12,13 +12,11 @@ class MonitoringObject(MonitoringObjectSerializer): - def get(self, value=None, field_name=None, depth=0): - # par defaut on filtre sur l'id if not field_name: - field_name = self.config_param('id_field_name') + field_name = self.config_param("id_field_name") if not value: value = self._id @@ -28,9 +26,7 @@ def get(self, value=None, field_name=None, depth=0): try: Model = self.MonitoringModel() - req = ( - DB.session.query(Model) - ) + req = DB.session.query(Model) # Test pour mettre les relations à joined # if depth > 0: @@ -39,24 +35,21 @@ def get(self, value=None, field_name=None, depth=0): # relation_name = children_type + 's' # req = req.options(joinedload(relation_name)) - self._model = ( - req - .filter(getattr(Model, field_name) == value) - .one() - ) + self._model = req.filter(getattr(Model, field_name) == value).one() - self._id = getattr(self._model, self.config_param('id_field_name')) + self._id = getattr(self._model, self.config_param("id_field_name")) return self except Exception as e: - raise GeoNatureError('MONITORING : get_object : {}'.format(e)) + raise GeoNatureError( + f"MONITORING : get_object {self._module_code} {self._object_type} ({field_name}={value}) : {e}" + ) def process_post_data_properties(self, post_data): - # id_parent dans le cas d'heritage - properties = post_data['properties'] + properties = post_data["properties"] # id_parent = post_data.get('id_parent') # TODO remove # if id_parent: @@ -64,26 +57,24 @@ def process_post_data_properties(self, post_data): # properties[parent_id_field_name] = post_data['id_parent'] def process_synthese(self, process_module=False, limit=1000): - # test du parametre synthese - if not self.config().get('synthese'): + if not self.config().get("synthese"): return # on ne le fait pas en automatique pour les modules # le process peut être trop long # peut être fait avec une api exprès (TODO !!) - if self._object_type == 'module' and not process_module: + if self._object_type == "module" and not process_module: return - - table_name = 'v_synthese_{}'.format(self._module_code) + table_name = "v_synthese_{}".format(self._module_code) try: import_from_table( - 'gn_monitoring', + "gn_monitoring", table_name, - self.config_param('id_field_name'), - self.config_value('id_field_name'), - limit + self.config_param("id_field_name"), + self.config_value("id_field_name"), + limit, ) except ValueError as e: # warning @@ -91,24 +82,21 @@ def process_synthese(self, process_module=False, limit=1000): """Error in module monitoring, process_synthese. Function import_from_table with parameters({}, {}, {}) raises the following error : {} - """ - .format( + """.format( table_name, - self.config_param('id_field_name'), - self.config_value('id_field_name'), - e + self.config_param("id_field_name"), + self.config_value("id_field_name"), + e, ) ) - return {"message": '{}'.format(e)}, 500 + return {"message": "{}".format(e)}, 500 except Exception as e: - return {"message": '{}'.format(e)}, 500 + return {"message": "{}".format(e)}, 500 return True def create_or_update(self, post_data): - try: - # si id existe alors c'est un update self.get() @@ -124,22 +112,18 @@ def create_or_update(self, post_data): DB.session.add(self._model) DB.session.commit() - self._id = getattr(self._model, self.config_param('id_field_name')) + self._id = getattr(self._model, self.config_param("id_field_name")) self.process_synthese() return self except Exception as e: - raise GeoNatureError( - "MONITORING: create_or_update {} : {}" - .format(self, str(e)) - ) + raise GeoNatureError("MONITORING: create_or_update {} : {}".format(self, str(e))) def delete(self): - if not self._id: - raise GeoNatureError('Monitoring : delete object has no id') + raise GeoNatureError("Monitoring : delete object has no id") try: self.get() @@ -152,45 +136,37 @@ def delete(self): return monitoring_object_out except Exception as e: - raise GeoNatureError( - 'Delete {} raise error {}' - .format(self, str(e)) - ) + raise GeoNatureError("Delete {} raise error {}".format(self, str(e))) def breadcrumb(self, params): - if not self._id: return - breadcrumb = { - 'id': self._id, - 'label': self.config_param('label'), - 'description': str(self.config_value('description_field_name')), - 'module_code': self._module_code, - 'object_type': self._object_type, + "id": self._id, + "label": self.config_param("label"), + "description": str(self.config_value("description_field_name")), + "module_code": self._module_code, + "object_type": self._object_type, } - if params['parents_path']: - breadcrumb['params'] = { - 'parents_path': [parent for parent in params['parents_path']] - } + if params["parents_path"]: + breadcrumb["params"] = {"parents_path": [parent for parent in params["parents_path"]]} return breadcrumb def breadcrumbs(self, params): - breadcrumb = self.breadcrumb(params) breadcrumbs = [breadcrumb] if breadcrumb else [] - next=None + next = None - if params['parents_path']: - object_type = params.get('parents_path', []).pop() + if params["parents_path"]: + object_type = params.get("parents_path", []).pop() next = MonitoringObject(self._module_code, object_type) - id_field_name = next.config_param('id_field_name') + id_field_name = next.config_param("id_field_name") next._id = self.get_value(id_field_name) or params.get(id_field_name) next.get(0) else: @@ -202,16 +178,16 @@ def breadcrumbs(self, params): return breadcrumbs def get_list(self, args=None): - ''' - renvoie une liste d'objet serialisés - possibilité de filtrer - args arguments de requête get - get_list(request.args.to_dict()) + """ + renvoie une liste d'objet serialisés + possibilité de filtrer + args arguments de requête get + get_list(request.args.to_dict()) - TODO ajouter sort, page ou autres avec args - TODO traiter geojson ?? - TODO filtrer par module ++++ - ''' + TODO ajouter sort, page ou autres avec args + TODO traiter geojson ?? + TODO filtrer par module ++++ + """ # test si présent dans le module # sinon [] @@ -221,13 +197,11 @@ def get_list(self, args=None): Model = self.MonitoringModel() - limit = args.get('limit') + limit = args.get("limit") - order_by = args.getlist('order_by') + order_by = args.getlist("order_by") - req = ( - DB.session.query(Model) - ) + req = DB.session.query(Model) # Traitement de la liste des colonnes à retourner fields_list = args.getlist("fields") @@ -238,10 +212,9 @@ def get_list(self, args=None): else: fields_list = [field for field in fields_list if field in props] - # filtres params get for key in args: - if hasattr(Model, key) and args[key] not in ['', None, 'null', 'undefined']: + if hasattr(Model, key) and args[key] not in ["", None, "null", "undefined"]: vals = args.getlist(key) req = req.filter(getattr(Model, key).in_(vals)) @@ -253,41 +226,36 @@ def get_list(self, args=None): # order_by for s in order_by: - if '*' in s: - continue # order by number - elif s[-1] == '-': + if "*" in s: + continue # order by number + elif s[-1] == "-": req = req.order_by(getattr(Model, s[:-1]).desc()) else: req = req.order_by(getattr(Model, s)) # TODO page etc... - res = ( - req - .limit(limit) - .all() - ) + res = req.limit(limit).all() # patch order by number - out = [ - r.as_dict(fields=fields_list) - for r in res - ] + out = [r.as_dict(fields=fields_list) for r in res] # order by number # pour les cas 1.2 truc muche for s in order_by: - if '*' in s: + if "*" in s: s2 = s.replace("-", "").replace("*", "") + def extract_number(x): try: - n =x[s2].split(' ')[0].split('.') + n = x[s2].split(" ")[0].split(".") n0 = n[0] n1 = n[1] - return to_int(n0)*1e5 + to_int(n1) + return to_int(n0) * 1e5 + to_int(n1) except: return 0 + out = sorted(out, key=extract_number) return out diff --git a/backend/gn_module_monitoring/monitoring/serializer.py b/backend/gn_module_monitoring/monitoring/serializer.py index 0d888e969..75bb82758 100644 --- a/backend/gn_module_monitoring/monitoring/serializer.py +++ b/backend/gn_module_monitoring/monitoring/serializer.py @@ -1,6 +1,6 @@ -''' +""" serialiser -''' +""" import datetime import uuid from flask import current_app @@ -12,30 +12,22 @@ class MonitoringObjectSerializer(MonitoringObjectBase): - def get_parent(self): - 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_definitions.monitoring_object_instance( + self._module_code, parent_type, self.id_parent() + ).get() return self._parent def get_site_id(self): if not self._id: return - if hasattr(self._model, 'id_base_site'): + if hasattr(self._model, "id_base_site"): return self._model.id_base_site return # parent = self.get_parent() @@ -48,24 +40,24 @@ def as_dict(self, depth): def flatten_specific_properties(self, properties): # mise a plat des données spécifiques - if 'data' not in properties: + if "data" not in properties: return - data = properties.pop('data') + data = properties.pop("data") data = data if data else {} - for attribut_name in self.config_schema(type_schema='specific'): + for attribut_name in self.config_schema(type_schema="specific"): properties[attribut_name] = data.get(attribut_name) def unflatten_specific_properties(self, properties): data = {} - for attribut_name in self.config_schema('specific'): + for attribut_name in self.config_schema("specific"): val = properties.pop(attribut_name) data[attribut_name] = val if data: - properties['data'] = data + properties["data"] = data def serialize_children(self, depth): - children_types = self.config_param('children_types') + children_types = self.config_param("children_types") if not children_types: return @@ -74,7 +66,7 @@ def serialize_children(self, depth): for children_type in children_types: # attention a bien nommer les relation en children_type + 's' !!! - relation_name = children_type + 's' + relation_name = children_type + "s" if not hasattr(self._model, relation_name): continue @@ -82,9 +74,8 @@ 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_definitions.monitoring_object_instance( + self._module_code, children_type, model=child_model ) children_of_type.append(child.serialize(depth)) @@ -93,15 +84,15 @@ def serialize_children(self, depth): return children def properties_names(self): - generic = list(self.config_schema('generic').keys()) - data = ['data'] if hasattr(self._model, 'data') else [] + generic = list(self.config_schema("generic").keys()) + data = ["data"] if hasattr(self._model, "data") else [] return generic + data def serialize(self, depth=1): # TODO faire avec un as_dict propre (avec props et relationships) if depth is None: depth = 1 - depth = depth-1 + depth = depth - 1 if not self._model: # on recupère le modèle SQLA Model = self.MonitoringModel() @@ -114,7 +105,6 @@ def serialize(self, depth=1): properties = {} for field_name in self.properties_names(): - # val = self._model.__dict__.get(field_name) val = getattr(self._model, field_name) if isinstance(val, (datetime.date, uuid.UUID)): @@ -125,7 +115,6 @@ def serialize(self, depth=1): if depth >= 0: children = self.serialize_children(depth) - # processe properties self.flatten_specific_properties(properties) @@ -136,38 +125,31 @@ def serialize(self, depth=1): if not isinstance(value, list): continue - type_util = definition.get('type_util') + type_util = definition.get("type_util") # on passe d'une list d'objet à une liste d'id # si type_util est defini pour ce champs # si on a bien affaire à une liste de modèles sqla properties[key] = [ - getattr(v, id_field_name_dict[type_util]) if (isinstance(v, DB.Model) and type_util) - else v.as_dict() if (isinstance(v, DB.Model) and not type_util) + getattr(v, id_field_name_dict[type_util]) + if (isinstance(v, DB.Model) and type_util) + else v.as_dict() + if (isinstance(v, DB.Model) and not type_util) else v for v in value ] monitoring_object_dict = { - 'properties': properties, - 'object_type': self._object_type, - 'module_code': self._module_code, - 'site_id': self.get_site_id(), - 'id': self._id, + "properties": properties, + "object_type": self._object_type, + "module_code": self._module_code, + "site_id": self.get_site_id(), + "id": self._id, } - if self._object_type == 'module': - monitoring_object_dict['cruved'] = get_scopes_by_action(module_code=self._module_code) - monitoring_object_dict['cruved_objects'] = {} - monitoring_object_dict['cruved_objects']['site'] = get_scopes_by_action(module_code=self._module_code, object_code="GNM_SITES") - monitoring_object_dict['cruved_objects']['sites_group'] = get_scopes_by_action(module_code=self._module_code, object_code="GNM_GRP_SITES") - monitoring_object_dict['cruved_objects']['visite'] = get_scopes_by_action(module_code=self._module_code, object_code="GNM_VISITES") - monitoring_object_dict['cruved_objects']['observation'] = get_scopes_by_action(module_code=self._module_code, object_code="GNM_OBSERVATIONS") - - - properties['id_parent'] = to_int(self.id_parent()) - if(children): - monitoring_object_dict['children'] = children + properties["id_parent"] = to_int(self.id_parent()) + if children: + monitoring_object_dict["children"] = children return monitoring_object_dict @@ -178,7 +160,7 @@ def preprocess_data(self, data): def populate(self, post_data): # pour la partie sur les relationships mettre le from_dict dans utils_flask_sqla ??? - properties = post_data['properties'] + properties = post_data["properties"] # données spécifiques self.unflatten_specific_properties(properties) @@ -187,7 +169,7 @@ 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"): self._model.from_geofeature(post_data, True) else: self._model.from_dict(properties, True) diff --git a/backend/gn_module_monitoring/routes/__init__.py b/backend/gn_module_monitoring/routes/__init__.py index b074f0422..9a91ec296 100644 --- a/backend/gn_module_monitoring/routes/__init__.py +++ b/backend/gn_module_monitoring/routes/__init__.py @@ -1,4 +1,5 @@ from os.path import dirname, basename, isfile, join import glob + list_modules = glob.glob(join(dirname(__file__), "*.py")) -__all__ = [basename(f)[:-3] for f in list_modules if isfile(f) and not f.endswith('__init__.py')] +__all__ = [basename(f)[:-3] for f in list_modules if isfile(f) and not f.endswith("__init__.py")] diff --git a/backend/gn_module_monitoring/routes/config.py b/backend/gn_module_monitoring/routes/config.py index 9fe2ac471..f645f8cde 100644 --- a/backend/gn_module_monitoring/routes/config.py +++ b/backend/gn_module_monitoring/routes/config.py @@ -3,20 +3,15 @@ from utils_flask_sqla.response import json_resp from ..blueprint import blueprint -from ..config.repositories import ( - get_config, - get_config_frontend -) +from ..config.repositories import get_config, get_config_frontend -@blueprint.route('/config/', methods=['GET']) -@blueprint.route('/config', defaults={'module_code': None}, methods=['GET']) +@blueprint.route("/config/", methods=["GET"]) +@blueprint.route("/config", defaults={"module_code": None}, methods=["GET"]) @json_resp def get_config_api(module_code): """ - route qui renvoie la config pour un module donné + route qui renvoie la config pour un module donné """ return get_config_frontend(module_code, force=True) - - diff --git a/backend/gn_module_monitoring/routes/modules.py b/backend/gn_module_monitoring/routes/modules.py index 97667789d..e0ea12889 100644 --- a/backend/gn_module_monitoring/routes/modules.py +++ b/backend/gn_module_monitoring/routes/modules.py @@ -1,6 +1,6 @@ -''' +""" routes pour les modules de suivis... -''' +""" from flask import request from utils_flask_sqla.response import json_resp_accept_empty_list, json_resp @@ -19,44 +19,44 @@ ) -@blueprint.route('/module/', methods=['GET']) -@check_cruved_scope('R', module_code=MODULE_CODE) +@blueprint.route("/module/", methods=["GET"]) +@check_cruved_scope("R", module_code=MODULE_CODE) @json_resp def get_module_api(value): - ''' - Renvoie un module référencé par son champ module_code - par default cherche par id_module - on peut preciser field_name en parametre de requete GET - ?field_name=module_code pour avoir unmodule depuis son champs module_code - ''' + """ + Renvoie un module référencé par son champ module_code + par default cherche par id_module + on peut preciser field_name en parametre de requete GET + ?field_name=module_code pour avoir unmodule depuis son champs module_code + """ - depth = to_int(request.args.get('depth', 0)) - field_name = request.args.get('field_name', 'id_module') + depth = to_int(request.args.get("depth", 0)) + field_name = request.args.get("field_name", "id_module") module = get_module(field_name, value) module_out = [] if module: module_out = module.as_dict(depth=depth) - module_out['cruved'] = get_scopes_by_action(module_code=module.module_code) + module_out["cruved"] = get_scopes_by_action(module_code=module.module_code) return module_out -@blueprint.route('/modules', methods=['GET']) -@check_cruved_scope('R', module_code=MODULE_CODE) +@blueprint.route("/modules", methods=["GET"]) +@check_cruved_scope("R", module_code=MODULE_CODE) @json_resp_accept_empty_list def get_modules_api(): - ''' - Renvoie la liste des modules de suivi - ''' + """ + Renvoie la liste des modules de suivi + """ - depth = to_int(request.args.get('depth', 0)) + depth = to_int(request.args.get("depth", 0)) modules_out = [] modules = get_modules() for module in modules: module_out = module.as_dict(depth=depth) - module_out['cruved'] = get_scopes_by_action(module_code=module.module_code) + module_out["cruved"] = get_scopes_by_action(module_code=module.module_code) modules_out.append(module_out) diff --git a/backend/gn_module_monitoring/routes/monitoring.py b/backend/gn_module_monitoring/routes/monitoring.py index 6432d9567..089884844 100644 --- a/backend/gn_module_monitoring/routes/monitoring.py +++ b/backend/gn_module_monitoring/routes/monitoring.py @@ -1,117 +1,137 @@ -''' +""" module definissant les routes d'accès de modification des objects site, visit, observation, ... -''' +""" +from pathlib import Path +from werkzeug.exceptions import NotFound from flask import request, send_from_directory, url_for, g, current_app -from utils_flask_sqla.response import ( - json_resp, json_resp_accept_empty_list -) +import datetime as dt + +from sqlalchemy.orm import joinedload + +from utils_flask_sqla.response import json_resp, json_resp_accept_empty_list +from utils_flask_sqla.response import to_csv_resp, to_json_resp +from utils_flask_sqla_geo.generic import GenericTableGeo +from utils_flask_sqla.generic import serializeQuery -from werkzeug.exceptions import NotFound from ..blueprint import blueprint from geonature.core.gn_permissions.decorators import check_cruved_scope from geonature.core.gn_commons.models.base import TModules -from geonature.core.gn_permissions.models import TObjects +from geonature.core.gn_permissions.models import TObjects, Permission -# from geonature.utils.errors import GeoNatureError +from geonature.utils.env import DB, ROOT_DIR +import geonature.utils.filemanager as fm + +from gn_module_monitoring import MODULE_CODE from ..monitoring.definitions import monitoring_definitions, MonitoringPermissions_dict from ..modules.repositories import get_module from ..utils.utils import to_int from ..config.repositories import get_config -from gn_module_monitoring import MODULE_CODE -from utils_flask_sqla_geo.generic import GenericTableGeo -from geonature.utils.env import DB, ROOT_DIR -import datetime as dt -from utils_flask_sqla.response import to_csv_resp, to_json_resp -from utils_flask_sqla.generic import serializeQuery -import geonature.utils.filemanager as fm -from pathlib import Path - @blueprint.url_value_preprocessor def set_current_module(endpoint, values): - - requested_module = values.get("module_code") or MODULE_CODE - g.current_module = TModules.query.filter_by(module_code=requested_module).first_or_404( - f"No module with code {requested_module} {endpoint}" + # recherche du sous-module courrant + requested_module_code = values.get("module_code") or MODULE_CODE + current_module = ( + TModules.query.options(joinedload(TModules.objects)) + .filter_by(module_code=requested_module_code) + .first_or_404(f"No module with code {requested_module_code} {endpoint}") ) + g.current_module = current_module + # recherche de l'object de permission courrant object_type = values.get("object_type") if object_type: - request_code_object = MonitoringPermissions_dict.get(object_type, 'ALL') - g.current_object = TObjects.query.filter_by(code_object=request_code_object).first_or_404( - f"No permission object with code {request_code_object} {endpoint}" + requested_permission_object_code = MonitoringPermissions_dict.get(object_type) + + if requested_permission_object_code is None: + # error ? + return + + # Test si l'object de permission existe + requested_permission_object = TObjects.query.filter_by( + code_object=requested_permission_object_code + ).first_or_404( + f"No permission object with code {requested_permission_object_code} {endpoint}" ) -@blueprint.route('/object///', methods=['GET']) + # si l'object de permission est associé au module => il devient l'objet courant + # - sinon se sera 'ALL' par defaut + for module_perm_object in current_module.objects: + if module_perm_object == requested_permission_object: + g.current_object = requested_permission_object + return + + +@blueprint.route("/object///", methods=["GET"]) @blueprint.route( - '/object//', - defaults={'id': None}, - methods=['GET']) + "/object//", defaults={"id": None}, methods=["GET"] +) @blueprint.route( - '/object/module', - defaults={'module_code': None, 'object_type': 'module', 'id': None}, - methods=['GET']) -@check_cruved_scope('R') + "/object/module", + defaults={"module_code": None, "object_type": "module", "id": None}, + methods=["GET"], +) +@check_cruved_scope("R") @json_resp def get_monitoring_object_api(module_code, object_type, id): - ''' - renvoie un object, à partir de type de l'object et de son id + """ + renvoie un object, à partir de type de l'object et de son id - :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 + :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 requis - :rtype: dict - ''' + :return: renvoie l'object requis + :rtype: dict + """ # field_name = param.get('field_name') # value = module_code if object_type == 'module' get_config(module_code, force=True) - depth = to_int(request.args.get('depth', 1)) + depth = to_int(request.args.get("depth", 1)) return ( - monitoring_definitions - .monitoring_object_instance(module_code, object_type, id) - .get(depth=depth) + monitoring_definitions.monitoring_object_instance(module_code, object_type, id).get( + depth=depth + ) # .get(value=value, field_name = field_name) .serialize(depth) ) def create_or_update_object_api(module_code, object_type, id): - ''' - 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)) + """ + 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()) - module = get_module('module_code', module_code) + module = get_module("module_code", module_code) # on rajoute id_module s'il n'est pas renseigné par défaut ?? - post_data['properties']['id_module'] = module.id_module + post_data["properties"]["id_module"] = module.id_module return ( monitoring_definitions.monitoring_object_instance(module_code, object_type, id) @@ -119,13 +139,15 @@ def create_or_update_object_api(module_code, object_type, id): .serialize(depth) ) + # update object -@blueprint.route('object///', methods=['PATCH']) +@blueprint.route("object///", methods=["PATCH"]) @blueprint.route( - '/object//module', - defaults={'id': None, 'object_type': 'module'}, - methods=['PATCH']) -@check_cruved_scope('U') + "/object//module", + defaults={"id": None, "object_type": "module"}, + methods=["PATCH"], +) +@check_cruved_scope("U") @json_resp def update_object_api(module_code, object_type, id): get_config(module_code, force=True) @@ -133,12 +155,15 @@ def update_object_api(module_code, object_type, id): # create object -@blueprint.route('object//', defaults={'id': None}, methods=['POST']) @blueprint.route( - '/object/module', - defaults={'module_code': None, 'object_type': 'module', 'id': None}, - methods=['POST']) -@check_cruved_scope('C') + "object//", defaults={"id": None}, methods=["POST"] +) +@blueprint.route( + "/object/module", + defaults={"module_code": None, "object_type": "module", "id": None}, + methods=["POST"], +) +@check_cruved_scope("C") @json_resp def create_object_api(module_code, object_type, id): get_config(module_code, force=True) @@ -146,75 +171,64 @@ def create_object_api(module_code, object_type, id): # delete -@blueprint.route('object///', methods=['DELETE']) +@blueprint.route("object///", methods=["DELETE"]) @blueprint.route( - '/object//module', - defaults={'id': None, 'object_type': 'module'}, - methods=['DELETE']) -@check_cruved_scope('D') + "/object//module", + defaults={"id": None, "object_type": "module"}, + methods=["DELETE"], +) +@check_cruved_scope("D") @json_resp def delete_object_api(module_code, object_type, id): - get_config(module_code, force=True) - return ( - monitoring_definitions - .monitoring_object_instance(module_code, object_type, id) - .delete() - ) + return monitoring_definitions.monitoring_object_instance(module_code, object_type, id).delete() # breadcrumbs -@blueprint.route('breadcrumbs///', methods=['GET']) -@blueprint.route('breadcrumbs//', - defaults={'id': None}, - methods=['GET'] +@blueprint.route("breadcrumbs///", methods=["GET"]) +@blueprint.route( + "breadcrumbs//", defaults={"id": None}, methods=["GET"] ) @blueprint.route( - '/breadcrumbs//module', - defaults={'id': None, 'object_type': 'module'}, - methods=['GET']) -@check_cruved_scope('R') + "/breadcrumbs//module", + defaults={"id": None, "object_type": "module"}, + methods=["GET"], +) +@check_cruved_scope("R") @json_resp def breadcrumbs_object_api(module_code, object_type, id): - get_config(module_code, force=True) query_params = dict(**request.args) - query_params['parents_path'] = request.args.getlist('parents_path') + query_params["parents_path"] = request.args.getlist("parents_path") return ( - monitoring_definitions - .monitoring_object_instance(module_code, object_type, id) + monitoring_definitions.monitoring_object_instance(module_code, object_type, id) .get() .breadcrumbs(query_params) ) # listes pour les formulaires par exemple -@blueprint.route('list//', methods=['GET']) -@check_cruved_scope('R') +@blueprint.route("list//", methods=["GET"]) +@check_cruved_scope("R") @json_resp_accept_empty_list def list_object_api(module_code, object_type): - get_config(module_code, force=True) - return ( - monitoring_definitions - .monitoring_object_instance(module_code, object_type) - .get_list(request.args) + return monitoring_definitions.monitoring_object_instance(module_code, object_type).get_list( + request.args ) # mise à jour de la synthèse -@blueprint.route('synthese/', methods=['POST']) -@check_cruved_scope('E') +@blueprint.route("synthese/", methods=["POST"]) +@check_cruved_scope("E") @json_resp def update_synthese_api(module_code): - get_config(module_code, force=True) return ( - monitoring_definitions - .monitoring_object_instance(module_code, 'module') + monitoring_definitions.monitoring_object_instance(module_code, "module") .get() .process_synthese(process_module=True) ) @@ -222,8 +236,8 @@ def update_synthese_api(module_code): # export add mje # export all observations -@blueprint.route('/exports/csv//', methods=['GET']) -@check_cruved_scope('R') +@blueprint.route("/exports/csv//", methods=["GET"]) +@check_cruved_scope("R") def export_all_observations(module_code, method): """ Export all data in csv of a custom module view @@ -241,8 +255,7 @@ def export_all_observations(module_code, method): view = GenericTableGeo( tableName=f"v_export_{module_code.lower()}_{method}", schemaName="gn_monitoring", - engine=DB.engine - + engine=DB.engine, ) columns = view.tableDef.columns q = DB.session.query(*columns) @@ -259,11 +272,12 @@ def export_all_observations(module_code, method): data=serializeQuery(data, q.column_descriptions), separator=";", columns=[ - db_col.key for db_col in columns if db_col.key != 'geom' + db_col.key for db_col in columns if db_col.key != "geom" ], # Exclude the geom column from CSV ) -@blueprint.route('/exports/pdf///', methods=['POST']) + +@blueprint.route("/exports/pdf///", methods=["POST"]) def post_export_pdf(module_code, object_type, id): """ Export the fiche individu as a PDF file. @@ -271,23 +285,22 @@ def post_export_pdf(module_code, object_type, id): Need to set a template in sub-module. """ - depth = to_int(request.args.get('depth', 0)) - monitoring_object= ( - monitoring_definitions - .monitoring_object_instance(module_code, object_type, id - ).get() + depth = to_int(request.args.get("depth", 0)) + monitoring_object = ( + monitoring_definitions.monitoring_object_instance(module_code, object_type, id) + .get() .serialize(depth) ) df = { - 'module_code': module_code, - 'monitoring_object': monitoring_object, - 'extra_data': request.json['extra_data'], - 'static_pdf_dir': url_for('media', filename=f"monitorings/{module_code}/exports/pdf/"), - 'map_image': request.json['map'] + "module_code": module_code, + "monitoring_object": monitoring_object, + "extra_data": request.json["extra_data"], + "static_pdf_dir": url_for("media", filename=f"monitorings/{module_code}/exports/pdf/"), + "map_image": request.json["map"], } - template = request.json['template'] + template = request.json["template"] pdf_file = fm.generate_pdf( f"{module_code}/exports/pdf/{template}", diff --git a/backend/gn_module_monitoring/utils/utilsjsonschema.py b/backend/gn_module_monitoring/utils/utilsjsonschema.py index d5bf48c55..e784a4edd 100644 --- a/backend/gn_module_monitoring/utils/utilsjsonschema.py +++ b/backend/gn_module_monitoring/utils/utilsjsonschema.py @@ -5,8 +5,8 @@ def validable(cls): """ - Décorateur de classe - Ajoute une fonction de valider des jsonschema sur un colonne JSONB + Décorateur de classe + Ajoute une fonction de valider des jsonschema sur un colonne JSONB """ def is_valid(self, col_name, file_schema_path): @@ -33,7 +33,7 @@ def is_valid(self, col_name, file_schema_path): return # test si l'objet json est conforme au schema - with open(file_schema_path, 'r') as f: + with open(file_schema_path, "r") as f: schema = load(f) try: diff --git a/docs/MAJ.md b/docs/MAJ.md index 0db714619..b27fa764d 100644 --- a/docs/MAJ.md +++ b/docs/MAJ.md @@ -3,64 +3,52 @@ Avant la mise à jour ==================== -- Copier la configuration du module -``` sh -cp ~/gn_module_monitoring_old/config/conf_gn_module.toml ~/geonature/config/monitorings_config.toml -``` +- Si vous ne l'avez pas encore fait, copier l'éventuelle configuration du module dans le dossier centralisé de GeoNature (depuis sa version 2.12) : + ``` sh + cp ~/gn_module_monitoring/config/conf_gn_module.toml ~/geonature/config/monitorings_config.toml + ``` -- Si vous ne l'avez pas encore fait, copier les configuration des modules dans le dossier `media` de geonature - - attention à bien reproduire la commande: +- Si vous ne l'avez pas encore fait, copier les configurations des sous-modules dans le dossier `media` de geonature + - attention à bien reproduire la commande : - source : `gn_module_monitoring_old/config/monitoring` (sans `s`) * destination : `~/geonature/config/monitorings` (avec `s`) -```sh -cp -R ~/gn_module_monitoring_old/config/monitoring/* ~/geonature/backend/media/monitorings -rm -R geonature/config/monitorings/generic -``` + ```sh + cp -R ~/gn_module_monitoring_old/config/monitoring/* ~/geonature/backend/media/monitorings + rm -R geonature/config/monitorings/generic + ``` -* à adapter si l'emplacement du dossier media à été modifié + Les chemins sont à adapter si l'emplacement du dossier `media` de GeoNature à été modifié. -Gestion des fichiers avec git -============================= - -Si vous souhaitez installer et mettre à jour le module avec git : - -* à l'installation - * récupérer le code du module avec - `git clone https://github.com/PnX-SI/gn_module_monitoring/` - * passer au tag voulu : `git co 0.2.6` -* à la mise à jour - * `git pull` - * passer au tag voulu : `git co 0.2.7` - -Méthode classique -================= +Mise à jour du module +===================== * Téléchargez la nouvelle version du module -```sh -wget https://github.com/PnX-SI/gn_module_monitoring/archive/X.Y.Z.zip -unzip X.Y.Z.zip -rm X.Y.Z.zip -``` + ```sh + wget https://github.com/PnX-SI/gn_module_monitoring/archive/X.Y.Z.zip + unzip X.Y.Z.zip + rm X.Y.Z.zip + ``` * Renommez l'ancien et le nouveau répertoire -```sh -mv /home/`whoami`/gn_module_monitoring /home/`whoami`/gn_module_monitoring_old -mv /home/`whoami`/gn_module_monitoring-X.Y.Z /home/`whoami`/gn_module_monitoring -``` + ```sh + mv ~/gn_module_monitoring ~/gn_module_monitoring_old + mv ~/gn_module_monitoring-X.Y.Z ~/gn_module_monitoring + ``` * Lancez la mise à jour du module -```sh - source ~/geonature/backend/venv/bin/activate - geonature install-gn-module gn_module_monitoring MONITORINGS - sudo systemctl reload geonature -``` + ```sh + source ~/geonature/backend/venv/bin/activate + geonature install-gn-module gn_module_monitoring MONITORINGS + sudo systemctl reload geonature + ``` + * Recréer les vues alimentant la synthèse de GeoNature -```sh - geonature monitorings process_csv -``` \ No newline at end of file + ```sh + geonature monitorings process_csv + ``` diff --git a/docs/changelog.md b/docs/changelog.md index ee29f941a..c504f2e3a 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,6 +1,25 @@ CHANGELOG ========= +0.7.0 (2023-08-23) +------------------ + +Nécessite la version 2.13.0 (ou plus) de GeoNature + +**Evolutions** + +* Compatibilité avec GeoNature 2.13.0 et la refonte des permissions, en définissant les permissions disponibles du module (#232) +* Gestion des permissions disponibles des sous-modules lors de leur installation et création de la commande `update_module_available_permissions` permettant de les mettre à jour (#236) +* Récupération des permissions depuis le service `ModulesService` de GeoNature + +**⚠️ Notes de version** + +* Si elle est renseignée dans la configuration de vos sous-modules, la variable `permission_objects` est à déplacer du fichier `module.json` au fichier `config.json` de ces sous-modules +* Après mise à jour du module, utiliser la commande pour générer les permissions disponibles pour les sous-modules déjà installés + ``` + geonature monitorings update_module_available_permissions + ``` + 0.6.0 (2023-05-23) ------------------ @@ -30,12 +49,13 @@ Nécessite GeoNature version 2.12.0 (ou plus) * L'installation des sous-modules se fait désormais en deux temps : * Copie du répertoire de configuration + ```sh + cp /backend/media/monitorings/ + ``` * Installation du sous-module avec la commande dédiée - - ```sh - cp /backend/media/monitoring/ - geonature monitorings install - ``` + ```sh + geonature monitorings install + ``` 0.5.0 (2023-03-29) ------------------ diff --git a/docs/sous_module.md b/docs/sous_module.md index 69eaff4be..5fb283852 100644 --- a/docs/sous_module.md +++ b/docs/sous_module.md @@ -43,8 +43,8 @@ title: 'Création d''un sous-module' serviront aux exports `csv` * le nommage des vues doit être `"v_export__` - * `` est une chaine de caratère qui permet de - caractriser différentes vues et différents exports pour + * `` est une chaine de caractère qui permet de + caractériser différentes vues et différents exports pour un module * `pdf` * *fichiers html/img/css* @@ -56,7 +56,7 @@ fichier de même nom présent dans le répertoire `config/monitoring/generic`. Le fichier `img.jpg` servira de vignette du sous-module sur la page -d'accueil du module Monitorings. Le format paysage est à privilégier. +d'accueil du module Monitoring. Le format paysage est à privilégier. Pour chacune un lien symbolique est créé automatiquement dans le répertoire `media/monitorings/.jpg` de GeoNature. @@ -82,6 +82,15 @@ Dans le fichier `config.json` : } ``` +* `permission_objects` : liste des objets permissions à associer au + module. Elle peut contenir les valeurs suivantes + `["GNM_MODULES", "GNM_GRP_SITES", "GNM_SITES", "GNM_VISITES", "GNM_OBSERVATIONS"]` + + * Par exemple, pour les sites, par défaut l'API va vérifier les droits pour l'objet de permission `ALL` associé au sous-module. + + * Si des permissions sont définies pour ce module et l'object `GNM_SITES`, l'API des sites ira vérifier les droits en rapport avec `GNM_SITES`. + + # Configuration des objets Dans le fichier `module.json`, deux variables doivent obligatoirement @@ -92,11 +101,7 @@ Dans le fichier `module.json`, deux variables doivent obligatoirement * `module_desc`: une description succinte du module. Une variable optionnelle permet de configurer les objets faisant -l'objet de permission: - -* `permission_objects`: liste des objets permissions à associer au - module. Elle peut contenir les valeurs suivantes - `["GNM_GRP_SITES", "GNM_SITES", "GNM_VISITES", "GNM_OBSERVATIONS"]` +l'objet de permission : Dans le cas général (`module.json`, `site.json`, `visit.json`, `observation.json`) on peut redéfinir au besoin certaines variables. @@ -287,7 +292,7 @@ Il est important d'ajouter `"type_util": "nomenclature",`. ``` La variable `"id_list": "__MODULE.ID_LIST_TAXONOMY"` définit la -liste de taxon. +liste de taxons. Il est important d'ajouter `"type_util": "taxonomy",`. @@ -427,29 +432,29 @@ Par exemple : Il est possible de définir des paramètre qui peuvent dépendre de plusieurs variables. La valeur de ce paramètre est alors une chaîne de -caractère qui définie une fonction, qui utilise les variables suivantes +caractère qui définit une fonction, qui utilise les variables suivantes -**Ce cas n'est pris en compte que pour les composant spécifique, ou +**Ce cas n'est pris en compte que pour les composants spécifiques, ou pour les composants redéfinis dans `specific`** -* `value`: les valeur du formulaire -* `attribut_name`: du composant concerné -* `meta`: un dictionnaire de données additionelles, et fourni au +* `value` : les valeurs du formulaire +* `attribut_name` : du composant concerné +* `meta` : un dictionnaire de données additionelles, et fourni au composant dynamicFormGenerator, il peut contenir des données sur * la nomenclature (pour avoir les valeurs des nomenclature à partir des id, ici un dictionnaire avec `id_nomenclature` comme clés. - * `bChainInput` si on enchaine les releves - * etc.. à redéfinir selon les besoin + * `bChainInput` si on enchaine les relevés + * etc.. à redéfinir selon les besoins La chaine de caractère qui décrit la fonction doit être de la forme -suivante: +suivante : ```json "hidden": "({value, attribut_name, }) => { return value.id == 't' }" ``` -Le format JSON ne permet pas les saut de ligne dans les chaines de +Le format JSON ne permet pas les sauts de ligne dans les chaines de caractère, et pour avoir plus de lisibilité, quand la fonction est plus complexe, on peut aussi utiliser un tableau de chaine de caractères : @@ -461,7 +466,7 @@ complexe, on peut aussi utiliser un tableau de chaine de caractères : ] ``` -Le lignes seront coléés entre elles avec l'ajout de saut de lignes +Le lignes seront collées entre elles avec l'ajout de saut de lignes (caractère [n]{.title-ref}). Il faut être certain de sa fonction. @@ -469,7 +474,7 @@ Il faut être certain de sa fonction. Exemples : * Afficher le composant `test2` et le rendre obligatoire seulement si - `test1` a pour valeur `t`: + `test1` a pour valeur `t` : ```json "specific": { @@ -513,7 +518,7 @@ valeur des variables en fonction d'autres variables, on peut définir On peut y définir une fonction qui sera appelée chaque fois que le formulaire change. -Un exemple (`module.json` du module test): +Un exemple (`module.json` du module test) : ```json { @@ -614,11 +619,11 @@ NB : pour ajouter une popup sur la liste des sites, éditez le fichier # Gestion des droits Actuellement le CRUVED est implémenté de manière partielle au niveau du -module MONITORINGS. Il n'y a actuellement pas de vérification des +module MONITORING. Il n'y a actuellement pas de vérification des portées, les droits s'appliquent sur toutes les données. Si on définit un CRUVED sur un sous-module, alors cela surcouche pour ce -sous-module le CRUVED définit au niveau de tout le module Monitorings. +sous-module le CRUVED défini au niveau de tout le module Monitoring. Par défaut les valeurs définies du CRUVED sont : @@ -648,7 +653,7 @@ Il est possible de configurer des exports (CSV ou PDF). Les fichiers de template (`.html`) et assets (images, style, etc..) pour l'export PDF sont à placer dans le dossier `/exports/pdf/` -* Dans le fichier de config d'un object (par exemple +* Dans le fichier de config d'un objet (par exemple `sites_group.json`: * ajouter la variable `export_pdf`: @@ -661,11 +666,11 @@ l'export PDF sont à placer dans le dossier `/exports/pdf/` ] ``` -* Dans les fichiers template on a accès à la variable `data` un +* Dans les fichiers template on a accès à la variable `data`, un dictionnaire contenant : * `static_pdf_dir` : chemin du dossier des assets de l'export pdf * `map_image` : l'image tirée de la carte leaflet - * `monitoring_object.properties`: propriété de l'objet courant + * `monitoring_object.properties` : propriété de l'objet courant * La commande `geonature monitorings process_export_pdf ` permet de : * placer les fichier de template en `.html` (lien symbolique) dans @@ -676,7 +681,7 @@ l'export PDF sont à placer dans le dossier `/exports/pdf/` ## CSV -les fichiers `.sql` qui définissent les vues pour l'export CSV sont +Les fichiers `.sql` qui définissent les vues pour l'export CSV sont placés dans le dossier `/exports/csv/`. * Dans le fichier de config du module (`module.json`) ou d'un objet @@ -697,5 +702,5 @@ placés dans le dossier `/exports/csv/`. Dans ce cas il faut que la vue ait un champ `id_dataset` * La commande `geonature monitorings process_export_csv ` permet de : - * jouer tous les fichiers SQL de ce répertoire + * exécuter tous les fichiers SQL de ce répertoire * les vues doivent être nommées `v_export__` diff --git a/frontend/.prettierignore b/frontend/.prettierignore new file mode 100644 index 000000000..b512c09d4 --- /dev/null +++ b/frontend/.prettierignore @@ -0,0 +1 @@ +node_modules \ No newline at end of file diff --git a/frontend/.prettierrc b/frontend/.prettierrc new file mode 100644 index 000000000..6365e3241 --- /dev/null +++ b/frontend/.prettierrc @@ -0,0 +1,9 @@ +{ + "printWidth": 100, + "singleQuote": true, + "useTabs": false, + "tabWidth": 2, + "semi": true, + "bracketSpacing": true, + "trailingComma" : "es5" +} diff --git a/frontend/app/class/monitoring-object-base.ts b/frontend/app/class/monitoring-object-base.ts index 520b50e40..c2ef2dc2e 100644 --- a/frontend/app/class/monitoring-object-base.ts +++ b/frontend/app/class/monitoring-object-base.ts @@ -1,9 +1,9 @@ -import { Observable, of } from "rxjs"; -import { concatMap } from "rxjs/operators"; -import { threadId } from "worker_threads"; -import { forkJoin } from "rxjs"; -import { MonitoringObjectService } from "../services/monitoring-object.service"; -import { Utils } from "../utils/utils"; +import { Observable, of } from 'rxjs'; +import { concatMap } from 'rxjs/operators'; +import { threadId } from 'worker_threads'; +import { forkJoin } from 'rxjs'; +import { MonitoringObjectService } from '../services/monitoring-object.service'; +import { Utils } from '../utils/utils'; export class MonitoringObjectBase { moduleCode: string; @@ -39,14 +39,9 @@ export class MonitoringObjectBase { public _objService: MonitoringObjectService; - constructor( - moduleCode: string, - objectType: string, - id, - objService: MonitoringObjectService - ) { + constructor(moduleCode: string, objectType: string, id, objService: MonitoringObjectService) { if (!moduleCode) { - throw new Error("Monitoring object sans moduleCode"); + throw new Error('Monitoring object sans moduleCode'); } this.objectType = objectType; this.moduleCode = moduleCode; @@ -63,62 +58,59 @@ export class MonitoringObjectBase { } testPremiereLettreVoyelle(s) { - return s && s[0] && "aeéiouy".includes(s[0].toLowerCase()); + return s && s[0] && 'aeéiouy'.includes(s[0].toLowerCase()); } labelArtDef() { return ( - (this.testPremiereLettreVoyelle(this.configParam("label")) + (this.testPremiereLettreVoyelle(this.configParam('label')) ? "l'" - : this.configParam("genre") == "F" - ? "la " - : "le ") + this.configParam("label").toLowerCase() + : this.configParam('genre') == 'F' + ? 'la ' + : 'le ') + this.configParam('label').toLowerCase() ); } labelDu() { const labelDu = - (this.testPremiereLettreVoyelle(this.configParam("label")) + (this.testPremiereLettreVoyelle(this.configParam('label')) ? "de l'" - : this.configParam("genre") == "F" - ? "de la " - : "du ") + this.configParam("label").toLowerCase(); + : this.configParam('genre') == 'F' + ? 'de la ' + : 'du ') + this.configParam('label').toLowerCase(); return labelDu; } labelArtUndef(newObj = false) { - const strNew = - (newObj && this.configParam("genre") == "F" ? "nouvelle " : "nouveau ") || - ""; + const strNew = (newObj && this.configParam('genre') == 'F' ? 'nouvelle ' : 'nouveau ') || ''; return ( - (this.configParam("genre") == "F" ? `une ` : `un `) + + (this.configParam('genre') == 'F' ? `une ` : `un `) + strNew + - this.configParam("label").toLowerCase() + this.configParam('label').toLowerCase() ); } initTemplate() { - this.template["export_pdf"] = this.configParam("export_pdf"); - this.template["export_csv"] = this.configParam("export_csv"); - this.template["color"] = this.configParam("color"); - this.template["idTableLocation"] = this.configParam("id_table_location"); - this.template["label"] = this.configParam("label"); - this.template["label_art_def"] = this.labelArtDef(); - this.template["label_art_undef"] = this.labelArtUndef(); - this.template["label_art_undef_new"] = this.labelArtUndef(true); - this.template["label_list"] = - this.configParam("label_list") || this.configParam("label") + "s"; + this.template['export_pdf'] = this.configParam('export_pdf'); + this.template['export_csv'] = this.configParam('export_csv'); + this.template['color'] = this.configParam('color'); + this.template['idTableLocation'] = this.configParam('id_table_location'); + this.template['label'] = this.configParam('label'); + this.template['label_art_def'] = this.labelArtDef(); + this.template['label_art_undef'] = this.labelArtUndef(); + this.template['label_art_undef_new'] = this.labelArtUndef(true); + this.template['label_list'] = this.configParam('label_list') || this.configParam('label') + 's'; // this.template["title"] = this.title(); - this.template["uuid"] = this.paramValue("uuid_field_name"); - this.template["description"] = this.description(); + this.template['uuid'] = this.paramValue('uuid_field_name'); + this.template['description'] = this.description(); - this.template["fieldLabels"] = this.fieldLabels(); - this.template["fieldNames"] = this.fieldNames("display_properties"); - this.template["fieldDefinitions"] = this.fieldDefinitions(); - this.template["fieldNamesList"] = this.fieldNames("display_list"); + this.template['fieldLabels'] = this.fieldLabels(); + this.template['fieldNames'] = this.fieldNames('display_properties'); + this.template['fieldDefinitions'] = this.fieldDefinitions(); + this.template['fieldNamesList'] = this.fieldNames('display_list'); } setConfig() { @@ -135,9 +127,7 @@ export class MonitoringObjectBase { this.userCruvedObject = data.cruved_objects; this.properties = data.properties || {}; this.geometry = data.geometry; - this.id = - this.id || - (this.properties && this.properties[this.configParam("id_field_name")]); + this.id = this.id || (this.properties && this.properties[this.configParam('id_field_name')]); this.medias = data.medias; this.siteId = data.site_id; this.idTableLocation = data.id_table_location; @@ -146,11 +136,7 @@ export class MonitoringObjectBase { idFieldName() { return this._objService .configService() - .configModuleObjectParam( - this.moduleCode, - this.objectType, - "id_field_name" - ); + .configModuleObjectParam(this.moduleCode, this.objectType, 'id_field_name'); } parentId(parentType = null) { @@ -165,15 +151,11 @@ export class MonitoringObjectBase { ? null : this._objService .configService() - .configModuleObjectParam( - this.moduleCode, - parentType, - "id_field_name" - ); + .configModuleObjectParam(this.moduleCode, parentType, 'id_field_name'); } resolveProperty(elem, val): Observable { - if (elem.type_widget === "date" || (elem.type_util === "date" && val)) { + if (elem.type_widget === 'date' || (elem.type_util === 'date' && val)) { val = Utils.formatDate(val); } @@ -198,8 +180,7 @@ export class MonitoringObjectBase { return forkJoin(observables).pipe( concatMap((resolvedProperties) => { for (const attribut_name of Object.keys(resolvedProperties)) { - this.resolvedProperties[attribut_name] = - resolvedProperties[attribut_name]; + this.resolvedProperties[attribut_name] = resolvedProperties[attribut_name]; } return of(true); }) @@ -213,16 +194,12 @@ export class MonitoringObjectBase { } cruved(c = null) { - const cruved = this.configParam("cruved") || {}; - return c - ? ![undefined, null].includes(cruved[c]) - ? cruved[c] - : 1 - : cruved; + const cruved = this.configParam('cruved') || {}; + return c ? (![undefined, null].includes(cruved[c]) ? cruved[c] : 1) : cruved; } childrenTypes(configParam: string = null): Array { - let childrenTypes = this.configParam("children_types") || []; + let childrenTypes = this.configParam('children_types') || []; if (configParam) { childrenTypes = childrenTypes.filter((TypeChildren) => { @@ -233,7 +210,7 @@ export class MonitoringObjectBase { } uniqueChildrenName() { - const childrenTypes = this.configParam("children_types") || []; + const childrenTypes = this.configParam('children_types') || []; if (childrenTypes.length === 1) { return this.child0(childrenTypes[0]).template.label_list; @@ -241,11 +218,11 @@ export class MonitoringObjectBase { } hasChildren() { - return !!this.configParam("children_types"); + return !!this.configParam('children_types'); } uniqueChildrenType() { - const childrenTypes = this.configParam("children_types") || []; + const childrenTypes = this.configParam('children_types') || []; if (childrenTypes.length === 1) { return childrenTypes[0]; @@ -253,7 +230,7 @@ export class MonitoringObjectBase { } parentTypes() { - return this.configParam("parent_types"); + return this.configParam('parent_types'); } parentType() { @@ -288,7 +265,7 @@ export class MonitoringObjectBase { childsLabel() { return Utils.mapArrayToDict(this.childrenTypes(), (childrenType) => { - return this.child0(childrenType).configParam("label"); + return this.child0(childrenType).configParam('label'); }); } @@ -313,15 +290,13 @@ export class MonitoringObjectBase { } description() { - let description = this.paramValue("description_field_name", true); + let description = this.paramValue('description_field_name', true); return description; } titleHTML(bEdit = false) { let description = this.description(); - description = description - ? `${description}` - : ""; + description = description ? `${description}` : ''; const text = bEdit ? this.id ? `Modification ${this.labelDu()} ${description}` @@ -331,23 +306,19 @@ export class MonitoringObjectBase { return text.trim(); } - schema(typeSchema = "all"): Object { - return this._objService - .configService() - .schema(this.moduleCode, this.objectType, typeSchema); + schema(typeSchema = 'all'): Object { + return this._objService.configService().schema(this.moduleCode, this.objectType, typeSchema); } change() { - return this._objService - .configService() - .change(this.moduleCode, this.objectType); + return this._objService.configService().change(this.moduleCode, this.objectType); } - fieldNames(typeDisplay = "") { - if (["display_properties", "display_list"].includes(typeDisplay)) { + fieldNames(typeDisplay = '') { + if (['display_properties', 'display_list'].includes(typeDisplay)) { return this.configParam(typeDisplay); } - if (typeDisplay === "schema") { + if (typeDisplay === 'schema') { return Object.keys(this.schema()); } } @@ -357,7 +328,7 @@ export class MonitoringObjectBase { const schema = this.schema(); const fieldLabels = {}; for (const key of Object.keys(schema)) { - fieldLabels[key] = schema[key]["attribut_label"]; + fieldLabels[key] = schema[key]['attribut_label']; } return fieldLabels; } @@ -366,20 +337,20 @@ export class MonitoringObjectBase { const schema = this.schema(); const fieldDefinitions = {}; for (const key of Object.keys(schema)) { - fieldDefinitions[key] = schema[key]["definition"]; + fieldDefinitions[key] = schema[key]['definition']; } return fieldDefinitions; } geoFeature() { // patch - this.resolvedProperties["object_type"] = this.objectType; - this.resolvedProperties["description"] = this.description(); + this.resolvedProperties['object_type'] = this.objectType; + this.resolvedProperties['description'] = this.description(); return { id: this.id, object_type: this.objectType, - type: "Feature", + type: 'Feature', geometry: this.geometry, properties: this.resolvedProperties, }; @@ -394,11 +365,9 @@ export class MonitoringObjectBase { navigateToAddChildren(childrenType = null, id = null) { const queryParamsAddChildren = {}; queryParamsAddChildren[this.idFieldName()] = this.id || id; - queryParamsAddChildren["parents_path"] = this.parentsPath.concat( - this.objectType - ); + queryParamsAddChildren['parents_path'] = this.parentsPath.concat(this.objectType); this._objService.navigate( - "create_object", + 'create_object', this.moduleCode, childrenType || this.uniqueChildrenType(), null, @@ -407,42 +376,25 @@ export class MonitoringObjectBase { } navigateToDetail(id = null) { - this._objService.navigate( - "object", - this.moduleCode, - this.objectType, - id || this.id, - { - parents_path: this.parentsPath, - } - ); + this._objService.navigate('object', this.moduleCode, this.objectType, id || this.id, { + parents_path: this.parentsPath, + }); } navigateToParent() { // cas module - if (this.objectType.includes("module")) { + if (this.objectType.includes('module')) { this.navigateToDetail(); // autres cas } else { const parentType = this.parentType(); this.parentsPath.pop(); - const parent = new this.myClass( - this.moduleCode, - parentType, - null, - this._objService - ); + const parent = new this.myClass(this.moduleCode, parentType, null, this._objService); const parentId = this.properties[parent.idFieldName()]; - this._objService.navigate( - "object", - this.moduleCode, - parentType, - parentId, - { - parents_path: this.parentsPath, - } - ); + this._objService.navigate('object', this.moduleCode, parentType, parentId, { + parents_path: this.parentsPath, + }); } } } diff --git a/frontend/app/class/monitoring-object.ts b/frontend/app/class/monitoring-object.ts index 901f0d28d..93a48a91e 100644 --- a/frontend/app/class/monitoring-object.ts +++ b/frontend/app/class/monitoring-object.ts @@ -1,20 +1,15 @@ -import { Observable, of, forkJoin } from "rxjs"; -import { mergeMap, concatMap } from "rxjs/operators"; +import { Observable, of, forkJoin } from 'rxjs'; +import { mergeMap, concatMap } from 'rxjs/operators'; -import { MonitoringObjectService } from "../services/monitoring-object.service"; -import { Utils } from "../utils/utils"; +import { MonitoringObjectService } from '../services/monitoring-object.service'; +import { Utils } from '../utils/utils'; -import { MonitoringObjectBase } from "./monitoring-object-base"; +import { MonitoringObjectBase } from './monitoring-object-base'; export class MonitoringObject extends MonitoringObjectBase { myClass = MonitoringObject; - constructor( - moduleCode: string, - objectType: string, - id, - objService: MonitoringObjectService - ) { + constructor(moduleCode: string, objectType: string, id, objService: MonitoringObjectService) { super(moduleCode, objectType, id, objService); } @@ -62,18 +57,12 @@ export class MonitoringObject extends MonitoringObjectBase { return of(true); } - const childIdFieldName = - this.child0(childrenType).configParam("id_field_name"); + const childIdFieldName = this.child0(childrenType).configParam('id_field_name'); const observables = []; for (const childData of childrenDataOfType) { const id = childData.properties[childIdFieldName]; - const child = new this.myClass( - this.moduleCode, - childrenType, - id, - this._objService - ); + const child = new this.myClass(this.moduleCode, childrenType, id, this._objService); child.parents[this.objectType] = this; this.children[childrenType].push(child); observables.push(child.init(childData)); @@ -111,7 +100,7 @@ export class MonitoringObject extends MonitoringObjectBase { .postObject(this.moduleCode, this.objectType, this.postData(formValue)) .pipe( mergeMap((postData) => { - this.id = postData["id"]; + this.id = postData['id']; this._objService.setCache(this, postData); return this.init(postData); }) @@ -121,12 +110,7 @@ export class MonitoringObject extends MonitoringObjectBase { patch(formValue) { return this._objService .dataMonitoringObjectService() - .patchObject( - this.moduleCode, - this.objectType, - this.id, - this.postData(formValue) - ) + .patchObject(this.moduleCode, this.objectType, this.id, this.postData(formValue)) .pipe( mergeMap((postData) => { this._objService.setCache(this, postData); @@ -221,18 +205,15 @@ export class MonitoringObject extends MonitoringObjectBase { if (!elem.type_widget) { continue; } - observables[attribut_name] = this._objService.toForm( - elem, - properties[attribut_name] - ); + observables[attribut_name] = this._objService.toForm(elem, properties[attribut_name]); } return forkJoin(observables).pipe( concatMap((formValues_in) => { const formValues = Utils.copy(formValues_in); // geometry - if (this.config["geometry_type"]) { - formValues["geometry"] = this.geometry; // copy??? + if (this.config['geometry_type']) { + formValues['geometry'] = this.geometry; // copy??? } return of(formValues); }) @@ -249,10 +230,7 @@ export class MonitoringObject extends MonitoringObjectBase { if (!elem.type_widget) { continue; } - propertiesData[attribut_name] = this._objService.fromForm( - elem, - formValue[attribut_name] - ); + propertiesData[attribut_name] = this._objService.fromForm(elem, formValue[attribut_name]); } const postData = { @@ -260,9 +238,9 @@ export class MonitoringObject extends MonitoringObjectBase { // id_parent: this.parentId }; - if (this.config["geometry_type"]) { - postData["geometry"] = formValue["geometry"]; - postData["type"] = "Feature"; + if (this.config['geometry_type']) { + postData['geometry'] = formValue['geometry']; + postData['type'] = 'Feature'; } return postData; } @@ -312,7 +290,7 @@ export class MonitoringObject extends MonitoringObjectBase { childrenFieldNames, (fieldName) => child.resolvedProperties[fieldName] ); - row["id"] = child.id; + row['id'] = child.id; return row; }); } diff --git a/frontend/app/components/breadcrumbs/breadcrumbs.component.ts b/frontend/app/components/breadcrumbs/breadcrumbs.component.ts index 77c9c9d56..0465da827 100644 --- a/frontend/app/components/breadcrumbs/breadcrumbs.component.ts +++ b/frontend/app/components/breadcrumbs/breadcrumbs.component.ts @@ -1,26 +1,19 @@ -import { of } from "rxjs"; -import { mergeMap } from "rxjs/operators"; +import { of } from 'rxjs'; +import { mergeMap } from 'rxjs/operators'; -import { - Component, - OnInit, - Input, - Output, - SimpleChanges, - EventEmitter, -} from "@angular/core"; +import { Component, OnInit, Input, Output, SimpleChanges, EventEmitter } from '@angular/core'; -import { DataMonitoringObjectService } from "../../services/data-monitoring-object.service"; -import { ConfigService } from "../../services/config.service"; +import { DataMonitoringObjectService } from '../../services/data-monitoring-object.service'; +import { ConfigService } from '../../services/config.service'; -import { MonitoringObject } from "../../class/monitoring-object"; -import { Router } from "@angular/router"; -import { ActivatedRoute } from "@angular/router"; +import { MonitoringObject } from '../../class/monitoring-object'; +import { Router } from '@angular/router'; +import { ActivatedRoute } from '@angular/router'; @Component({ - selector: "pnx-monitoring-breadcrumbs", - templateUrl: "./breadcrumbs.component.html", - styleUrls: ["./breadcrumbs.component.css"], + selector: 'pnx-monitoring-breadcrumbs', + templateUrl: './breadcrumbs.component.html', + styleUrls: ['./breadcrumbs.component.css'], }) export class BreadcrumbsComponent implements OnInit { public breadcrumbs; @@ -66,8 +59,7 @@ export class BreadcrumbsComponent implements OnInit { }) ) .subscribe((breadcrumbs) => { - this.frontendModuleMonitoringUrl = - this._configService.frontendModuleMonitoringUrl(); + this.frontendModuleMonitoringUrl = this._configService.frontendModuleMonitoringUrl(); this.breadcrumbs = breadcrumbs; }); } @@ -79,7 +71,7 @@ export class BreadcrumbsComponent implements OnInit { this._router.navigate( [ this._configService.frontendModuleMonitoringUrl(), - "object", + 'object', elem.module_code, elem.object_type, elem.id, @@ -89,9 +81,7 @@ export class BreadcrumbsComponent implements OnInit { } ); } else { - this._router.navigate([ - this._configService.frontendModuleMonitoringUrl(), - ]); + this._router.navigate([this._configService.frontendModuleMonitoringUrl()]); } }, 100); } @@ -102,7 +92,7 @@ export class BreadcrumbsComponent implements OnInit { const cur = chng.currentValue; const pre = chng.currentValue; switch (propName) { - case "obj": + case 'obj': this.initBreadcrumbs(); break; } diff --git a/frontend/app/components/draw-form/draw-form.component.spec.ts b/frontend/app/components/draw-form/draw-form.component.spec.ts index fe0252ba4..0eb90169a 100644 --- a/frontend/app/components/draw-form/draw-form.component.spec.ts +++ b/frontend/app/components/draw-form/draw-form.component.spec.ts @@ -8,9 +8,8 @@ describe('DrawFormComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ DrawFormComponent ] - }) - .compileComponents(); + declarations: [DrawFormComponent], + }).compileComponents(); })); beforeEach(() => { diff --git a/frontend/app/components/draw-form/draw-form.component.ts b/frontend/app/components/draw-form/draw-form.component.ts index 59c6f3710..9c0818e53 100644 --- a/frontend/app/components/draw-form/draw-form.component.ts +++ b/frontend/app/components/draw-form/draw-form.component.ts @@ -1,20 +1,13 @@ -import { - Component, - OnInit, - Input, - Output, - EventEmitter, - ViewEncapsulation, -} from "@angular/core"; -import { FormControl, FormGroup } from "@angular/forms"; - -import { leafletDrawOptions } from "./leaflet-draw.options"; -import { CustomMarkerIcon } from "@geonature_common/map/marker/marker.component"; +import { Component, OnInit, Input, Output, EventEmitter, ViewEncapsulation } from '@angular/core'; +import { FormControl, FormGroup } from '@angular/forms'; + +import { leafletDrawOptions } from './leaflet-draw.options'; +import { CustomMarkerIcon } from '@geonature_common/map/marker/marker.component'; @Component({ - selector: "pnx-draw-form", - templateUrl: "./draw-form.component.html", - styleUrls: ["./draw-form.component.css"], + selector: 'pnx-draw-form', + templateUrl: './draw-form.component.html', + styleUrls: ['./draw-form.component.css'], }) export class DrawFormComponent implements OnInit { public geojson; @@ -54,23 +47,23 @@ export class DrawFormComponent implements OnInit { this.displayed = true; switch (this.geometryType) { - case "Point": { + case 'Point': { this.leafletDrawOptions.draw.marker = { icon: new CustomMarkerIcon(), }; break; } - case "Polygon": { + case 'Polygon': { this.leafletDrawOptions.draw.polygon = { allowIntersection: false, // Restricts shapes to simple polygons drawError: { - color: "#e1e100", // Color the shape will turn when intersects - message: "Intersection forbidden !", // Message that will show when intersect + color: '#e1e100', // Color the shape will turn when intersects + message: 'Intersection forbidden !', // Message that will show when intersect }, }; break; } - case "LineString": { + case 'LineString': { this.leafletDrawOptions.draw.polyline = true; break; } @@ -89,10 +82,11 @@ export class DrawFormComponent implements OnInit { // init geometry from parentFormControl this.setGeojson(this.parentFormControl.value); // suivi formControl => composant - this.formValueChangeSubscription = - this.parentFormControl.valueChanges.subscribe((geometry) => { + this.formValueChangeSubscription = this.parentFormControl.valueChanges.subscribe( + (geometry) => { this.setGeojson(geometry); - }); + } + ); } } diff --git a/frontend/app/components/draw-form/leaflet-draw.options.ts b/frontend/app/components/draw-form/leaflet-draw.options.ts index 5cda4b7d1..44189193c 100644 --- a/frontend/app/components/draw-form/leaflet-draw.options.ts +++ b/frontend/app/components/draw-form/leaflet-draw.options.ts @@ -10,6 +10,6 @@ export const leafletDrawOptions: any = { }, edit: { remove: false, - moveMarker: true - } + moveMarker: true, + }, }; diff --git a/frontend/app/components/modal-msg/modal-msg.component.spec.ts b/frontend/app/components/modal-msg/modal-msg.component.spec.ts index 496333acd..1cb48b052 100644 --- a/frontend/app/components/modal-msg/modal-msg.component.spec.ts +++ b/frontend/app/components/modal-msg/modal-msg.component.spec.ts @@ -8,9 +8,8 @@ describe('ModalMsgComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ ModalMsgComponent ] - }) - .compileComponents(); + declarations: [ModalMsgComponent], + }).compileComponents(); })); beforeEach(() => { diff --git a/frontend/app/components/modal-msg/modal-msg.component.ts b/frontend/app/components/modal-msg/modal-msg.component.ts index c9e5d0542..430a9b9ab 100644 --- a/frontend/app/components/modal-msg/modal-msg.component.ts +++ b/frontend/app/components/modal-msg/modal-msg.component.ts @@ -1,18 +1,14 @@ import { Component, OnInit, Input } from '@angular/core'; - @Component({ selector: 'pnx-modal-msg', templateUrl: './modal-msg.component.html', - styleUrls: ['./modal-msg.component.css'] + styleUrls: ['./modal-msg.component.css'], }) export class ModalMsgComponent implements OnInit { - @Input() bDisplayModal; - constructor() { } - - ngOnInit() { - } + constructor() {} + ngOnInit() {} } diff --git a/frontend/app/components/modules/modules.component.ts b/frontend/app/components/modules/modules.component.ts index e346fcdc1..f75d28bb6 100644 --- a/frontend/app/components/modules/modules.component.ts +++ b/frontend/app/components/modules/modules.component.ts @@ -1,16 +1,16 @@ -import { Utils } from "./../../utils/utils"; -import { Component, OnInit } from "@angular/core"; -import { mergeMap } from "rxjs/operators"; +import { Utils } from './../../utils/utils'; +import { Component, OnInit } from '@angular/core'; +import { mergeMap } from 'rxjs/operators'; /** services */ -import { DataMonitoringObjectService } from "../../services/data-monitoring-object.service"; -import { ConfigService } from "../../services/config.service"; -import { get } from "https"; +import { DataMonitoringObjectService } from '../../services/data-monitoring-object.service'; +import { ConfigService } from '../../services/config.service'; +import { get } from 'https'; @Component({ - selector: "pnx-monitoring-modules", - templateUrl: "./modules.component.html", - styleUrls: ["./modules.component.css"], + selector: 'pnx-monitoring-modules', + templateUrl: './modules.component.html', + styleUrls: ['./modules.component.css'], }) export class ModulesComponent implements OnInit { modules: Array = []; @@ -34,20 +34,18 @@ export class ModulesComponent implements OnInit { .init() .pipe( mergeMap( - this._dataMonitoringObjectService.getModules.bind( - this._dataMonitoringObjectService - ) + this._dataMonitoringObjectService.getModules.bind(this._dataMonitoringObjectService) ) ) .subscribe((modules: Array) => { this.modules = modules.filter((m) => m.cruved.R >= 1); this.backendUrl = this._configService.backendUrl(); - this.frontendModuleMonitoringUrl = - this._configService.frontendModuleMonitoringUrl(); + this.frontendModuleMonitoringUrl = this._configService.frontendModuleMonitoringUrl(); this.moduleMonitoringCode = this._configService.moduleMonitoringCode(); this.urlApplication = this._configService.urlApplication(); - this.assetsDirectory = - `${this._configService.backendUrl()}/${this._configService.appConfig.MEDIA_URL}/monitorings/`; + this.assetsDirectory = `${this._configService.backendUrl()}/${ + this._configService.appConfig.MEDIA_URL + }/monitorings/`; this.bLoading = false; }); } diff --git a/frontend/app/components/monitoring-datatable/monitoring-datatable.component.spec.ts b/frontend/app/components/monitoring-datatable/monitoring-datatable.component.spec.ts index 10f90c298..ac44e4d9e 100644 --- a/frontend/app/components/monitoring-datatable/monitoring-datatable.component.spec.ts +++ b/frontend/app/components/monitoring-datatable/monitoring-datatable.component.spec.ts @@ -8,9 +8,8 @@ describe('MonitoringDatatableComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ MonitoringDatatableComponent ] - }) - .compileComponents(); + declarations: [MonitoringDatatableComponent], + }).compileComponents(); })); beforeEach(() => { diff --git a/frontend/app/components/monitoring-datatable/monitoring-datatable.component.ts b/frontend/app/components/monitoring-datatable/monitoring-datatable.component.ts index 7cabb5c41..8784b6b35 100644 --- a/frontend/app/components/monitoring-datatable/monitoring-datatable.component.ts +++ b/frontend/app/components/monitoring-datatable/monitoring-datatable.component.ts @@ -1,4 +1,4 @@ -import { DatatableComponent } from "@swimlane/ngx-datatable"; +import { DatatableComponent } from '@swimlane/ngx-datatable'; import { Component, OnInit, @@ -8,16 +8,16 @@ import { ViewChild, SimpleChanges, TemplateRef, -} from "@angular/core"; -import { Router } from "@angular/router"; -import { MonitoringObjectService } from "./../../services/monitoring-object.service"; -import { Subject } from "rxjs"; -import { catchError, map, tap, take, debounceTime } from "rxjs/operators"; +} from '@angular/core'; +import { Router } from '@angular/router'; +import { MonitoringObjectService } from './../../services/monitoring-object.service'; +import { Subject } from 'rxjs'; +import { catchError, map, tap, take, debounceTime } from 'rxjs/operators'; @Component({ - selector: "pnx-monitoring-datatable", - templateUrl: "./monitoring-datatable.component.html", - styleUrls: ["./monitoring-datatable.component.css"], + selector: 'pnx-monitoring-datatable', + templateUrl: './monitoring-datatable.component.html', + styleUrls: ['./monitoring-datatable.component.css'], }) export class MonitoringDatatableComponent implements OnInit { @Input() rows; @@ -45,8 +45,8 @@ export class MonitoringDatatableComponent implements OnInit { customColumnComparator; @ViewChild(DatatableComponent) table: DatatableComponent; - @ViewChild("actionsTemplate") actionsTemplate: TemplateRef; - @ViewChild("hdrTpl") hdrTpl: TemplateRef; + @ViewChild('actionsTemplate') actionsTemplate: TemplateRef; + @ViewChild('hdrTpl') hdrTpl: TemplateRef; constructor(private _monitoring: MonitoringObjectService) {} @@ -55,7 +55,7 @@ export class MonitoringDatatableComponent implements OnInit { } initDatatable() { - this.filters = this.child0.configParam("filters"); + this.filters = this.child0.configParam('filters'); this.filterSubject.pipe(debounceTime(500)).subscribe(() => { this.filter(); }); @@ -86,10 +86,9 @@ export class MonitoringDatatableComponent implements OnInit { continue; } val = String(val).toLowerCase(); - const vals = val.split(" "); + const vals = val.split(' '); for (const v of vals) { - bCondVisible = - bCondVisible && (String(row[key]) || "").toLowerCase().includes(v); + bCondVisible = bCondVisible && (String(row[key]) || '').toLowerCase().includes(v); } } @@ -97,9 +96,8 @@ export class MonitoringDatatableComponent implements OnInit { return bCondVisible; } bChange = bChange || bCondVisible !== this.rowStatus[index].visible; - this.rowStatus[index]["visible"] = bCondVisible; - this.rowStatus[index]["selected"] = - this.rowStatus[index]["selected"] && bCondVisible; + this.rowStatus[index]['visible'] = bCondVisible; + this.rowStatus[index]['selected'] = this.rowStatus[index]['selected'] && bCondVisible; return bCondVisible; }); @@ -114,7 +112,7 @@ export class MonitoringDatatableComponent implements OnInit { } onRowClick(event) { - if (!(event && event.type === "click")) { + if (!(event && event.type === 'click')) { return; } const id = event.row && event.row.id; @@ -125,7 +123,7 @@ export class MonitoringDatatableComponent implements OnInit { this.rowStatus.forEach((status) => { const bCond = status.id === id; - status["selected"] = bCond && !status["selected"]; + status['selected'] = bCond && !status['selected']; }); this.setSelected(); @@ -161,7 +159,7 @@ export class MonitoringDatatableComponent implements OnInit { tooltip(column) { return this.child0.template.fieldDefinitions[column.prop] - ? column.name + " : " + this.child0.template.fieldDefinitions[column.prop] + ? column.name + ' : ' + this.child0.template.fieldDefinitions[column.prop] : column.name; } @@ -171,10 +169,10 @@ export class MonitoringDatatableComponent implements OnInit { const cur = chng.currentValue; const pre = chng.currentValue; switch (propName) { - case "rowStatus": + case 'rowStatus': this.setSelected(); break; - case "child0": + case 'child0': this.customColumnComparator = this.customColumnComparator_(); break; } @@ -186,7 +184,7 @@ export class MonitoringDatatableComponent implements OnInit { let x1 = propA, x2 = propB; - const res = 1 - Number(sortDirection === "asc") * 2; + const res = 1 - Number(sortDirection === 'asc') * 2; if (!x1 && !x2) { return 0; @@ -213,12 +211,12 @@ export class MonitoringDatatableComponent implements OnInit { const typeUtil = elem.type_widget || elem.type_util; switch (typeUtil) { - case "date": + case 'date': x1 = this._monitoring.dateFromString(x1); x2 = this._monitoring.dateFromString(x2); out = x1 === x2 ? 0 : x1 > x2 ? 1 : -1; break; - case "text": + case 'text': // quand les propriete sont de la forme "1.1 Nom_site" const v1 = this._monitoring.numberFromString(x1); const v2 = this._monitoring.numberFromString(x2); diff --git a/frontend/app/components/monitoring-form/monitoring-form.component.html b/frontend/app/components/monitoring-form/monitoring-form.component.html index b69e435e6..9987e3068 100644 --- a/frontend/app/components/monitoring-form/monitoring-form.component.html +++ b/frontend/app/components/monitoring-form/monitoring-form.component.html @@ -126,7 +126,7 @@

Attention

color="warn" class="float-left" (click)="bDeleteModal = true" - *ngIf="obj.id && (currentUser['cruved_object'][obj.objectType] || currentUser['cruved']).D >= obj.cruved('D')" + *ngIf="obj.id && currentUser.moduleCruved[obj.objectType].D >= obj.cruved('D')" > Supprimer diff --git a/frontend/app/components/monitoring-form/monitoring-form.component.spec.ts b/frontend/app/components/monitoring-form/monitoring-form.component.spec.ts index 1cfc81be0..f34b4c007 100644 --- a/frontend/app/components/monitoring-form/monitoring-form.component.spec.ts +++ b/frontend/app/components/monitoring-form/monitoring-form.component.spec.ts @@ -8,9 +8,8 @@ describe('MonitoringFormComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ MonitoringFormComponent ] - }) - .compileComponents(); + declarations: [MonitoringFormComponent], + }).compileComponents(); })); beforeEach(() => { diff --git a/frontend/app/components/monitoring-form/monitoring-form.component.ts b/frontend/app/components/monitoring-form/monitoring-form.component.ts index 8f0d81983..3e0ae5634 100644 --- a/frontend/app/components/monitoring-form/monitoring-form.component.ts +++ b/frontend/app/components/monitoring-form/monitoring-form.component.ts @@ -229,8 +229,6 @@ export class MonitoringFormComponent implements OnInit { this.obj.navigateToDetail(); } - - /** * Valider et aller à la page de l'objet */ @@ -239,7 +237,6 @@ export class MonitoringFormComponent implements OnInit { this.obj.navigateToParent(); } - msgToaster(action) { return `${action} ${this.obj.labelDu()} ${this.obj.description()} effectuée`.trim(); } @@ -249,7 +246,7 @@ export class MonitoringFormComponent implements OnInit { const action = this.obj.id ? this.obj.patch(this.objForm.value) : this.obj.post(this.objForm.value); - const actionLabel = this.obj.id ? "Modification" : "Création"; + const actionLabel = this.obj.id ? 'Modification' : 'Création'; action.subscribe((objData) => { this._commonService.regularToaster('success', this.msgToaster(actionLabel)); this.bSaveSpinner = this.bSaveAndAddChildrenSpinner = false; diff --git a/frontend/app/components/monitoring-lists/monitoring-list.component.spec.ts b/frontend/app/components/monitoring-lists/monitoring-list.component.spec.ts index 6b8273f94..d9be7770d 100644 --- a/frontend/app/components/monitoring-lists/monitoring-list.component.spec.ts +++ b/frontend/app/components/monitoring-lists/monitoring-list.component.spec.ts @@ -8,9 +8,8 @@ describe('MonitoringListComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ MonitoringListComponent ] - }) - .compileComponents(); + declarations: [MonitoringListComponent], + }).compileComponents(); })); beforeEach(() => { diff --git a/frontend/app/components/monitoring-lists/monitoring-lists.component.html b/frontend/app/components/monitoring-lists/monitoring-lists.component.html index 55acb6f81..3a1c73b5d 100644 --- a/frontend/app/components/monitoring-lists/monitoring-lists.component.html +++ b/frontend/app/components/monitoring-lists/monitoring-lists.component.html @@ -1,9 +1,11 @@
+ - +
- filter_alt + *ngIf="obj.moduleCode && currentUser.moduleCruved[child0.objectType].R >= child0.cruved('R')" + > + filter_alt @@ -27,7 +29,7 @@ color="primary" class="btn btn-success float-right" (click)="obj.navigateToAddChildren(child0.objectType)" - *ngIf="obj.moduleCode && (currentUser['cruved_object'][child0.objectType] || currentUser['cruved']).C >= child0.cruved('C')" + *ngIf="obj.moduleCode && currentUser.moduleCruved[child0.objectType].C >= child0.cruved('C')" > Ajouter {{ (child0.template["label_art_undef_new"] || "") }} diff --git a/frontend/app/components/monitoring-lists/monitoring-lists.component.ts b/frontend/app/components/monitoring-lists/monitoring-lists.component.ts index eb0d07389..50bb1a8d2 100644 --- a/frontend/app/components/monitoring-lists/monitoring-lists.component.ts +++ b/frontend/app/components/monitoring-lists/monitoring-lists.component.ts @@ -1,22 +1,15 @@ -import { DataMonitoringObjectService } from "./../../services/data-monitoring-object.service"; -import { - Component, - OnInit, - Input, - Output, - EventEmitter, - SimpleChanges, -} from "@angular/core"; -import { ConfigService } from "../../services/config.service"; +import { DataMonitoringObjectService } from './../../services/data-monitoring-object.service'; +import { Component, OnInit, Input, Output, EventEmitter, SimpleChanges } from '@angular/core'; +import { ConfigService } from '../../services/config.service'; -import { MonitoringObject } from "../../class/monitoring-object"; +import { MonitoringObject } from '../../class/monitoring-object'; -import { Utils } from "../../utils/utils"; +import { Utils } from '../../utils/utils'; @Component({ - selector: "pnx-monitoring-lists", - templateUrl: "./monitoring-lists.component.html", - styleUrls: ["./monitoring-lists.component.css"], + selector: 'pnx-monitoring-lists', + templateUrl: './monitoring-lists.component.html', + styleUrls: ['./monitoring-lists.component.css'], }) export class MonitoringListComponent implements OnInit { @Input() obj: MonitoringObject; @@ -42,8 +35,7 @@ export class MonitoringListComponent implements OnInit { // medias; @Input() objectsStatus: Object; - @Output() objectsStatusChange: EventEmitter = - new EventEmitter(); + @Output() objectsStatusChange: EventEmitter = new EventEmitter(); constructor(private _configService: ConfigService) {} @@ -57,19 +49,18 @@ export class MonitoringListComponent implements OnInit { for (const key of Object.keys(this.obj.children)) { this.queyParamsNewObject[key] = {}; this.queyParamsNewObject[key][this.obj.idFieldName()] = this.obj.id; - this.queyParamsNewObject[key]["parents_path"] = - this.obj.parentsPath.concat([this.obj.objectType]); + this.queyParamsNewObject[key]['parents_path'] = this.obj.parentsPath.concat([ + this.obj.objectType, + ]); } - this.frontendModuleMonitoringUrl = - this._configService.frontendModuleMonitoringUrl(); + this.frontendModuleMonitoringUrl = this._configService.frontendModuleMonitoringUrl(); this.backendUrl = this._configService.backendUrl(); this.children0Array = this.obj.children0Array(); - this.activetab = - this.children0Array[0] && this.children0Array[0].objectType; + this.activetab = this.children0Array[0] && this.children0Array[0].objectType; // datatable - this.childrenDataTable = this.obj.childrenColumnsAndRows("display_list"); + this.childrenDataTable = this.obj.childrenColumnsAndRows('display_list'); // this.medias = this.obj.children['media'] && this.obj.children['media'].map(e => e.properties); } @@ -77,7 +68,7 @@ export class MonitoringListComponent implements OnInit { onSelectedChildren(typeObject, event) { this.objectsStatus[typeObject] = event; let status_type = Utils.copy(this.objectsStatus); - status_type["type"] = typeObject; + status_type['type'] = typeObject; this.objectsStatusChange.emit(status_type); } @@ -93,7 +84,7 @@ export class MonitoringListComponent implements OnInit { if (Array.isArray(status_type[typeObject])) { for (let i in status_type[typeObject]) { try { - status_type[typeObject][i]["selected"] = false; + status_type[typeObject][i]['selected'] = false; } catch (error) { console.error(error.message, status_type[typeObject][i]); } @@ -108,7 +99,7 @@ export class MonitoringListComponent implements OnInit { displayNumber(chidrenType) { if (!this.objectsStatus[chidrenType]) { - return ""; + return ''; } const visibles = this.objectsStatus[chidrenType].filter((s) => s.visible); const nbSelected = visibles.length; @@ -122,7 +113,7 @@ export class MonitoringListComponent implements OnInit { const cur = chng.currentValue; const prev = chng.previousValue; switch (propName) { - case "obj": + case 'obj': this.initDataTable(); break; } diff --git a/frontend/app/components/monitoring-map/monitoring-map.component.spec.ts b/frontend/app/components/monitoring-map/monitoring-map.component.spec.ts index 8deb77e81..27f646211 100644 --- a/frontend/app/components/monitoring-map/monitoring-map.component.spec.ts +++ b/frontend/app/components/monitoring-map/monitoring-map.component.spec.ts @@ -8,9 +8,8 @@ describe('MonitoringMapComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ MonitoringMapComponent ] - }) - .compileComponents(); + declarations: [MonitoringMapComponent], + }).compileComponents(); })); beforeEach(() => { diff --git a/frontend/app/components/monitoring-map/monitoring-map.component.ts b/frontend/app/components/monitoring-map/monitoring-map.component.ts index 66bd543fc..18d6c4daa 100644 --- a/frontend/app/components/monitoring-map/monitoring-map.component.ts +++ b/frontend/app/components/monitoring-map/monitoring-map.component.ts @@ -1,35 +1,27 @@ -import { - Component, - OnInit, - Input, - Output, - EventEmitter, - SimpleChanges, -} from "@angular/core"; - -import { FormGroup } from "@angular/forms"; -import { MonitoringObject } from "../../class/monitoring-object"; -import { Layer, svg, Path } from "leaflet"; -import { ConfigService } from "../../services/config.service"; -import { DataMonitoringObjectService } from "../../services/data-monitoring-object.service"; - -import { MapService } from "@geonature_common/map/map.service"; -import { MapListService } from "@geonature_common/map-list/map-list.service"; -import { Utils } from "../../utils/utils"; -import * as L from "leaflet"; +import { Component, OnInit, Input, Output, EventEmitter, SimpleChanges } from '@angular/core'; + +import { FormGroup } from '@angular/forms'; +import { MonitoringObject } from '../../class/monitoring-object'; +import { Layer, svg, Path } from 'leaflet'; +import { ConfigService } from '../../services/config.service'; +import { DataMonitoringObjectService } from '../../services/data-monitoring-object.service'; + +import { MapService } from '@geonature_common/map/map.service'; +import { MapListService } from '@geonature_common/map-list/map-list.service'; +import { Utils } from '../../utils/utils'; +import * as L from 'leaflet'; @Component({ - selector: "pnx-monitoring-map", - templateUrl: "./monitoring-map.component.html", - styleUrls: ["./monitoring-map.component.css"], + selector: 'pnx-monitoring-map', + templateUrl: './monitoring-map.component.html', + styleUrls: ['./monitoring-map.component.css'], }) export class MonitoringMapComponent implements OnInit { @Input() bEdit: boolean; @Input() obj: MonitoringObject; @Input() objectsStatus: Object; - @Output() objectsStatusChange: EventEmitter = - new EventEmitter(); + @Output() objectsStatusChange: EventEmitter = new EventEmitter(); @Input() objForm: FormGroup; @@ -56,31 +48,31 @@ export class MonitoringMapComponent implements OnInit { hidden: { opacity: 0, fillOpacity: 0, - color: "blue", + color: 'blue', zIndex: 0, }, default: { opacity: 0.7, fillOpacity: 0.5, - color: "blue", + color: 'blue', zIndex: 600, }, current: { opacity: 0.7, fillOpacity: 0.5, - color: "green", + color: 'green', zIndex: 650, }, selected: { opacity: 0.7, fillOpacity: 0.5, - color: "red", + color: 'red', zIndex: 660, }, edit: { opacity: 0.2, fillOpacity: 0.1, - color: "blue", + color: 'blue', zIndex: 600, }, }; @@ -96,7 +88,7 @@ export class MonitoringMapComponent implements OnInit { initSites() { this.removeLabels(); - const layers = this._mapService.map["_layers"]; + const layers = this._mapService.map['_layers']; for (const key of Object.keys(layers)) { const layer = layers[key]; try { @@ -105,15 +97,15 @@ export class MonitoringMapComponent implements OnInit { } setTimeout(() => { this.initPanes(); - if (this.sites && this.sites["features"]) { + if (this.sites && this.sites['features']) { this.initSitesStatus(); - for (const site of this.sites["features"]) { + for (const site of this.sites['features']) { this.setPopup(site.id); const layer = this.findSiteLayer(site.id); // pane const fClick = this.onLayerClick(site); - layer.off("click", fClick); - layer.on("click", fClick); + layer.off('click', fClick); + layer.on('click', fClick); // layer.removeFrom(this._mapService.map); layer.addTo(this._mapService.map); @@ -128,7 +120,7 @@ export class MonitoringMapComponent implements OnInit { if (!this._mapService.map) { return; } - const layers = this._mapService.map["_layers"]; + const layers = this._mapService.map['_layers']; for (const key of Object.keys(layers)) { const layer = layers[key]; if (layer.options.permanent) { @@ -138,7 +130,7 @@ export class MonitoringMapComponent implements OnInit { } onEachFeature = (feature, layer) => { - const mapLabelFieldName = this.obj.configParam("map_label_field_name"); + const mapLabelFieldName = this.obj.configParam('map_label_field_name'); if (!mapLabelFieldName) { return; } @@ -148,7 +140,7 @@ export class MonitoringMapComponent implements OnInit { } let coordinates; - if (feature.geometry.type == "Point") { + if (feature.geometry.type == 'Point') { coordinates = layer.getLatLng(); } else { coordinates = layer.getBounds().getCenter(); @@ -156,8 +148,8 @@ export class MonitoringMapComponent implements OnInit { var text = L.tooltip({ permanent: true, - direction: "top", - className: "text", + direction: 'top', + className: 'text', }) .setContent(textValue) .setLatLng(coordinates); @@ -188,17 +180,17 @@ export class MonitoringMapComponent implements OnInit { } initSitesStatus() { - if (!this.objectsStatus["site"]) { - this.objectsStatus["site"] = []; + if (!this.objectsStatus['site']) { + this.objectsStatus['site'] = []; } const $this = this; - this.sites["features"].forEach((site) => { - const status = $this.objectsStatus["site"].find((s) => s.id === site.id); + this.sites['features'].forEach((site) => { + const status = $this.objectsStatus['site'].find((s) => s.id === site.id); if (status) { return; } - $this.objectsStatus["site"].push({ + $this.objectsStatus['site'].push({ selected: false, visible: true, id: site.id, @@ -212,25 +204,23 @@ export class MonitoringMapComponent implements OnInit { } // Get old select site - let old_s_site = this.objectsStatus["site"].filter( - (site) => site.id == this.selectedSiteId - ); + let old_s_site = this.objectsStatus['site'].filter((site) => site.id == this.selectedSiteId); if (old_s_site.length > 0) { - old_s_site[0]["selected"] = false; + old_s_site[0]['selected'] = false; this.setSiteStyle(old_s_site[0]); } // Get new select site - let new_s_site = this.objectsStatus["site"].filter((site) => site.id == id); + let new_s_site = this.objectsStatus['site'].filter((site) => site.id == id); if (new_s_site.length > 0) { - new_s_site[0]["selected"] = true; + new_s_site[0]['selected'] = true; this.setSiteStyle(new_s_site[0]); } this.selectedSiteId = id; } setSitesStyle() { - const objectType = this.objectsStatus["type"]; + const objectType = this.objectsStatus['type']; let openPopup = true; if (this._mapService.map) { @@ -240,16 +230,12 @@ export class MonitoringMapComponent implements OnInit { }); } // Si le dessin des groupes de sites est actif calcul de l'aire - if ( - this._configService.config()[this.obj.moduleCode]["module"][ - "b_draw_sites_group" - ] - ) { + if (this._configService.config()[this.obj.moduleCode]['module']['b_draw_sites_group']) { this.publicDisplaySitesGroup = true; } } - setSiteStyle(status, openPopup = true, objectType = "site") { + setSiteStyle(status, openPopup = true, objectType = 'site') { /* Défini le style des éléments statuts = statut de l'élément provient de objectsStatus @@ -262,29 +248,29 @@ export class MonitoringMapComponent implements OnInit { return; } - const style_name = !status["visible"] - ? "hidden" - : status["current"] - ? "current" - : status["selected"] - ? "selected" + const style_name = !status['visible'] + ? 'hidden' + : status['current'] + ? 'current' + : status['selected'] + ? 'selected' : this.bEdit - ? "edit" - : "default"; + ? 'edit' + : 'default'; - const style = this.styles[style_name] || this.styles["default"]; + const style = this.styles[style_name] || this.styles['default']; - style["pane"] = this.panes[style_name]; - style["renderer"] = this.renderers[style_name]; + style['pane'] = this.panes[style_name]; + style['renderer'] = this.renderers[style_name]; // layer.removeFrom(map); layer.setStyle(style); // layer.addTo(map); - if (status["selected"]) { + if (status['selected']) { this._mapListService.zoomOnSelectedLayer(map, layer); } - if (status["selected"] && openPopup == true) { + if (status['selected'] && openPopup == true) { if (!(layer as any)._popup) { this.setPopup(status.id); layer = this.findSiteLayer(status.id, objectType); @@ -292,14 +278,14 @@ export class MonitoringMapComponent implements OnInit { layer.openPopup(); } - if (!status["visible"] || !status["selected"]) { + if (!status['visible'] || !status['selected']) { layer.closePopup(); } // Affichage des tooltips uniquement si la feature est visible if (layer.getTooltip) { var toolTip = layer.getTooltip(); - if (style_name == "hidden") { + if (style_name == 'hidden') { if (toolTip) { map.closeTooltip(toolTip); } @@ -311,8 +297,8 @@ export class MonitoringMapComponent implements OnInit { } } - findSiteLayer(id, objectType = "site"): Path { - const layers = this._mapService.map["_layers"]; + findSiteLayer(id, objectType = 'site'): Path { + const layers = this._mapService.map['_layers']; const layerKey = Object.keys(layers) .filter((key) => { const monitoringObject = layers[key] && layers[key].feature; @@ -320,21 +306,17 @@ export class MonitoringMapComponent implements OnInit { }) .find((key) => { const feature = layers[key] && layers[key].feature; - return ( - feature && (feature["id"] === id || feature.properties["id"] === id) - ); + return feature && (feature['id'] === id || feature.properties['id'] === id); }); return layerKey && layers[layerKey]; } findSiteLayers(value, property): Array { - const layers = this._mapService.map["_layers"]; + const layers = this._mapService.map['_layers']; let filterlayers = Object.keys(layers) .filter( - (key) => - layers[key].feature && - layers[key]["feature"]["properties"][property] == value + (key) => layers[key].feature && layers[key]['feature']['properties'][property] == value ) .map((key) => ({ [key]: layers[key] })); @@ -352,20 +334,18 @@ export class MonitoringMapComponent implements OnInit { // TODO verifier si le fait de spécifier # en dur // Ne pose pas de soucis pour certaine configuration const url = [ - "#", + '#', this._configService.frontendModuleMonitoringUrl(), - "object", + 'object', this.obj.moduleCode, - "site", + 'site', layer['feature'].properties.id_base_site, - ].join("/"); + ].join('/'); const sPopup = `
-

${ - layer["feature"].properties.base_site_name - }

- ${layer["feature"].properties.description || ""} +

${layer['feature'].properties.base_site_name}

+ ${layer['feature'].properties.description || ''}
`; @@ -381,18 +361,18 @@ export class MonitoringMapComponent implements OnInit { const cur = chng.currentValue; const pre = chng.currentValue; switch (propName) { - case "objectsStatus": + case 'objectsStatus': if (!this.bListen) { this.bListen = true; } else { this.setSitesStyle(); } break; - case "bEdit": + case 'bEdit': this.setSitesStyle(); break; - case "sites": + case 'sites': this.initSites(); } } diff --git a/frontend/app/components/monitoring-object/monitoring-object.component.ts b/frontend/app/components/monitoring-object/monitoring-object.component.ts index e5264e669..eab3efe1a 100644 --- a/frontend/app/components/monitoring-object/monitoring-object.component.ts +++ b/frontend/app/components/monitoring-object/monitoring-object.component.ts @@ -1,24 +1,24 @@ -import { Observable, of, forkJoin } from "rxjs"; -import { mergeMap, concatMap } from "rxjs/operators"; +import { Observable, of, forkJoin } from 'rxjs'; +import { mergeMap, concatMap } from 'rxjs/operators'; -import { MonitoringObject } from "../../class/monitoring-object"; -import { Component, OnInit } from "@angular/core"; -import { FormGroup, FormBuilder } from "@angular/forms"; +import { MonitoringObject } from '../../class/monitoring-object'; +import { Component, OnInit } from '@angular/core'; +import { FormGroup, FormBuilder } from '@angular/forms'; // services -import { ActivatedRoute } from "@angular/router"; -import { MonitoringObjectService } from "../../services/monitoring-object.service"; -import { ConfigService } from "../../services/config.service"; -import { DataUtilsService } from "../../services/data-utils.service"; -import { AuthService, User } from "@geonature/components/auth/auth.service"; -import { CommonService } from "@geonature_common/service/common.service"; -import { MapService } from "@geonature_common/map/map.service"; - -import { Utils } from "../../utils/utils"; +import { ActivatedRoute } from '@angular/router'; +import { MonitoringObjectService } from '../../services/monitoring-object.service'; +import { ConfigService } from '../../services/config.service'; +import { DataUtilsService } from '../../services/data-utils.service'; +import { AuthService, User } from '@geonature/components/auth/auth.service'; +import { CommonService } from '@geonature_common/service/common.service'; +import { MapService } from '@geonature_common/map/map.service'; + +import { Utils } from '../../utils/utils'; @Component({ - selector: "pnx-object", - templateUrl: "./monitoring-object.component.html", - styleUrls: ["./monitoring-object.component.css"], + selector: 'pnx-object', + templateUrl: './monitoring-object.component.html', + styleUrls: ['./monitoring-object.component.css'], }) export class MonitoringObjectComponent implements OnInit { obj: MonitoringObject; @@ -53,24 +53,19 @@ export class MonitoringObjectComponent implements OnInit { ) {} ngAfterViewInit() { - const container = document.getElementById("object"); + const container = document.getElementById('object'); const height = this._commonService.calcCardContentHeight(); - container.style.height = height - 40 + "px"; - this.heightMap = height - 80 + "px"; + container.style.height = height - 40 + 'px'; + setTimeout(() => { + this.heightMap = height - 80 + 'px'; + }); } ngOnInit() { - const elements = document.getElementsByClassName( - "monitoring-map-container" - ); + const elements = document.getElementsByClassName('monitoring-map-container'); if (elements.length >= 1) { elements[0].remove(); } - this.currentUser = this._auth.getCurrentUser(); - - this.currentUser["cruved"] = {}; - this.currentUser["cruved_objects"] = {}; - of(true) .pipe( mergeMap(() => { @@ -81,6 +76,7 @@ export class MonitoringObjectComponent implements OnInit { }), mergeMap(() => { + this.initCurrentUser(); return this.initData(); // recupérations des données Nomenclature, Taxonomie, Utilisateur.. et mise en cache }), @@ -102,7 +98,7 @@ export class MonitoringObjectComponent implements OnInit { this.bLoadingModal = false; // fermeture du modal this.obj.bIsInitialized = true; // obj initialisé - if (!this.sites || this.obj.children["site"]) { + if (!this.sites || this.obj.children['site']) { this.initSites(); } else { this.initObjectsStatus(); @@ -110,21 +106,20 @@ export class MonitoringObjectComponent implements OnInit { }); } + initCurrentUser() { + this.currentUser = this._auth.getCurrentUser(); + this.currentUser['moduleCruved'] = this._configService.moduleCruved(this.obj.moduleCode); + } + getModuleSet() { // Verifie si le module est configué this.module.get(0).subscribe(() => { - const schema = this._configService.schema( - this.module.moduleCode, - "module" - ); + const schema = this._configService.schema(this.module.moduleCode, 'module'); const moduleFieldList = Object.keys( - this._configService.schema(this.module.moduleCode, "module") + this._configService.schema(this.module.moduleCode, 'module') ).filter((key) => schema[key].required); this.moduleSet = moduleFieldList.every( - (v) => - ![null, undefined].includes( - this.module.properties[v] || this.obj.properties[v] - ) + (v) => ![null, undefined].includes(this.module.properties[v] || this.obj.properties[v]) ); }); } @@ -132,29 +127,24 @@ export class MonitoringObjectComponent implements OnInit { initSites() { return this.module.get(1).subscribe(() => { // TODO liste indépendantes carte et listes - this.currentUser["cruved"] = this.module.userCruved; - this.currentUser["cruved_object"] = this.module.userCruvedObject; // affichage des groupes de site uniquement si l'objet est un module - if ( - this.obj.objectType == "module" && - this.obj["children"]["sites_group"] - ) { - const sitesGroup = this.obj["children"]["sites_group"]; + if (this.obj.objectType == 'module' && this.obj['children']['sites_group']) { + const sitesGroup = this.obj['children']['sites_group']; this.sitesGroup = { features: sitesGroup.map((group) => { - group["id"] = group["properties"]["id_sites_group"]; - group["type"] = "Feature"; + group['id'] = group['properties']['id_sites_group']; + group['type'] = 'Feature'; return group; }), - type: "FeatureCollection", + type: 'FeatureCollection', }; } // affichage des sites du premier parent qui a des sites dans l'odre de parent Path let sites = null; let cur = this.obj; do { - sites = cur["children"]["site"]; + sites = cur['children']['site']; cur = cur.parent(); } while (!!cur && !sites); @@ -163,11 +153,11 @@ export class MonitoringObjectComponent implements OnInit { } this.sites = { features: sites.map((site) => { - site["id"] = site["properties"]["id_base_site"]; - site["type"] = "Feature"; + site['id'] = site['properties']['id_base_site']; + site['type'] = 'Feature'; return site; }), - type: "FeatureCollection", + type: 'FeatureCollection', }; this.initObjectsStatus(); }); @@ -176,29 +166,27 @@ export class MonitoringObjectComponent implements OnInit { initObjectsStatus() { const objectsStatus = {}; for (const childrenType of Object.keys(this.obj.children)) { - objectsStatus[childrenType] = this.obj.children[childrenType].map( - (child) => { - return { - id: child.id, - selected: false, - visible: true, - current: false, - }; - } - ); + objectsStatus[childrenType] = this.obj.children[childrenType].map((child) => { + return { + id: child.id, + selected: false, + visible: true, + current: false, + }; + }); } // init site status if (this.obj.siteId) { - objectsStatus["site"] = []; - this.sites["features"].forEach((f) => { + objectsStatus['site'] = []; + this.sites['features'].forEach((f) => { // determination du site courrant let cur = false; if (f.properties.id_base_site == this.obj.siteId) { cur = true; } - objectsStatus["site"].push({ + objectsStatus['site'].push({ id: f.properties.id_base_site, selected: false, visible: true, @@ -223,22 +211,19 @@ export class MonitoringObjectComponent implements OnInit { initRoutesParams() { return this._route.paramMap.pipe( mergeMap((params) => { - const objectType = params.get("objectType") - ? params.get("objectType") - : "module"; + const objectType = params.get('objectType') ? params.get('objectType') : 'module'; this.obj = new MonitoringObject( - params.get("moduleCode"), + params.get('moduleCode'), objectType, - params.get("id"), + params.get('id'), this._objService ); - this.obj.parentsPath = - this._route.snapshot.queryParamMap.getAll("parents_path") || []; + this.obj.parentsPath = this._route.snapshot.queryParamMap.getAll('parents_path') || []; this.module = new MonitoringObject( - params.get("moduleCode"), - "module", + params.get('moduleCode'), + 'module', null, this._objService ); @@ -255,8 +240,7 @@ export class MonitoringObjectComponent implements OnInit { initConfig(): Observable { return this._configService.init(this.obj.moduleCode).pipe( mergeMap(() => { - this.frontendModuleMonitoringUrl = - this._configService.frontendModuleMonitoringUrl(); + this.frontendModuleMonitoringUrl = this._configService.frontendModuleMonitoringUrl(); this.backendUrl = this._configService.backendUrl(); return of(true); }) @@ -289,7 +273,7 @@ export class MonitoringObjectComponent implements OnInit { onObjChanged(obj: MonitoringObject) { this.obj = obj; - if (obj["objectType"] === "site") { + if (obj['objectType'] === 'site') { this.initSites(); } this.getModuleSet(); diff --git a/frontend/app/components/monitoring-properties/monitoring-properties.component.html b/frontend/app/components/monitoring-properties/monitoring-properties.component.html index 90891d5dc..9189186c8 100644 --- a/frontend/app/components/monitoring-properties/monitoring-properties.component.html +++ b/frontend/app/components/monitoring-properties/monitoring-properties.component.html @@ -68,7 +68,6 @@ attr.aria-labelledby="nav-medias-tab" *ngIf="obj.properties['medias'] && obj.properties['medias'].length" > - @@ -79,7 +78,7 @@ (click)="onEditClick()" *ngIf=" !bEdit && - (currentUser['cruved_object'][obj.objectType] || currentUser['cruved']).U >= obj.cruved('U') + currentUser?.moduleCruved[obj.objectType].U >= obj.cruved('U') " > Éditer {{ (obj.template['label_art_def'] || "") }} @@ -106,7 +105,7 @@ *ngIf=" !bEdit && obj.objectType === 'module' && - currentUser?.cruved.U >= 3 && + currentUser?.moduleCruved.module.U >= 3 && obj.properties['b_synthese'] " matTooltip="Attention, cette opération peux prendre du temps" diff --git a/frontend/app/components/monitoring-properties/monitoring-properties.component.spec.ts b/frontend/app/components/monitoring-properties/monitoring-properties.component.spec.ts index 5ecfd3130..57e5b9d55 100644 --- a/frontend/app/components/monitoring-properties/monitoring-properties.component.spec.ts +++ b/frontend/app/components/monitoring-properties/monitoring-properties.component.spec.ts @@ -8,9 +8,8 @@ describe('MonitoringPropertiesComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ MonitoringPropertiesComponent ] - }) - .compileComponents(); + declarations: [MonitoringPropertiesComponent], + }).compileComponents(); })); beforeEach(() => { diff --git a/frontend/app/components/monitoring-properties/monitoring-properties.component.ts b/frontend/app/components/monitoring-properties/monitoring-properties.component.ts index 108092c8b..997831825 100644 --- a/frontend/app/components/monitoring-properties/monitoring-properties.component.ts +++ b/frontend/app/components/monitoring-properties/monitoring-properties.component.ts @@ -1,23 +1,18 @@ -import { Component, OnInit, Input, Output, EventEmitter } from "@angular/core"; -import { MonitoringObject } from "../../class/monitoring-object"; -import { ConfigService } from "../../services/config.service"; -import { DataMonitoringObjectService } from "../../services/data-monitoring-object.service"; -import { CommonService } from "@geonature_common/service/common.service"; -import { MediaService } from "@geonature_common/service/media.service"; -import html2canvas from "html2canvas"; -import { MapService } from "@geonature_common/map/map.service"; -import { NgbModal } from "@ng-bootstrap/ng-bootstrap"; -import { - FormGroup, - FormControl, - FormBuilder, - Validators, -} from "@angular/forms"; +import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; +import { MonitoringObject } from '../../class/monitoring-object'; +import { ConfigService } from '../../services/config.service'; +import { DataMonitoringObjectService } from '../../services/data-monitoring-object.service'; +import { CommonService } from '@geonature_common/service/common.service'; +import { MediaService } from '@geonature_common/service/media.service'; +import html2canvas from 'html2canvas'; +import { MapService } from '@geonature_common/map/map.service'; +import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { FormGroup, FormControl, FormBuilder, Validators } from '@angular/forms'; @Component({ - selector: "pnx-monitoring-properties", - templateUrl: "./monitoring-properties.component.html", - styleUrls: ["./monitoring-properties.component.css"], + selector: 'pnx-monitoring-properties', + templateUrl: './monitoring-properties.component.html', + styleUrls: ['./monitoring-properties.component.css'], }) export class MonitoringPropertiesComponent implements OnInit { @Input() obj: MonitoringObject; @@ -56,14 +51,14 @@ export class MonitoringPropertiesComponent implements OnInit { () => { this.bUpdateSyntheseSpinner = false; this._commonService.regularToaster( - "success", + 'success', `La synthèse a été mise à jour pour le module ${this.obj.moduleCode}` ); }, (err) => { this.bUpdateSyntheseSpinner = false; this._commonService.regularToaster( - "error", + 'error', `Erreur lors de la mise à jour de la synthèse pour le module ${this.obj.moduleCode} - ${err.error.message}` ); } @@ -72,7 +67,7 @@ export class MonitoringPropertiesComponent implements OnInit { // add mje: show dowload modal openModalExportCsv(event, modal) { this.selectedDataSet = []; - this.modalReference = this.ngbModal.open(modal, { size: "lg" }); + this.modalReference = this.ngbModal.open(modal, { size: 'lg' }); } onDatasetChanged(id_dataset: any, i) { @@ -82,11 +77,7 @@ export class MonitoringPropertiesComponent implements OnInit { getExportCsv(exportDef: any, jd: number) { const queryParams = jd != null ? { id_dataset: jd } : {}; - this._dataService.getExportCsv( - this.obj.moduleCode, - exportDef.method, - queryParams - ); + this._dataService.getExportCsv(this.obj.moduleCode, exportDef.method, queryParams); } //mje: generate PDF export @@ -96,26 +87,26 @@ export class MonitoringPropertiesComponent implements OnInit { try { var zoomInElement = document.querySelector( - "#monitoring-map-container .leaflet-control-zoom-in" + '#monitoring-map-container .leaflet-control-zoom-in' ); - var snapshotElement = document.getElementById("geometry"); + var snapshotElement = document.getElementById('geometry'); var config = { allowTaint: true, useCORS: true, ignoreElements: function (element) { return ( - element.classList[0] == "leaflet-control-zoom-in" || - element.classList[0] == "leaflet-control-zoom-out" || - element.classList[0] == "leaflet-control-layers-toggle" || - element.title == "A JS library for interactive maps" || - element.placeholder == "Rechercher un lieu" + element.classList[0] == 'leaflet-control-zoom-in' || + element.classList[0] == 'leaflet-control-zoom-out' || + element.classList[0] == 'leaflet-control-layers-toggle' || + element.title == 'A JS library for interactive maps' || + element.placeholder == 'Rechercher un lieu' ); }, logging: false, }; html2canvas(snapshotElement, config).then(function (canvas) { - var imgData = canvas.toDataURL("image/png"); + var imgData = canvas.toDataURL('image/png'); const extra_data = { resolved_properties: $this.obj.resolvedProperties, }; @@ -130,16 +121,13 @@ export class MonitoringPropertiesComponent implements OnInit { ) .subscribe(() => { $this._commonService.regularToaster( - "success", + 'success', "L'export PDF est prêt à être récupéré dans le dossier de Téléchargement" ); }); }); } catch { - $this._commonService.regularToaster( - "error", - "Une erreur est survenue durant l'export PDF" - ); + $this._commonService.regularToaster('error', "Une erreur est survenue durant l'export PDF"); } } } diff --git a/frontend/app/gnModule.module.ts b/frontend/app/gnModule.module.ts index 64fecadc7..bb54d438d 100644 --- a/frontend/app/gnModule.module.ts +++ b/frontend/app/gnModule.module.ts @@ -1,44 +1,41 @@ -import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from "@angular/core"; -import { GN2CommonModule } from "@geonature_common/GN2Common.module"; -import { Routes, RouterModule } from "@angular/router"; -import { CommonModule } from "@angular/common"; -import { FormsModule } from "@angular/forms"; -import { ReactiveFormsModule } from "@angular/forms"; -import { HttpClient } from "@angular/common/http"; -import { HttpClientXsrfModule } from "@angular/common/http"; +import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { GN2CommonModule } from '@geonature_common/GN2Common.module'; +import { Routes, RouterModule } from '@angular/router'; +import { CommonModule } from '@angular/common'; +import { FormsModule } from '@angular/forms'; +import { ReactiveFormsModule } from '@angular/forms'; +import { HttpClient } from '@angular/common/http'; +import { HttpClientXsrfModule } from '@angular/common/http'; // Service -import { DataMonitoringObjectService } from "./services/data-monitoring-object.service"; -import { DataUtilsService } from "./services/data-utils.service"; -import { CacheService } from "./services/cache.service"; -import { MonitoringObjectService } from "./services/monitoring-object.service"; -import { ConfigService } from "./services/config.service"; +import { DataMonitoringObjectService } from './services/data-monitoring-object.service'; +import { DataUtilsService } from './services/data-utils.service'; +import { CacheService } from './services/cache.service'; +import { MonitoringObjectService } from './services/monitoring-object.service'; +import { ConfigService } from './services/config.service'; // Component -import { BreadcrumbsComponent } from "./components/breadcrumbs/breadcrumbs.component"; -import { ModulesComponent } from "./components/modules/modules.component"; -import { MonitoringObjectComponent } from "./components/monitoring-object/monitoring-object.component"; -import { DrawFormComponent } from "./components/draw-form/draw-form.component"; -import { ModalMsgComponent } from "./components/modal-msg/modal-msg.component"; -import { MonitoringMapComponent } from "./components/monitoring-map/monitoring-map.component"; -import { MonitoringFormComponent } from "./components/monitoring-form/monitoring-form.component"; -import { MonitoringListComponent } from "./components/monitoring-lists/monitoring-lists.component"; -import { MonitoringPropertiesComponent } from "./components/monitoring-properties/monitoring-properties.component"; -import { MonitoringDatatableComponent } from "./components/monitoring-datatable/monitoring-datatable.component"; - -import { MatSlideToggleModule } from "@angular/material/slide-toggle"; -import { MatFormFieldModule} from "@angular/material/form-field"; -import { MatAutocompleteModule} from "@angular/material/autocomplete"; -import { MatSelectModule} from "@angular/material/select"; -import { MatInputModule} from "@angular/material/input"; - - +import { BreadcrumbsComponent } from './components/breadcrumbs/breadcrumbs.component'; +import { ModulesComponent } from './components/modules/modules.component'; +import { MonitoringObjectComponent } from './components/monitoring-object/monitoring-object.component'; +import { DrawFormComponent } from './components/draw-form/draw-form.component'; +import { ModalMsgComponent } from './components/modal-msg/modal-msg.component'; +import { MonitoringMapComponent } from './components/monitoring-map/monitoring-map.component'; +import { MonitoringFormComponent } from './components/monitoring-form/monitoring-form.component'; +import { MonitoringListComponent } from './components/monitoring-lists/monitoring-lists.component'; +import { MonitoringPropertiesComponent } from './components/monitoring-properties/monitoring-properties.component'; +import { MonitoringDatatableComponent } from './components/monitoring-datatable/monitoring-datatable.component'; +import { MatSlideToggleModule } from '@angular/material/slide-toggle'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatAutocompleteModule } from '@angular/material/autocomplete'; +import { MatSelectModule } from '@angular/material/select'; +import { MatInputModule } from '@angular/material/input'; // my module routing const routes: Routes = [ /** modules */ - { path: "", component: ModulesComponent }, + { path: '', component: ModulesComponent }, /** module */ { path: 'module/:moduleCode', component: MonitoringObjectComponent }, @@ -47,12 +44,12 @@ const routes: Routes = [ /** object */ { - path: "object/:moduleCode/:objectType/:id", + path: 'object/:moduleCode/:objectType/:id', component: MonitoringObjectComponent, }, /** create object */ { - path: "create_object/:moduleCode/:objectType", + path: 'create_object/:moduleCode/:objectType', component: MonitoringObjectComponent, }, ]; @@ -82,7 +79,7 @@ const routes: Routes = [ MatSelectModule, MatInputModule, HttpClientXsrfModule.withOptions({ - headerName: "token", + headerName: 'token', }), ], providers: [ diff --git a/frontend/app/services/cache.service.ts b/frontend/app/services/cache.service.ts index 876ac07f0..f8d0ed5e7 100644 --- a/frontend/app/services/cache.service.ts +++ b/frontend/app/services/cache.service.ts @@ -1,11 +1,11 @@ -import { ObserversComponent } from "@geonature_common/form/observers/observers.component"; -import { Injectable } from "@angular/core"; -import { HttpClient, HttpHeaders } from "@angular/common/http"; +import { ObserversComponent } from '@geonature_common/form/observers/observers.component'; +import { Injectable } from '@angular/core'; +import { HttpClient, HttpHeaders } from '@angular/common/http'; -import { Observable, of, Subject } from "rxjs"; -import { mergeMap, concatMap } from "rxjs/operators"; +import { Observable, of, Subject } from 'rxjs'; +import { mergeMap, concatMap } from 'rxjs/operators'; -import { ConfigService } from "./config.service"; +import { ConfigService } from './config.service'; /** * Ce service référence et execute les requêtes bers le serveur backend @@ -16,7 +16,10 @@ export class CacheService { private _cache = {}; private _pendingCache = {}; - constructor(private _http: HttpClient, private _config: ConfigService) {} + constructor( + private _http: HttpClient, + private _config: ConfigService + ) {} /** http request */ @@ -27,29 +30,24 @@ export class CacheService { * @param urlRelative url relative de la route * @param data post data (optionnel) */ - request( - requestType: string, - urlRelative: string, - { postData = {}, queryParams = {} } = {} - ) { + request(requestType: string, urlRelative: string, { postData = {}, queryParams = {} } = {}) { // verification de requestType - if (!["get", "post", "patch", "delete"].includes(requestType)) { + if (!['get', 'post', 'patch', 'delete'].includes(requestType)) { return of(null); } const url_params = Object.keys(queryParams).length - ? "?" + + ? '?' + Object.keys(queryParams) .map((key) => Array.isArray(queryParams[key]) - ? queryParams[key].map((val) => `${key}=${val}`).join("&") + ? queryParams[key].map((val) => `${key}=${val}`).join('&') : `${key}=${queryParams[key]}` ) - .join("&") - : ""; + .join('&') + : ''; - const url = - this._config.backendModuleUrl() + "/" + urlRelative + url_params; + const url = this._config.backendModuleUrl() + '/' + urlRelative + url_params; // requete return this._http[requestType](url, postData); @@ -66,7 +64,7 @@ export class CacheService { getFromCache(sCachePaths: string, cache = null) { cache = cache || this._cache; - const cachePaths = sCachePaths.split("|"); + const cachePaths = sCachePaths.split('|'); // parcours du dictionnaire _cache let current = cache; @@ -95,7 +93,7 @@ export class CacheService { */ setCacheValue(sCachePaths: string, value: any, cache = null) { cache = cache || this._cache; - const cachePaths = sCachePaths.split("|"); + const cachePaths = sCachePaths.split('|'); const key = cachePaths.pop(); @@ -110,7 +108,7 @@ export class CacheService { removeCacheValue(sCachePaths: string, cache = null) { cache = cache || this._cache; - const cachePaths = sCachePaths.split("|"); + const cachePaths = sCachePaths.split('|'); const key = cachePaths.pop(); // parcours du cache @@ -131,11 +129,7 @@ export class CacheService { * @param urlRelative url relative de la route * @param sCachePaths chaine de caractères tableau qui permet de parcourir le dictionnaire _cache */ - cache_or_request( - requestType: string, - urlRelative: string, - sCachePaths: string - ) { + cache_or_request(requestType: string, urlRelative: string, sCachePaths: string) { // on renvoie un observable return new Observable((observer) => { // recuperation depuis le cache @@ -184,13 +178,12 @@ export class CacheService { const url_params = Object.keys(queryParams) .map((key) => Array.isArray(queryParams[key]) - ? queryParams[key].map((val) => `${key}=${val}`).join("&") + ? queryParams[key].map((val) => `${key}=${val}`).join('&') : `${key}=${queryParams[key]}` ) - .join("&"); + .join('&'); - const url = - this._config.backendModuleUrl() + "/" + urlRelative + "?" + url_params; + const url = this._config.backendModuleUrl() + '/' + urlRelative + '?' + url_params; // requete window.open(url); @@ -203,17 +196,17 @@ export class CacheService { { postData = {}, queryParams = {} } = {} ) { const httpHeaders: HttpHeaders = new HttpHeaders({ - Accept: "application/pdf", + Accept: 'application/pdf', }); - const url = this._config.backendModuleUrl() + "/" + urlRelative; + const url = this._config.backendModuleUrl() + '/' + urlRelative; return this._http[requestType](url, postData, { - responseType: "arraybuffer", + responseType: 'arraybuffer', headers: httpHeaders, }).pipe( mergeMap((file) => { let blob = new Blob([file as BlobPart], { - type: "application/pdf", + type: 'application/pdf', }); let url = window.URL.createObjectURL(blob); window.open(url); diff --git a/frontend/app/services/config.service.ts b/frontend/app/services/config.service.ts index 85e9bd528..2cc057826 100644 --- a/frontend/app/services/config.service.ts +++ b/frontend/app/services/config.service.ts @@ -9,7 +9,11 @@ import { ConfigService as GnConfigService } from '@geonature/services/config.ser export class ConfigService { private _config; - constructor(private _http: HttpClient, private _moduleService: ModuleService, public appConfig: GnConfigService) {} + constructor( + private _http: HttpClient, + private _moduleService: ModuleService, + public appConfig: GnConfigService + ) {} /** Configuration */ @@ -66,6 +70,28 @@ export class ConfigService { return this._moduleService.currentModule.module_path; } + moduleCruved(module_code) { + const permObjectDict = { + site: 'GNM_SITES', + sites_group: 'GNM_GRP_SITES', + visit: 'GNM_VISITES', + observation: 'GNM_OBSERVATIONS', + module: 'GNM_MODULES', + }; + + const module = this._moduleService.getModule(module_code); + + const moduleCruved = {}; + + for (const [objectCode, permObjectCode] of Object.entries(permObjectDict)) { + moduleCruved[objectCode] = + module.objects.find((o) => o.code_object == permObjectDict[objectCode])?.cruved || + module.cruved; + } + + return moduleCruved; + } + moduleMonitoringCode() { return this.appConfig.MONITORINGS.MODULE_CODE; } diff --git a/frontend/app/services/data-monitoring-object.service.ts b/frontend/app/services/data-monitoring-object.service.ts index 083d54cd3..1558af72d 100644 --- a/frontend/app/services/data-monitoring-object.service.ts +++ b/frontend/app/services/data-monitoring-object.service.ts @@ -1,10 +1,10 @@ -import { mergeMap } from "rxjs/operators"; -import { Observable, of } from "rxjs"; -import { Injectable } from "@angular/core"; +import { mergeMap } from 'rxjs/operators'; +import { Observable, of } from 'rxjs'; +import { Injectable } from '@angular/core'; -import { CacheService } from "./cache.service"; -import { ConfigService } from "./config.service"; -import { HttpClient } from "@angular/common/http"; +import { CacheService } from './cache.service'; +import { ConfigService } from './config.service'; +import { HttpClient } from '@angular/common/http'; /** * Ce service référence et execute les requêtes bers le serveur backend @@ -24,7 +24,7 @@ export class DataMonitoringObjectService { * Renvoie la liste des modules */ getModules() { - return this._cacheService.request("get", `modules`); + return this._cacheService.request('get', `modules`); } // /** @@ -40,10 +40,8 @@ export class DataMonitoringObjectService { urlMonitoring(apiType, moduleCode, objectType, id = null) { let url: string; const params = []; - if (objectType.includes("module")) { - url = moduleCode - ? `${apiType}/${moduleCode}/${objectType}` - : `${apiType}/module`; + if (objectType.includes('module')) { + url = moduleCode ? `${apiType}/${moduleCode}/${objectType}` : `${apiType}/module`; } else { url = id ? `${apiType}/${moduleCode}/${objectType}/${id}` @@ -54,8 +52,8 @@ export class DataMonitoringObjectService { } paramsMonitoring(objectType, queryParams = {}) { - if (objectType.includes("module")) { - queryParams["field_name"] = "module_code"; + if (objectType.includes('module')) { + queryParams['field_name'] = 'module_code'; } return queryParams; } @@ -68,9 +66,9 @@ export class DataMonitoringObjectService { * @param id l'identifiant de l'objet */ getObject(moduleCode, objectType, id = null, depth = null) { - const url = this.urlMonitoring("object", moduleCode, objectType, id); + const url = this.urlMonitoring('object', moduleCode, objectType, id); const queryParams = this.paramsMonitoring(objectType, { depth }); - return this._cacheService.request("get", url, { queryParams }); + return this._cacheService.request('get', url, { queryParams }); } /** @@ -81,8 +79,8 @@ export class DataMonitoringObjectService { * @param id l'identifiant de l'objet */ patchObject(moduleCode, objectType, id, postData) { - const url = this.urlMonitoring("object", moduleCode, objectType, id); - return this._cacheService.request("patch", url, { postData }); + const url = this.urlMonitoring('object', moduleCode, objectType, id); + return this._cacheService.request('patch', url, { postData }); } /** @@ -93,8 +91,8 @@ export class DataMonitoringObjectService { * @param id l'identifiant de l'objet */ postObject(moduleCode, objectType, postData) { - const url = this.urlMonitoring("object", moduleCode, objectType); - return this._cacheService.request("post", url, { postData }); + const url = this.urlMonitoring('object', moduleCode, objectType); + return this._cacheService.request('post', url, { postData }); } /** @@ -105,8 +103,8 @@ export class DataMonitoringObjectService { * @param id l'identifiant de l'objet */ deleteObject(moduleCode, objectType, id) { - const url = this.urlMonitoring("object", moduleCode, objectType, id); - return this._cacheService.request("delete", url); + const url = this.urlMonitoring('object', moduleCode, objectType, id); + return this._cacheService.request('delete', url); } /** breadcrumbs */ @@ -118,8 +116,8 @@ export class DataMonitoringObjectService { * @param id l'identifiant de l'objet */ getBreadcrumbs(moduleCode, objectType, id, queryParams) { - const url = this.urlMonitoring("breadcrumbs", moduleCode, objectType, id); - return this._cacheService.request("get", url, { queryParams }); + const url = this.urlMonitoring('breadcrumbs', moduleCode, objectType, id); + return this._cacheService.request('get', url, { queryParams }); } /** Mise à jour de toute la synthèse du module @@ -127,7 +125,7 @@ export class DataMonitoringObjectService { */ updateSynthese(moduleCode) { const url = `synthese/${moduleCode}`; - return this._cacheService.request("post", url); + return this._cacheService.request('post', url); } /** @@ -144,7 +142,7 @@ export class DataMonitoringObjectService { queryParams: queryParams, }; - this._cacheService.requestExport("get", url, params); + this._cacheService.requestExport('get', url, params); } /** @@ -155,16 +153,9 @@ export class DataMonitoringObjectService { * id_inventor ??? * **/ - postPdfExport( - module_code, - object_type, - id, - template, - map_image, - extra_data = {} - ) { + postPdfExport(module_code, object_type, id, template, map_image, extra_data = {}) { const url = `exports/pdf/${module_code}/${object_type}/${id}`; - return this._cacheService.requestExportCreatedPdf("post", url, { + return this._cacheService.requestExportCreatedPdf('post', url, { postData: { map: map_image, template, diff --git a/frontend/app/services/data-utils.service.ts b/frontend/app/services/data-utils.service.ts index b926407c8..0d19aff0c 100644 --- a/frontend/app/services/data-utils.service.ts +++ b/frontend/app/services/data-utils.service.ts @@ -1,13 +1,13 @@ -import { Injectable } from "@angular/core"; +import { Injectable } from '@angular/core'; -import { Observable, forkJoin, of } from "rxjs"; -import { concatMap, mergeMap } from "rxjs/operators"; +import { Observable, forkJoin, of } from 'rxjs'; +import { concatMap, mergeMap } from 'rxjs/operators'; -import { Utils } from "./../utils/utils"; +import { Utils } from './../utils/utils'; -import { CacheService } from "./cache.service"; -import { ConfigService } from "./config.service"; -import { DataFormService } from "@geonature_common/form/data-form.service"; +import { CacheService } from './cache.service'; +import { ConfigService } from './config.service'; +import { DataFormService } from '@geonature_common/form/data-form.service'; /** * Ce service référence et execute les requêtes bers le serveur backend @@ -45,27 +45,25 @@ export class DataUtilsService { // parametre pour le stockage dans le cache const sCachePaths = `util|${typeUtil}|${id}`; // récupération dans le cache ou requête si besoin - return this._cacheService - .cache_or_request("get", urlRelative, sCachePaths) - .pipe( - mergeMap((value) => { - let out; - if (fieldName === "all") { - out = value; - } else if (fieldName.split(",").length >= 2) { - // plusieurs champs par ex 'nom_vern,lb_nom' si nom_vern null alors lb_nom - for (const fieldNameInter of fieldName.split(",")) { - if (value[fieldNameInter]) { - out = value[fieldNameInter]; - break; - } + return this._cacheService.cache_or_request('get', urlRelative, sCachePaths).pipe( + mergeMap((value) => { + let out; + if (fieldName === 'all') { + out = value; + } else if (fieldName.split(',').length >= 2) { + // plusieurs champs par ex 'nom_vern,lb_nom' si nom_vern null alors lb_nom + for (const fieldNameInter of fieldName.split(',')) { + if (value[fieldNameInter]) { + out = value[fieldNameInter]; + break; } - } else { - out = value[fieldName]; } - return of(out); - }) - ); + } else { + out = value[fieldName]; + } + return of(out); + }) + ); } /** @@ -82,14 +80,12 @@ export class DataUtilsService { const observables = []; // applique getUtil pour chaque id de ids for (const id of ids) { - observables.push( - this.getUtil(typeUtilObject, id, fieldName, idFieldName) - ); + observables.push(this.getUtil(typeUtilObject, id, fieldName, idFieldName)); } // renvoie un forkJoin du tableau d'observables return forkJoin(observables).pipe( concatMap((res) => { - return of(res.join(", ")); + return of(res.join(', ')); }) ); } @@ -98,7 +94,7 @@ export class DataUtilsService { getNomenclature(typeNomenclature, codeNomenclature) { const urlRelative = `util/nomenclature/${typeNomenclature}/${codeNomenclature}`; const sCachePaths = `util|nomenclature|${typeNomenclature}|${codeNomenclature}`; - return this._cacheService.cache_or_request("get", urlRelative, sCachePaths); + return this._cacheService.cache_or_request('get', urlRelative, sCachePaths); } /** Récupère les données qui seront utiles pour le module @@ -109,50 +105,44 @@ export class DataUtilsService { // récupération dans le cache ou requête si besoin const cache = this._cacheService.cache(); - if (cache[moduleCode] && cache[moduleCode]["init_data"]) { + if (cache[moduleCode] && cache[moduleCode]['init_data']) { return of(true); } const urlRelative = `util/init_data/${moduleCode}`; - return this._cacheService.request("get", urlRelative).pipe( + return this._cacheService.request('get', urlRelative).pipe( mergeMap((initData) => { - if (cache[moduleCode] && cache[moduleCode]["init_data"]) { + if (cache[moduleCode] && cache[moduleCode]['init_data']) { return of(true); } - for (const nomenclature of initData["nomenclature"] || []) { + for (const nomenclature of initData['nomenclature'] || []) { this._cacheService.setCacheValue( - `util|nomenclature|${nomenclature["id_nomenclature"]}`, + `util|nomenclature|${nomenclature['id_nomenclature']}`, nomenclature ); } - for (const user of initData["user"] || []) { - this._cacheService.setCacheValue( - `util|user|${user["id_role"]}`, - user - ); + for (const user of initData['user'] || []) { + this._cacheService.setCacheValue(`util|user|${user['id_role']}`, user); } - for (const sitesGroup of initData["sites_group"] || []) { + for (const sitesGroup of initData['sites_group'] || []) { this._cacheService.setCacheValue( - `util|sites_group|${sitesGroup["id_sites_group"]}`, + `util|sites_group|${sitesGroup['id_sites_group']}`, sitesGroup ); } - for (const dataset of initData["dataset"] || []) { - this._cacheService.setCacheValue( - `util|dataset|${dataset["id_dataset"]}`, - dataset - ); + for (const dataset of initData['dataset'] || []) { + this._cacheService.setCacheValue(`util|dataset|${dataset['id_dataset']}`, dataset); } // pour ne pas appeler la fonction deux fois if (!cache[moduleCode]) { cache[moduleCode] = {}; } - cache[moduleCode]["init_data"] = true; + cache[moduleCode]['init_data'] = true; return of(true); }) ); @@ -249,10 +239,10 @@ export class DataUtilsService { // } getDataUtil(key) { - return this._cacheService["_cache"]["util"][key]; + return this._cacheService['_cache']['util'][key]; } getNomenclatures() { - return this._cacheService["_cache"]["util"]["nomenclature"]; + return this._cacheService['_cache']['util']['nomenclature']; } } diff --git a/frontend/app/services/monitoring-object.service.ts b/frontend/app/services/monitoring-object.service.ts index 35d465aec..f17053605 100644 --- a/frontend/app/services/monitoring-object.service.ts +++ b/frontend/app/services/monitoring-object.service.ts @@ -1,13 +1,13 @@ -import { MonitoringObject } from "./../class/monitoring-object"; -import { Injectable } from "@angular/core"; -import { Observable, of } from "rxjs"; +import { MonitoringObject } from './../class/monitoring-object'; +import { Injectable } from '@angular/core'; +import { Observable, of } from 'rxjs'; -import { ConfigService } from "./config.service"; -import { DataMonitoringObjectService } from "./data-monitoring-object.service"; -import { DataUtilsService } from "./data-utils.service"; -import { Utils } from "../utils/utils"; -import { mergeMap } from "rxjs/operators"; -import { Router } from "@angular/router"; +import { ConfigService } from './config.service'; +import { DataMonitoringObjectService } from './data-monitoring-object.service'; +import { DataUtilsService } from './data-utils.service'; +import { Utils } from '../utils/utils'; +import { mergeMap } from 'rxjs/operators'; +import { Router } from '@angular/router'; @Injectable() export class MonitoringObjectService { @@ -28,7 +28,7 @@ export class MonitoringObjectService { } cache = cache[objectType] = cache[objectType] || {}; - if (objectType === "module") { + if (objectType === 'module') { return cache; } @@ -41,17 +41,17 @@ export class MonitoringObjectService { setCache(obj: MonitoringObject, objData) { // post ou update - if (obj.objectType === "module" && !obj.moduleCode) { + if (obj.objectType === 'module' && !obj.moduleCode) { return; } - if (obj.objectType !== "module" && !obj.id) { + if (obj.objectType !== 'module' && !obj.id) { return; } // object - if (obj.objectType === "module") { + if (obj.objectType === 'module') { const cache = this.cache(obj.moduleCode); - cache["module"] = objData; + cache['module'] = objData; } else { const cache = this.cache(obj.moduleCode, obj.objectType); cache[obj.id] = objData; @@ -91,15 +91,10 @@ export class MonitoringObjectService { // update nb_child const key = Object.keys(parent.properties).find((k) => - [ - "nb_visits", - "nb_observations", - "nb_sites", - "nb_sites_groups", - ].includes(k) + ['nb_visits', 'nb_observations', 'nb_sites', 'nb_sites_groups'].includes(k) ); if (key) { - console.log("up cache", parent.properties.base_site_name, key); + console.log('up cache', parent.properties.base_site_name, key); parent.properties[key] = parent.children[obj.objectType].length; } } @@ -107,11 +102,7 @@ export class MonitoringObjectService { } getParentFromCache(obj: MonitoringObject, parentType) { - const parentData = this.cache( - obj.moduleCode, - parentType, - obj.parentId(parentType) - ); + const parentData = this.cache(obj.moduleCode, parentType, obj.parentId(parentType)); if (!(parentData && parentData.children)) { return; } @@ -120,10 +111,10 @@ export class MonitoringObjectService { getFromCache(obj: MonitoringObject) { // get - if (obj.objectType === "module" && !obj.moduleCode) { + if (obj.objectType === 'module' && !obj.moduleCode) { return; } - if (obj.objectType !== "module" && !obj.id) { + if (obj.objectType !== 'module' && !obj.id) { return; } @@ -146,12 +137,7 @@ export class MonitoringObjectService { for (const childrenType of obj.childrenTypes()) { const childrenData = obj.children[childrenType] || []; for (const childData of childrenData) { - const child = new MonitoringObject( - obj.moduleCode, - childrenType, - childData.id, - this - ); + const child = new MonitoringObject(obj.moduleCode, childrenType, childData.id, this); this.deleteCache(child); } } @@ -167,7 +153,7 @@ export class MonitoringObjectService { parent.children[obj.objectType].splice(index, 1); // update nb_child const key = Object.keys(parent.properties).find((k) => - ["nb_visits", "nb_observations", "nb_sites"].includes(k) + ['nb_visits', 'nb_observations', 'nb_sites'].includes(k) ); if (key) { parent.properties[key] = parent.children[obj.objectType].length; @@ -181,9 +167,7 @@ export class MonitoringObjectService { } configUtils(elem, moduleCode) { - return this._configService.config()[moduleCode].display_field_names[ - elem.type_util - ]; + return this._configService.config()[moduleCode].display_field_names[elem.type_util]; } toForm(elem, val): Observable { @@ -192,7 +176,7 @@ export class MonitoringObjectService { x = [undefined, null].includes(x) ? elem.value || null : x; switch (elem.type_widget) { - case "date": { + case 'date': { const date = new Date(x); x = x ? { @@ -203,29 +187,27 @@ export class MonitoringObjectService { : null; break; } - case "observers": { + case 'observers': { x = !(x instanceof Array) ? [x] : x; break; } - case "taxonomy": { - x = x ? this._dataUtilsService.getUtil("taxonomy", x, "all") : null; + case 'taxonomy': { + x = x ? this._dataUtilsService.getUtil('taxonomy', x, 'all') : null; break; } } if ( - elem.type_util === "nomenclature" && + elem.type_util === 'nomenclature' && Utils.isObject(x) && x.code_nomenclature_type && x.cd_nomenclature ) { - x = this._dataUtilsService - .getNomenclature(x.code_nomenclature_type, x.cd_nomenclature) - .pipe( - mergeMap((nomenclature) => { - return of(nomenclature["id_nomenclature"]); - }) - ); + x = this._dataUtilsService.getNomenclature(x.code_nomenclature_type, x.cd_nomenclature).pipe( + mergeMap((nomenclature) => { + return of(nomenclature['id_nomenclature']); + }) + ); } x = x instanceof Observable ? x : of(x); @@ -235,21 +217,15 @@ export class MonitoringObjectService { fromForm(elem, val) { let x = val; switch (elem.type_widget) { - case "date": { - x = - x && x.year && x.month && x.day - ? `${x.year}-${x.month}-${x.day}` - : null; + case 'date': { + x = x && x.year && x.month && x.day ? `${x.year}-${x.month}-${x.day}` : null; break; } - case "observers": { - x = - elem.max_length === 1 && x instanceof Array && x.length === 1 - ? x[0] - : x; + case 'observers': { + x = elem.max_length === 1 && x instanceof Array && x.length === 1 ? x[0] : x; break; } - case "taxonomy": { + case 'taxonomy': { x = x instanceof Object ? x.cd_nom : x; break; } @@ -258,7 +234,7 @@ export class MonitoringObjectService { } dateFromString(s_date) { - const v_date = s_date.split("/"); + const v_date = s_date.split('/'); if (v_date.length !== 3) { return null; } @@ -267,9 +243,9 @@ export class MonitoringObjectService { } numberFromString(s) { - const v = s.split(" "); + const v = s.split(' '); const s_n = v[0]; - const v_n = s_n.split("."); + const v_n = s_n.split('.'); v_n[0] = Number(v_n[0]); v_n[1] = Number(v_n[1]); return v_n.length > 1 && v_n[0] ? v_n : null; diff --git a/frontend/app/utils/utils.ts b/frontend/app/utils/utils.ts index b301fd585..ef5e976b0 100644 --- a/frontend/app/utils/utils.ts +++ b/frontend/app/utils/utils.ts @@ -1,4 +1,4 @@ -import { Observable } from "rxjs/Observable"; +import { Observable } from 'rxjs/Observable'; export class Utils { /** Fonction pour copier un objet de type dictionnaire */ @@ -18,19 +18,15 @@ export class Utils { } static isObject(x) { - return typeof x === "object" && x != null; + return typeof x === 'object' && x != null; } static formatDate(val) { // return val ? new Date(val).toLocaleString('fr-FR', { timeZone: 'UTC' }).replace(',', '').split(' ')[0] : val; - return val ? val.split("-").reverse().join("/") : val; + return val ? val.split('-').reverse().join('/') : val; } - static mapDictToArray( - dictIn: Object, - processFunc = null, - fieldName: string = null - ): Array { + static mapDictToArray(dictIn: Object, processFunc = null, fieldName: string = null): Array { if (!dictIn) { return null; } @@ -50,11 +46,7 @@ export class Utils { * (opt) processFunc modifie les elements de arrayIn * (opt) field name pour prendre comme cle du dictOut elem[field_name] */ - static mapArrayToDict( - arrayIn: Array, - processFunc = null, - fieldName: string = null - ): Object { + static mapArrayToDict(arrayIn: Array, processFunc = null, fieldName: string = null): Object { if (!arrayIn) { return null; } diff --git a/requirements.in b/requirements.in index bdfd7e2a8..d16eb8ba2 100644 --- a/requirements.in +++ b/requirements.in @@ -1,2 +1,2 @@ -geonature>=2.11.0 +geonature>=2.13.0 jsonschema diff --git a/setup.py b/setup.py index 51d1afff4..fc5fcb5bb 100644 --- a/setup.py +++ b/setup.py @@ -23,8 +23,8 @@ packages=setuptools.find_packages("backend"), package_dir={"": "backend"}, package_data={ - "gn_module_monitoring.config": ["generic/*.json"], - "gn_module_monitoring.migrations": ["data/*.sql"], + "gn_module_monitoring.config": ["generic/*.json"], + "gn_module_monitoring.migrations": ["data/*.sql"], }, install_requires=requirements, tests_require=[],