Aller au contenu

12. Automatiser

L’étape précédente a écrit une relance en Python. Elle marche, elle est testée, et elle a un défaut : pour changer le délai, le destinataire ou le texte, il faut un développeur et un redéploiement.

Kewos a un deuxième outil pour ça : le moteur de flux, dans Workflows. Un déclencheur, des conditions, des actions, reliés sur une toile. Aucun code, et le client peut l’ouvrir et le modifier lui-même.

Cette étape livre un flux avec le module, pour que l’automatisation existe dès l’installation au lieu d’être à rebâtir sur chaque instance.

Les deux ne servent pas à la même chose

Ce n’est pas « l’un remplace l’autre ». La frontière est nette et vaut la peine d’être apprise une fois :

Le code de l’étape 11Un flux
Ce qui le lancel’horloge (tous les matins)un événement : une fiche créée, modifiée, supprimée, ou un clic
Qui peut le changerun développeurl’utilisateur, dans l’écran Workflows
Ce qu’il sait fairetout ce que Python sait faireles blocs disponibles : condition, aiguillage, créer, modifier, transition, courriel, notification, agent IA
Où il tournedans la tâche planifiéedans la file de travaux, en asynchrone

Un flux n’a pas de déclencheur horaire. Les déclencheurs sont record.created, record.updated, record.deleted et « manuel ». Balayer tous les emprunts chaque matin reste donc le travail du code de l’étape 11. En revanche, réagir au moment où un exemplaire est déclaré perdu, c’est exactement un flux.

Le fichier

Un module livre ses flux dans flows/, à côté de views/. Rien à appeler : le noyau lit ce dossier au chargement du module, comme il lit onboarding.json.

modules/library/flows/lost_copy_alert.json :

{
"ref": "library.lost_copy_alert",
"module": "library",
"name": "Tell the desk when a copy is declared lost",
"description": "A lost copy is a decision someone has to act on.",
"nodes": [
{
"node_key": "loan_changed",
"kind": "trigger.record_updated",
"name": "A loan changed",
"config": { "models": ["library_loan"] },
"pos_x": 40,
"pos_y": 120
},
{
"node_key": "is_it_lost",
"kind": "logic.condition",
"name": "Is it lost?",
"config": { "expression": { "==": [{ "field": "state" }, "lost"] } },
"pos_x": 320,
"pos_y": 120
},
{
"node_key": "tell_the_desk",
"kind": "action.send_notification",
"name": "Tell the desk",
"config": {
"recipients": [{ "type": "group", "id": "library_librarian" }],
"title": "A copy was declared lost",
"body": "Loan {{ trigger.record.id }} was marked lost."
},
"pos_x": 620,
"pos_y": 60
}
],
"edges": [
{ "from_node": "loan_changed", "to_node": "is_it_lost" },
{ "from_node": "is_it_lost", "to_node": "tell_the_desk", "branch": "yes" }
]
}

Quatre choses méritent d’être lues lentement.

ref. C’est l’identité du flux. Le noyau l’installe une fois et ne le réécrit jamais : après la première installation, le flux appartient au client. Renommez-le, changez la condition, ajoutez une branche : une montée de version ne défera rien. C’est l’inverse d’une vue, qui est réécrite à chaque démarrage.

Il arrive éteint. Un flux livré est en brouillon. Installer un module ne doit jamais se mettre à envoyer des messages à quelqu’un sans que personne ne l’ait décidé. C’est un interrupteur dans l’écran Workflows.

Le groupe se désigne par son code, pas par un identifiant numérique : library_librarian existe sur toutes les instances, l’identifiant 3 ne veut rien dire ailleurs.

"branch": "yes". Une arête n’est suivie que si son libellé correspond exactement à ce que le bloc a renvoyé. Une condition renvoie yes ou no. Écrire true, ce que tout le monde écrit la première fois, ne produit aucune erreur : le flux s’arrête simplement après la condition. Le noyau refuse maintenant ce fichier au chargement, avec le message qui dit quelles branches le bloc renvoie vraiment. Ce garde-fou existe parce que l’exemple de cette page l’avait, lui aussi, écrit true.

Ce que ça donne

Après kewos reconcile, le flux est là, sans avoir touché à l’interface :

La liste des workflows : le flux livré par le module, marqué actif

Et il s’ouvre comme n’importe quel flux dessiné à la main. Le client peut ajouter un envoi de courriel, changer la condition, ou le débrancher :

La toile du flux : déclencheur, condition, notification, avec la branche « yes »

Le faire tourner pour de vrai

Déclarez un exemplaire perdu depuis la fiche d’emprunt (le bouton Declare lost de l’étape 6). Puis regardez :

Fenêtre de terminal
# The run, and each step it went through.
psql -c "select id, flow_id, state from kws_flow_run order by id desc limit 1"
psql -c "select node_key, state from kws_flow_run_step order by id"
id | flow_id | state
----+---------+---------
1 | 1 | success
node_key | state
---------------+---------
is_it_lost | success
tell_the_desk | success

Et la notification est arrivée, avec le {{ trigger.record.id }} résolu :

{"title": "A copy was declared lost", "body": "Loan 1 was marked lost."}

Le piège qui ne dit rien

Un flux ne s’exécute pas dans la requête qui l’a déclenché. Il est mis dans la file de travaux, et c’est le worker qui le déroule. Sans worker, le message reste dans la file : aucune erreur, aucune ligne dans kws_flow_run, rien dans l’interface. Le flux a l’air de ne pas fonctionner alors qu’il attend.

En écrivant cette page, la première exécution n’a rien produit exactement pour cette raison. La vérification tient en une ligne :

Fenêtre de terminal
redis-cli LLEN dramatiq:default # > 0 with no worker running
kewos worker # start one, and the queue drains

Ce que le client peut faire ensuite

Rien de ce qui suit ne demande de développeur :

  • ajouter un action.send_mail au membre à côté de la notification ;
  • remplacer la condition par un aiguillage sur l’état et traiter returned autrement ;
  • brancher un ai.agent qui rédige le message ;
  • désactiver le flux pendant l’été.

C’est le point de la manœuvre : ce que vous livrez est un point de départ, pas une règle gravée.

Et ensuite

Le module agit seul et réagit aux événements. Reste ce qu’il doit savoir faire des fichiers : Les fichiers.