Projet

Général

Profil

Development #13812

Système de notification

Ajouté par Thomas Noël il y a plus de 7 ans. Mis à jour il y a plus de 5 ans.

Statut:
Fermé
Priorité:
Normal
Assigné à:
Version cible:
-
Début:
31 octobre 2016
Echéance:
% réalisé:

0%

Temps estimé:
Patch proposed:
Oui
Planning:

Description

Pour un utilisateur, on veut afficher des messages qui viennent de Publik, à faible durée de vie, sous forme de notications.

Exemples en front:
  • les dernières factures à payer
  • les messages généraux de la ville (auxquels on est abonné)
  • les messages spécifiques concernant des demandes
En backoffice:
  • les dernières demandes à traiter
  • les messages spécifiques concernant certaines demandes (retour utilisateur dispo, etc)

Il faut donc : un modèle de données, de quoi l'alimenter (API et webservices), un affichage (avec éventuellement utilisation de https://developer.mozilla.org/en-US/docs/Web/API/Notification)


Fichiers

0001-notification-v0.patch (8,24 ko) 0001-notification-v0.patch Thomas Noël, 31 octobre 2016 18:28
0001-notification-v0.1.patch (9,28 ko) 0001-notification-v0.1.patch Thomas Noël, 01 novembre 2016 19:51
0001-notification-v0.2-ws.patch (13 ko) 0001-notification-v0.2-ws.patch Thomas Noël, 02 novembre 2016 17:32
0001-add-notification-system-13812.patch (23,6 ko) 0001-add-notification-system-13812.patch Thomas Noël, 09 novembre 2016 01:38
0001-add-notification-system-13812.patch (26,1 ko) 0001-add-notification-system-13812.patch Thomas Noël, 09 novembre 2016 22:44
0001-add-notification-system-13812.patch (27,1 ko) 0001-add-notification-system-13812.patch Thomas Noël, 10 novembre 2016 12:13

Demandes liées

Lié à Combo - Development #13912: faire de lingo.check_request_signature une fonction dans utilsFermé09 novembre 2016

Actions

Révisions associées

Révision 963bc1fa (diff)
Ajouté par Thomas Noël il y a plus de 7 ans

add notification system (#13812)

Historique

#1

Mis à jour par Thomas Noël il y a plus de 7 ans

#2

Mis à jour par Frédéric Péters il y a plus de 7 ans

Attention à ne pas confondre notification et message, pour moi une notification ne doit (presque) pas avoir de contenu; par exemple pour moi on aura "nouvelle facture xxx à payer" mais pas de contenu, ça sera juste un lien vers la page présentant les factures à payer; ou bien "la demande xxx a été clôturée", lien vers la demande. Et donc les messages généraux de la ville, je ne les verrais pas dans ce cadre.

#3

Mis à jour par Thomas Noël il y a plus de 7 ans

Frédéric Péters a écrit :

Et donc les messages généraux de la ville, je ne les verrais pas dans ce cadre.

Oui. Je pensais aux messages "à la SMS", qui pointent éventuellement vers le "vrai message" (une page plus complète). Exemple typique, les grèves dans l'école des enfants. Ou bien "Ouverture de la chasse aux oeufs demain à 12h !".

#4

Mis à jour par Frédéric Péters il y a plus de 7 ans

Ok; rapide proposition :

Modèle (en partant sur des termes employés dans la notification spec) : summary / body / url / urgency [low/normal/critical] + acked et creation_timestamp.

API :

  • notify(summary, body=None, url=None, urgency=normal, replaces_id=None) → notification_id
  • remove_notification(notification_id)
#5

Mis à jour par Thomas Noël il y a plus de 7 ans

j'ai vide fait ça, la suite après les bonbons.

#6

Mis à jour par Frédéric Péters il y a plus de 7 ans

ici le script qui charge et recharge les notifications toutes les 30 secondes

En fait tu peux juste poser ajax_refresh = 30 dans ta cellule.

</div><!-- sera remplie par le js -->

Pareil ici, fais la cellule de manière statique. Si on veut de l'HTML pour transfomer les nouvelles notifications en notification javascript/desktop, il y a un événement combo:cell-loaded qui est lancé après le chargement d'une cellule, on aurait alors du js qui l'attraperait et aurait gardé trace des id des notifs déjà présentes et verrait grâce à ça les nouvelles. (mais perso, pas convaincu d'avoir besoin de notifs desktop, on est dans des temps quand même différents).

#7

Mis à jour par Thomas Noël il y a plus de 7 ans

Voici, en suivant ces conseils (il faut que je fasse plus de combo pour mieux attraper ces automatismes).

Il manque plein de trucs, notamment pour que ça commence à ressembler à quelque chose, du CSS+js pour effacer une notification.

(Les tests, aussi, of course.)

Je veux bien des avis sur le modèle. Particulièrement, le "seen" me semble finalement sans intérêt (autant carrément effacer les notifications "vues").

#8

Mis à jour par Frédéric Péters il y a plus de 7 ans

title = models.CharField(_('Label'), max_length=140), j'aimais bien le nom summary de la NotificationSpec, qui implique moins une notion d'hiérarchie.

body = models.TextField(_('Body'), default='') ça ne demande pas un blank=True ?

start_timestamp = models.DateTimeField(), il peut y avoir dedans un auto_now=True. Et je ne sais pas si on veut un start_timestamp et permettre la création de notifications qui n'apparaitront qu'à un moment donné, pour moi un creation_timestamp était suffisant.

seen = models.BooleanField(default=False), je pense que oui c'est utile d'avoir cet attribut (et ne pas faire disparaitre les notifications trop facilement); mais je préférais "acked", pour signifier la nécessité d'intervention active de l'usager.

tag = models.CharField(_('Tag'), max_length=32); je ne suis pas sûr, c'est quoi l'idée derrière ? (opposé à simplement utiliser l'id qui serait retourné)

#9

Mis à jour par Thomas Noël il y a plus de 7 ans

Frédéric Péters a écrit :

title = models.CharField(_('Label'), max_length=140), j'aimais bien le nom summary de la NotificationSpec, qui implique moins une notion d'hiérarchie.

Ok pour summary.

body = models.TextField(_('Body'), default='') ça ne demande pas un blank=True ?

Apparement non (mais j'ai pas relu la doc de TextField). Je testerais.

start_timestamp = models.DateTimeField(), il peut y avoir dedans un auto_now=True. Et je ne sais pas si on veut un start_timestamp et permettre la création de notifications qui n'apparaitront qu'à un moment donné, pour moi un creation_timestamp était suffisant.

En fait j'ai posé dans le save() ce qu'il faut pour faire le auto_add_now ; mais en laissant la possibilité d'imposer une date. J'avoue avoir imaginé que chrono pose des rappels de rendez-vous avec ça.

seen = models.BooleanField(default=False), je pense que oui c'est utile d'avoir cet attribut (et ne pas faire disparaitre les notifications trop facilement); mais je préférais "acked", pour signifier la nécessité d'intervention active de l'usager.

Ok.

tag = models.CharField(_('Tag'), max_length=32); je ne suis pas sûr, c'est quoi l'idée derrière ? (opposé à simplement utiliser l'id qui serait retourné)

Alors justement c'est pour éviter à un système distant de s'embêter à stocker un id externe, en lui permettant d'en imposer un au système de notification. Cas typique : les alertes sur les factures, lingo va envoyer sont propre tag "lingo-regie-<R>-new-invoice-<I>" et pourra donc faire référence à cette notification sans avoir besoin de connaitre autre chose que ce tag local. Bon, ça demande à ce que le système tiers soit "intelligent" et gère l'unicité des tags, ce qui est le cas pour les factures par exemple, mais aussi wcs et ses démarches, fargo et les documents qu'il reçoit, chrono et ses rendez-vous, corbo et ses messages, ...

Bref, ça évite de retenir des notification_id dans lingo, wcs, fargo, chrono, etc.

À un moment j'avais été plus loin en ajoutant aussi un champ "sender" sensé contenir le nom du système emetteur (lingo, wcs, etc). Ca m'a finalement paru un peu lourd ; j'ai laissé la charge à ceux qui imposent des "tag" d'utiliser un préfix/namespace.

#10

Mis à jour par Frédéric Péters il y a plus de 7 ans

start_timestamp = models.DateTimeField(), il peut y avoir dedans un auto_now=True. Et je ne sais pas si on veut un start_timestamp et permettre la création de notifications qui n'apparaitront qu'à un moment donné, pour moi un creation_timestamp était suffisant.

En fait j'ai posé dans le save() ce qu'il faut pour faire le auto_add_now ; mais en laissant la possibilité d'imposer une date. J'avoue avoir imaginé que chrono pose des rappels de rendez-vous avec ça.

Ok pour l'usage.

tag = models.CharField(_('Tag'), max_length=32); je ne suis pas sûr, c'est quoi l'idée derrière ? (opposé à simplement utiliser l'id qui serait retourné)

Alors justement c'est pour éviter à un système distant de s'embêter à stocker un id externe, en lui permettant d'en imposer un au système de notification. Cas typique : les alertes sur les factures, lingo va envoyer sont propre tag "lingo-regie-<R>-new-invoice-<I>" et pourra donc faire référence à cette notification sans avoir besoin de connaitre autre chose que ce tag local. Bon, ça demande à ce que le système tiers soit "intelligent" et gère l'unicité des tags, ce qui est le cas pour les factures par exemple, mais aussi wcs et ses démarches, fargo et les documents qu'il reçoit, chrono et ses rendez-vous, corbo et ses messages...

Ok aussi mais j'aimerais autre chose que "tag" comme nom, peut-être external_id ?

#11

Mis à jour par Thomas Noël il y a plus de 7 ans

Frédéric Péters a écrit :

Ok aussi mais j'aimerais autre chose que "tag" comme nom, peut-être external_id ?

Ca me va.
On le laisse vide quand il n'y en a pas défini.
Et lors d'un remove_notify(id=xxx) on cherche l'id dans les external_id et dans les id.
Ok ?

(et pour me dédouaner, le nom "tag" venait de https://developer.mozilla.org/en-US/docs/Web/API/notification)

#12

Mis à jour par Frédéric Péters il y a plus de 7 ans

Thomas Noël a écrit :

Ca me va.
On le laisse vide quand il n'y en a pas défini.
Et lors d'un remove_notify(id=xxx) on cherche l'id dans les external_id et dans les id.
Ok ?

J'en étais même à me dire qu'on pouvait juste se limiter aux external_id, jamais exposer d'id spécifique.

#13

Mis à jour par Thomas Noël il y a plus de 7 ans

Voici une version avec l'API (Python) qui marche. Tests à venir.

Pour l'API webservices j'ai commencé la partie qui va vérifier la signature de l'appelant et rechercher l'utilisateur nommé (nameid= ou email=). L'API pourra aussi fonctionner sans signature et dans ce cas notifier l'utilisateur de la session (principalement pour le remove ou le ack qui seront appelées en javascript quand l'utilisateur agit sur ses notifications affichées).

#14

Mis à jour par Thomas Noël il y a plus de 7 ans

À noter que j'ai déplacé la logique de création des notifications dans api.py (avec ou sans id, avec ou sans dates, etc.) au lieu de faire des choses cachées dans le save() du modèle.

#15

Mis à jour par Thomas Noël il y a plus de 7 ans

  • Lié à Development #13912: faire de lingo.check_request_signature une fonction dans utils ajouté
#16

Mis à jour par Thomas Noël il y a plus de 7 ans

  • Fichier 0004-add-notification-system-13812.patch ajouté
  • Statut changé de Nouveau à En cours
  • Patch proposed changé de Non à Oui

Patch avec les webservices et des tests, à lire et relire.

Il manque une partie HTML propre avec un peu de CSS, et du JS associé pour que l'usager puisse cliquer sur une notification pour la acker puis l'effacer (de simple appels GET en ajax sur (ack|remove)/public_id). Mais bon je suis assez mauvais pour ça, si y'a une âme charitable...

Cela suit #13812 qui déplace le check de signature de lingo et dans combo.utils.

#17

Mis à jour par Thomas Noël il y a plus de 7 ans

  • Fichier 0004-add-notification-system-13812.patch supprimé
#18

Mis à jour par Thomas Noël il y a plus de 7 ans

Un fichier inutile traînait dans le patch précédent, voici une version corrigée.

#19

Mis à jour par Thomas Noël il y a plus de 7 ans

  • Patch proposed changé de Oui à Non
notes de ce matin:
  • renommer l'appli : notifications (avec un s final)
  • intégrer api.py dans models.Notification
  • passer par DRF pour les webservices / avec deux autorisations, Publik puis Session (parce que #13914)
  • Fred est volontaire pour HTML/CSS/JS
#20

Mis à jour par Thomas Noël il y a plus de 7 ans

Voici.

J'ai supprimé de l'API remove pour le remplacer par forget : la notification n'est pas effacée mais sa date d'expiration est posée dans le passé. Ca permet de conserver l'info que l'usager a été notifié. Il faudra prévoir un mécanisme de nettoyage des vieilles notifs (genre, expirées depuis 15 jours) ; amélioration à faire après que ce patch sera validé (?).

Le HTML reste l'ultra-sommaire version actuelle qui ne fait que de l'affichage, mais peut-être que le patch peut être poussé ainsi et que des améliorations viendront le compléter asap.

#21

Mis à jour par Frédéric Péters il y a plus de 7 ans

Thomas Noël a écrit :

J'ai supprimé de l'API remove pour le remplacer par forget : la notification n'est pas effacée mais sa date d'expiration est posée dans le passé. Ca permet de conserver l'info que l'usager a été notifié. Il faudra prévoir un mécanisme de nettoyage des vieilles notifs (genre, expirées depuis 15 jours) ; amélioration à faire après que ce patch sera validé (?).

Yep.

Le HTML reste l'ultra-sommaire version actuelle qui ne fait que de l'affichage, mais peut-être que le patch peut être poussé ainsi et que des améliorations viendront le compléter asap.

Oui, mais a minima quand même, passer par gettext pour <h2>Notifications</h2>

<div> (c'est le premier div, qui englobe le tout), je ne le mettrais pas, s'il était nécessaire avant, ça doit être réparé par #13816. (et si ce n'est pas réparé j'en serais même à ne pas mettre de ajax_refresh au début).

<br/>{{ notification.body }}, pour afficher du texte non formatté, je me suis récemment mis à employer |linebreaks, pour que les retours à la ligne passent à la ligne et doubles retours fassent des <p>, et j'aime bien. (après, oui, ça va sans doute donner plus d'espaces, à cause des marges des <p>, mais ça s'arrangera via css).

Quand même déjà ajouter un get_badge, qui retourne le nombre de notifications non ackées/supprimées de l'usager ? (ce qui me fait dire que les badges devraient également se rafraichir par ajax).

+     'rest_framework',

Le monter au-dessus des 'combo.whatever' ?

~~

Question plus large qui me vient maintenant, on n'a pas la possibilité de créer de notifications "globales", qui s'afficheraient pour tous les usagers, y compris les anonymes. (je pense à une notif genre "downtime prévu"). Ça pourrait être dans le périmètre de cette infra ? On peut dire non et que s'il y a un message public, il peut l'être dans une cellule texte, ou publié dans un fil rss, etc. Là où j'imaginais le truc sympa, c'est que ça permet ainsi d'afficher une notif même pour les gens qui ne passeraient pas par le portail, et on appellerait combo-manage tenant_command wall --all "On redémarre le système dans cinq minutes." en début de procédure de mise à jour et ça apparaitrait partout.

#22

Mis à jour par Thomas Noël il y a plus de 7 ans

Frédéric Péters a écrit :

Oui, mais a minima quand même, passer par gettext pour <h2>Notifications</h2>

Évidemment, merci.

<div> (c'est le premier div, qui englobe le tout), je ne le mettrais pas, s'il était nécessaire avant, ça doit être réparé par #13816. (et si ce n'est pas réparé j'en serais même à ne pas mettre de ajax_refresh au début).

Je l'ai retiré et supprimé le ajax_refresh qui n'est pas encore bien pertinent.

<br/>{{ notification.body }}, pour afficher du texte non formatté, je me suis récemment mis à employer |linebreaks, pour que les retours à la ligne passent à la ligne et doubles retours fassent des <p>, et j'aime bien. (après, oui, ça va sans doute donner plus d'espaces, à cause des marges des <p>, mais ça s'arrangera via css).

|linebreaks adopté.

Quand même déjà ajouter un get_badge, qui retourne le nombre de notifications non ackées/supprimées de l'usager ? (ce qui me fait dire que les badges devraient également se rafraichir par ajax).

Voilà avec get_badge qui affiche "non-ack/total" (nombre de notifications nouvelles/total), uniquement s'il y a des nouvelles notifs.

~~

Question plus large qui me vient maintenant, on n'a pas la possibilité de créer de notifications "globales", qui s'afficheraient pour tous les usagers, y compris les anonymes. (je pense à une notif genre "downtime prévu"). Ça pourrait être dans le périmètre de cette infra ? On peut dire non et que s'il y a un message public, il peut l'être dans une cellule texte, ou publié dans un fil rss, etc. Là où j'imaginais le truc sympa, c'est que ça permet ainsi d'afficher une notif même pour les gens qui ne passeraient pas par le portail, et on appellerait combo-manage tenant_command wall --all "On redémarre le système dans cinq minutes." en début de procédure de mise à jour et ça apparaitrait partout.

J'y ai pensé. Ca pourrait se faire avec un user=None au niveau de la notif, mais ce genre de notification ne sera pas "ackable/forgettable" par chaque usager, et ça m'ennuie de modifier le code pour prendre le cas user=None en charge. Je préférerai qu'on envoie déjà cela ainsi, et qu'on voit si on peut y ajouter le cas de notifs globales ensuite.

#23

Mis à jour par Frédéric Péters il y a plus de 7 ans

Quand pas de notif, ne pas mettre de <ul> mais plutôt un <p> avec un message disant qu'il n'y a aucune notif.

#24

Mis à jour par Frédéric Péters il y a plus de 7 ans

Pour l'intégration la plus facile avec ce qu'il y a dans publik-base-theme aujourd'hui :

<h2>{% trans "Notifications" %}</h2>
{% if notifications %}
<ul>
  {% for notification in notifications %}
  <li class="combo-notification {% if notification.acked %}combo-notification-acked{% endif %}" 
      data-combo-notification-id="{{ notification.public_id }}">
    <a href="{{ notification.url|default:"#" }}">{{ notification.summary }}</a>
    {% if notification.body %}
    <div class="description">
    {{ notification.body|linebreaks }}
    </div>
    {% endif %}
  </li>
  {% endfor %}
</ul>
{% else %}
<p>{% trans 'No notifications.' %}</p>
{% endif %}
#25

Mis à jour par Thomas Noël il y a plus de 7 ans

  • Statut changé de En cours à Résolu (à déployer)
commit 963bc1faf7ccc8e6220d928d503592212d554961
Author: Thomas NOEL <tnoel@entrouvert.com>
Date:   Wed Nov 9 01:36:20 2016 +0100

    add notification system (#13812)

#26

Mis à jour par Frédéric Péters il y a plus de 5 ans

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

Formats disponibles : Atom PDF