Le filtrage géographique des observations repose sur la géométrie précise et n’a pas besoin de tenir compte du floutage, contrairement à la recherche géographique de la synthèse.
On considère que l’accès à une observation est acquis dès lors que sa géométrie intersecte la zone géographique autorisées (l’observation n’a pas besoin d’être totalement inclus dans le périmètre géographique autorisé), ce qui est le comportement du trigger de peuplement de la table cor_area_synthese qui pourra donc être utilisé pour le filtrage.
L’utilisation de cor_area_synthese suppose donc que les filtres géographiques portent uniquement sur des zones du référentiels. Cette hypothèse a pour gros avantage de ne pas nécessiter d’intersection dynamique.
Il peut être nécessaire de donner accès aux données sur plusieurs zones, par exemple plusieurs communes. Les permissions étant cumulatives, il est possible de créer plusieurs permissions chacune portant sur une unique zone. Cependant, la gestion des permissions apparaît alors compliquée. On souhaite plutôt pouvoir préciser plusieurs zones directement sur une seule permission.
Pour cela, il faut :
Rajouter une table cor_permission_area au schéma gn_permissions avec une colonne FK vers gn_permissions.t_permissions.id_permission et une colonne FK vers ref_geo.l_areas.id_area.
Modifier les modèles en conséquence, notamment en rajoutant une relationship areas_filter sur le modèle Permission contenant la liste des areas.
Rajouter une colonne booléenne areas_filter dans la table t_permissions_available
Rajouter dans Permission.filters_fields une entrée "AREA": areas_filter.
Il faut adapter la property Permission.filters de sorte qu’elle continue lorsque la liste areas_filter est vide (cas d’un filtre de type liste non existant jusque là).
Il faut compléter PermFilter.__str__ pour l’affichage de la permission dans l’interface d’admin :
elif self.name == "AREA":
if self.value:
areas_names = ','.join([a.area_name for a in self.value])
return f"""<i class="fa fa-map-marker" aria-hidden="true"></i> {areas_names}"""
else:
return """<i class="fa fa-globe" aria-hidden="true"></i> partout"""
Compléter PermissionAdmin et PermissionAvailableAdmin et notamment le filters_formatter
Vient ensuite l’implémentation dans la synthèse ! Pour le moment, le filtre n’est pas connue par la synthèse, ainsi toute permission contenant un filtre géographique sera ignoré.
Pour en prendre compte dans la synthèse, il faut implémenter le filtre géographique deux fois :
En python pour la vérification unitaire de l’accès à une observation lors de l’accès à la route get_one_synthese
En SQL pour le filtrage des observations à récupérer lors de l’accès à la route get_observations_for_web
Puis il faut passer la colonne areas_filter à TRUE dans la table t_permissions_available pour le R (et le E) de la synthèse.
Pour la vérification unitaire, à implémenter dans Synthese._has_permission_grant :
rajouter AREA à if perm.has_other_filters_than(...)
rajouter un bloc if perm.areas_filter: vérifiant si l’observation est dans une des zones concernées.
À priori on peut faire quelque chose comme return {a.id_area for a in perm.areas_filter}.intersect({a.id_area for a in obs.areas}).
Cela suppose que les zonages utilisés dans les filtres géographiques sont actifs (c’est-à-dire qu’ils sont pris en compte par le trigger de peuplement de la table cor_area_synthese et donc remonte bien dans obs.areas).
Pour le filtrage d’une liste d’observation, à implémenter dans SyntheseQuery.build_permissions_filter :
rajouter AREA à if perm.has_other_filters_than(...)
rajouter un bloc if perm.areas_filter:. On pourra faire quelque chose comme cor_area_synthese.c.id_area.in_([a.id_area for a in perm.areas_filter]). Il peut être nécessaire de joindre la table cor_area_synthese à la synthèse avec un alias, afin d’éviter les conflits entre les permissions géographiques et le groupement du mode maille qui joint déjà cor_area_synthese et ce, sans alias (dans la route get_observations_for_web).
Lorsqu’un utilisateur possède une permission via un groupe sur un territoire donnée, mais possède également une permission personnelle sans restriction géographique, il est intéressant de simplifier le jeu de permissions de l’utilisateur pour ne garder que la permission la plus permissive. La comparaison des permissions est faite par la méthode Permission.__le__, qui compare chaque filtre.
La comparaison par défaut (__default_le__) ne fonctionne pas vraiment pour les filtres géographiques (liste de zone), on rajoutera donc une comparaison spécifique :
class Permission:
@staticmethod
def __AREA_le__(a, b):
return set(a) == set(b) or not b
Cette implémentation est naïve, elle permet uniquement d’identifier les permissions dont la liste des zones géographique est identique, ou les permissions sans restriction géographique.
Il serait intéressant d’identifier les inclusions de territoire, c’est-à-dire quelque chose comme ST_Contains(ST_Union(a), ST_Union(b)).
Cependant, cela alourdirait probablement beaucoup les performances puisque la simplification des permissions a lieu à chaque récupération des permissions, c’est-à-dire à chaque requête.
On pourrait toutefois pré-calculer certaines inclusions dans une table dédiée, notamment les communes dans les départements, les départements dans les régions, etc. Cela permettrait déjà d’effectuer de manière efficace quelques simplifications de permissions. Dans tous les cas, il parait judicieux d’étudier des jeux de permissions réels afin de déterminer s’il a lieu de chercher à les simplifier.
Il est souhaité de rajouter aux permissions un filtre géographique permettant de restreindre une permission à zone géographique donnée (https://github.com/PnX-SI/GeoNature/issues/3097).
Le filtrage géographique des observations repose sur la géométrie précise et n’a pas besoin de tenir compte du floutage, contrairement à la recherche géographique de la synthèse. On considère que l’accès à une observation est acquis dès lors que sa géométrie intersecte la zone géographique autorisées (l’observation n’a pas besoin d’être totalement inclus dans le périmètre géographique autorisé), ce qui est le comportement du trigger de peuplement de la table
cor_area_synthese
qui pourra donc être utilisé pour le filtrage.L’utilisation de
cor_area_synthese
suppose donc que les filtres géographiques portent uniquement sur des zones du référentiels. Cette hypothèse a pour gros avantage de ne pas nécessiter d’intersection dynamique.Il peut être nécessaire de donner accès aux données sur plusieurs zones, par exemple plusieurs communes. Les permissions étant cumulatives, il est possible de créer plusieurs permissions chacune portant sur une unique zone. Cependant, la gestion des permissions apparaît alors compliquée. On souhaite plutôt pouvoir préciser plusieurs zones directement sur une seule permission.
Pour cela, il faut :
cor_permission_area
au schémagn_permissions
avec une colonne FK versgn_permissions.t_permissions.id_permission
et une colonne FK versref_geo.l_areas.id_area
.areas_filter
sur le modèlePermission
contenant la liste desareas
.areas_filter
dans la tablet_permissions_available
Permission.filters_fields
une entrée"AREA": areas_filter
.Permission.filters
de sorte qu’ellecontinue
lorsque la listeareas_filter
est vide (cas d’un filtre de type liste non existant jusque là).PermFilter.__str__
pour l’affichage de la permission dans l’interface d’admin :PermissionAdmin
etPermissionAvailableAdmin
et notamment lefilters_formatter
Vient ensuite l’implémentation dans la synthèse ! Pour le moment, le filtre n’est pas connue par la synthèse, ainsi toute permission contenant un filtre géographique sera ignoré.
Pour en prendre compte dans la synthèse, il faut implémenter le filtre géographique deux fois :
get_one_synthese
get_observations_for_web
Puis il faut passer la colonneareas_filter
àTRUE
dans la tablet_permissions_available
pour le R (et le E) de la synthèse.Pour la vérification unitaire, à implémenter dans
Synthese._has_permission_grant
:AREA
àif perm.has_other_filters_than(...)
if perm.areas_filter:
vérifiant si l’observation est dans une des zones concernées. À priori on peut faire quelque chose commereturn {a.id_area for a in perm.areas_filter}.intersect({a.id_area for a in obs.areas})
. Cela suppose que les zonages utilisés dans les filtres géographiques sont actifs (c’est-à-dire qu’ils sont pris en compte par le trigger de peuplement de la tablecor_area_synthese
et donc remonte bien dansobs.areas
).Pour le filtrage d’une liste d’observation, à implémenter dans
SyntheseQuery.build_permissions_filter
:AREA
àif perm.has_other_filters_than(...)
if perm.areas_filter:
. On pourra faire quelque chose commecor_area_synthese.c.id_area.in_([a.id_area for a in perm.areas_filter])
. Il peut être nécessaire de joindre la tablecor_area_synthese
à la synthèse avec un alias, afin d’éviter les conflits entre les permissions géographiques et le groupement du mode maille qui joint déjàcor_area_synthese
et ce, sans alias (dans la routeget_observations_for_web
).Lorsqu’un utilisateur possède une permission via un groupe sur un territoire donnée, mais possède également une permission personnelle sans restriction géographique, il est intéressant de simplifier le jeu de permissions de l’utilisateur pour ne garder que la permission la plus permissive. La comparaison des permissions est faite par la méthode
Permission.__le__
, qui compare chaque filtre. La comparaison par défaut (__default_le__
) ne fonctionne pas vraiment pour les filtres géographiques (liste de zone), on rajoutera donc une comparaison spécifique :Cette implémentation est naïve, elle permet uniquement d’identifier les permissions dont la liste des zones géographique est identique, ou les permissions sans restriction géographique.
Il serait intéressant d’identifier les inclusions de territoire, c’est-à-dire quelque chose comme
ST_Contains(ST_Union(a), ST_Union(b))
. Cependant, cela alourdirait probablement beaucoup les performances puisque la simplification des permissions a lieu à chaque récupération des permissions, c’est-à-dire à chaque requête.On pourrait toutefois pré-calculer certaines inclusions dans une table dédiée, notamment les communes dans les départements, les départements dans les régions, etc. Cela permettrait déjà d’effectuer de manière efficace quelques simplifications de permissions. Dans tous les cas, il parait judicieux d’étudier des jeux de permissions réels afin de déterminer s’il a lieu de chercher à les simplifier.