Projet

Général

Profil

Development #50054

journal, filtrer sur un type d'événement

Ajouté par Frédéric Péters il y a plus de 3 ans. Mis à jour il y a environ 3 ans.

Statut:
Fermé
Priorité:
Normal
Assigné à:
Catégorie:
-
Version cible:
-
Début:
11 janvier 2021
Echéance:
% réalisé:

0%

Temps estimé:
Patch proposed:
Oui
Planning:
Non

Description

Pour débugger l'accès à un compte j'ai voulu filtrer le tableau des événements pour juste voir les "changement de mot de passe par un administrateur" et je n'ai pas pu. (j'ai simplement essayé de taper "changement de mot de passe par un administrateur" dans la zone de recherche).

Je ne suis pas sûr que juste filtrer sur le type précis soit suffisant, plus tard j'ai voulu filtrer sur "demande de réinitilisation du mot de passe" et ça me serait bien allé que ce filtrage attrape aussi les "demande de réinitilisation du mot de passe par un administrateur".


Fichiers

Révisions associées

Révision 91a190cd (diff)
Ajouté par Valentin Deniaud il y a environ 3 ans

manager: easier journal filtering by event types (#50054)

Historique

#1

Mis à jour par Valentin Deniaud il y a environ 3 ans

  • Assigné à mis à Valentin Deniaud
#2

Mis à jour par Valentin Deniaud il y a environ 3 ans

Bon j'ai codé mon affaire (0002) puis en allant écrire les tests je vois que c'était déjà possible mais pas indiqué à cause d'une typo (0001).

Je pense que mon 0002 vaut le coup quand même parce qu'actuellement on ne peut rechercher qu'en connaissant les slugs, ce qui est OK pour nous mais moins pour les agents, mais je laisse un relecteur se prononcer avant d'écrire les tests.

#3

Mis à jour par Benjamin Dauvergne il y a environ 3 ans

Je veux bien un screenshot pour voir ce que ça donne, mais déjà il y a un bout que je ne comprends pas :

        self.fields['event_types'].choices = sorted(
            self.fields['event_types'].choices, key=lambda x: unicodedata.normalize('NFKD', x[1])
        )

C'est quoi le souci avec le choice de base ?

#4

Mis à jour par Valentin Deniaud il y a environ 3 ans

Benjamin Dauvergne a écrit :

C'est quoi le souci avec le choice de base ?

Il est trié par slug, or ici on affiche le nom traduit, et donc on a l'impression d'un tri aléatoire sans ça (et il faut normaliser pour les évènements genre « échec d'authentification »).

#5

Mis à jour par Benjamin Dauvergne il y a environ 3 ans

Valentin Deniaud a écrit :

Benjamin Dauvergne a écrit :

C'est quoi le souci avec le choice de base ?

Il est trié par slug, or ici on affiche le nom traduit, et donc on a l'impression d'un tri aléatoire sans ça (et il faut normaliser pour les évènements genre « échec d'authentification »).

C'est grandement un hack, ça ne march pas sur les majuscules ni sur la lettre suivante (ici ed classé avant éc):

>>> sorted(['éc', 'b', 'd', 'É', 'b', 'd', 'ed', 'b', 'd', 'E'], key=lambda s: unicodedata.normalize('NFKD', s))
['E', 'É', 'b', 'b', 'b', 'd', 'd', 'd', 'ed', 'éc']

Il faut appliquer les règles de collation pour la locale française pour faire bien les choses, c'est naturellement fait par postgresql quand on trie en base et que la bonne locale est posée, coté python ça peut-être via locale.strxfrm mais ça nécessite de bouger la locale globale via locale.setlocale(LC_ALL, 'fr_FR'). En moins global tu peux utiliser python3-icu qui implémente une API de locale non globale.

Mais pour pas trop te faire chier tu peux aussi recopier wcs.qommon.misc.simplify() qui te donnera certainement à peu de chose près le même comportement :

def simplify(s, space='-'):
    if s is None:
        return ''
    if not isinstance(s, six.text_type):
        if get_publisher() and get_publisher().site_charset:
            s = force_text('%s' % s, get_publisher().site_charset, errors='ignore')
        else:
            s = force_text('%s' % s, 'iso-8859-1', errors='ignore')
    s = force_text(unicodedata.normalize('NFKD', s).encode('ascii', 'ignore'))
    s = re.sub(r'[^\w\s\'%s]' % space, '', s).strip().lower()
    s = re.sub(r'[\s\'%s]+' % space, space, s)
    return s

--

Concernant la vidéo je ne suis pas fan de la popup gratuite, en plus on n'a pas de retour visible sur l'évènement actuellement filtré. Ne pourrait-t-on utiliser une simple liste à choix ?

#6

Mis à jour par Valentin Deniaud il y a environ 3 ans

Benjamin Dauvergne a écrit :

Mais pour pas trop te faire chier tu peux aussi recopier wcs.qommon.misc.simplify() qui te donnera certainement à peu de chose près le même comportement :

Ouep OK je vais faire ça.

Concernant la vidéo je ne suis pas fan de la popup gratuite, en plus on n'a pas de retour visible sur l'évènement actuellement filtré. Ne pourrait-t-on utiliser une simple liste à choix ?

Fred dit dans la description que filtrer sur un seul type n'est pas suffisant. J'ajoute un retour visuel ?

#7

Mis à jour par Benjamin Dauvergne il y a environ 3 ans

Valentin Deniaud a écrit :

Benjamin Dauvergne a écrit :

Mais pour pas trop te faire chier tu peux aussi recopier wcs.qommon.misc.simplify() qui te donnera certainement à peu de chose près le même comportement :

Ouep OK je vais faire ça.

Concernant la vidéo je ne suis pas fan de la popup gratuite, en plus on n'a pas de retour visible sur l'évènement actuellement filtré. Ne pourrait-t-on utiliser une simple liste à choix ?

Fred dit dans la description que filtrer sur un seul type n'est pas suffisant. J'ajoute un retour visuel ?

J'ai deux idées :
  • une liste à choix multiple, mais je suis pas fan,
  • utiliser des wildcard sur les noms des types d'évènements pour générer des métas types, ex. :
    • "manager.*" pour tout ce qui concerne le BO,
    • "*.password.*" pour tout ce qui concerne les mots de passe,
    • "*.user.*" pour tout ce qui concerne les utilisateurs.

La liste est là :

    name = 'manager.role.administrator.role.addition'
    name = 'manager.role.administrator.role.removal'
    name = 'manager.role.administrator.user.addition'
    name = 'manager.role.administrator.user.removal'
    name = 'manager.role.creation'
    name = 'manager.role.deletion'
    name = 'manager.role.edit'
    name = 'manager.role.inheritance.addition'
    name = 'manager.role.inheritance.removal'
    name = 'manager.role.membership.grant'
    name = 'manager.role.membership.removal'
    name = 'manager.user.activation'
    name = 'manager.user.creation'
    name = 'manager.user.deactivation'
    name = 'manager.user.deletion'
    name = 'manager.user.email.change.request'
    name = 'manager.user.password.change'
    name = 'manager.user.password.change.force'
    name = 'manager.user.password.change.unforce'
    name = 'manager.user.password.reset.request'
    name = 'manager.user.profile.edit'
    name = 'manager.user.sso.authorization.deletion'
    name = 'user.deletion'
    name = 'user.login'
    name = 'user.login.failure'
    name = 'user.logout'
    name = 'user.password.change'
    name = 'user.password.reset'
    name = 'user.password.reset.failure'
    name = 'user.password.reset.request'
    name = 'user.profile.edit'
    name = 'user.registration'
    name = 'user.registration.request'
    name = 'user.service.sso'
    name = 'user.service.sso.authorization'
    name = 'user.service.sso.unauthorization'

Dans un premier temps on peut juste couvrir le cas d'usage listé par Fred (on peut même cacher les types précis dans ce cas, ne garder que le méta-type qui groupe les deux) avec un wildcard "*.password.reset.*" et ajouter un booléen aux types d'évènements pour indiquer qu'ils ne doivent pas être visibles directement dans la liste. Avec une menu à deux niveaux ce serait joli :

Utilisateurs :
 * Connexion (user.login*)
 * Ré-init mot de passe (*.password.reset.*)
Rôles :
...

#8

Mis à jour par Valentin Deniaud il y a environ 3 ans

J'aime bien, sans faire de magie il y a moyen de s'en sortir de manière très simple avec genre dans le formulaire

+EVENT_TYPE_CHOICES = (
+    ('user.login', _('Login')),
+    ('password-reset', _('Password reset')),
+)
+
+    event_types = forms.ChoiceField(required=False, choices=EVENT_TYPE_CHOICES)
+
+    def clean_event_types(self):
+        return models.EventType.objects.filter(name__contains=self.cleaned_data['event_types'])

Je pars là dessus ? (par contre je visualise pas le menu à deux niveaux, si tu veux développer)

#11

Mis à jour par Benjamin Dauvergne il y a environ 3 ans

Je compte 6 types d'évènements qui ne sont couvert par rien de mieux que 'Tous':

user.service.sso
user.service.sso.authorization
user.service.sso.unauthorization
manager.user.creation
manager.user.email.change.request
manager.user.sso.authorization.deletion

il faut peut-être leur trouver une place dans ton classement (si c'est chiant de trouver une clé unique je me dit que tu pourrais avoir un truc de ce genre :
...
     ('user.email,profile.edit', _('Profile change')),
...
     patterns = choice.split(',')
     qs_filter = reduce(Q.__or__, (Q(name__contains=pattern) for pattern in patterns))
     return EventType.objects.filter(qs_filter)
)

Je verrais plutôt des groupes fonctionnels encore plus regroupés et ciblés :
  • Utilisateurs :
    • Connexion & SSO (login, creation, registration, sso, password change, password reset, front et back)
    • Mot de passe (password change, reset, front et back)
    • Modifications du profil (password change, email change, profile edit, en back ou front)
  • Backoffice
    • Tout
    • Gestion des rôles (je ne suis pas sûr que ce soit utile d'avoir plus fin, on peut toujours en ajouter plus tard)
    • Gestion des utilisateurs

À voir ce que les CPTs qui seraient amené à regarder dans le journal préfèrent aussi (si t'en as un ou deux autour de toi).

#12

Mis à jour par Valentin Deniaud il y a environ 3 ans

Benjamin Dauvergne a écrit :

il faut peut-être leur trouver une place dans ton classement (si c'est chiant de trouver une clé unique je me dit que tu pourrais avoir un truc de ce genre :
[...])

Oui OK pour intégrer ce bout de code.

À voir ce que les CPTs qui seraient amené à regarder dans le journal préfèrent aussi

Je crois qu'ils s'en fichent :/

Je verrais plutôt des groupes fonctionnels encore plus regroupés et ciblés :

OK mais autant que tu écrives ici le EVENT_TYPE_CHOICES de tes rêves, plutôt que j'essaye de guesser dans quel groupe tu imagines chaque type évènement ?

#13

Mis à jour par Benjamin Dauvergne il y a environ 3 ans

Valentin Deniaud a écrit :

Je verrais plutôt des groupes fonctionnels encore plus regroupés et ciblés :

OK mais autant que tu écrives ici le EVENT_TYPE_CHOICES de tes rêves, plutôt que j'essaye de guesser dans quel groupe tu imagines chaque type évènement ?

Modulo les autorisations de SSO que je vais rajouter et les motifs que je vais préciser celui que j'ai donné couvre tout il me semble

* Tout
* Utilisateurs :
** Connexion & SSO (*.login.*, *.user.creation, *.user.registration*, *sso*, *.password.*)
** Mot de passe (*.password.*)
** Modifications du profil (*.password.*, manager.user.*activation, manager.user.(password,profile,email).*, ^user.deletion,  ^user.profile.*)
* Backoffice :
** Tout (manager.*)
** Gestion des rôles (manager.user.*)
** Gestion des utilisateurs (manager.role.*)

#14

Mis à jour par Benjamin Dauvergne il y a environ 3 ans

  • Statut changé de Solution proposée à En cours
#16

Mis à jour par Benjamin Dauvergne il y a environ 3 ans

  • Statut changé de Solution proposée à Solution validée
#17

Mis à jour par Valentin Deniaud il y a environ 3 ans

  • Statut changé de Solution validée à Résolu (à déployer)
commit 91a190cda703e463ac4fb3ee59f153e17bfe4eb2
Author: Valentin Deniaud <vdeniaud@entrouvert.com>
Date:   Wed Mar 24 12:04:36 2021 +0100

    manager: easier journal filtering by event types (#50054)
#18

Mis à jour par Frédéric Péters il y a environ 3 ans

  • Statut changé de Résolu (à déployer) à Solution déployée

Formats disponibles : Atom PDF