Sujet sur Discussion Wikimedica:Tâches/Liste/69

Sauter à la navigation Sauter à la recherche

Intégration d’une bibliothèque déclarative avec une couche d’abstraction en Wikicode

13
Mattéo Delabre (discussioncontributions)

Pour reprendre ce que tu as dit @Antoine Mercier-Linteau dans le fil sur mxGraph:

Je propose donc d'y aller avec une librairie déclarative comme Mermaid (ou autre). Cette librairie serait appelée par un module (en Lua) qui s'occuperait de traduire le wikicode en la syntaxe comprise par la librairie. Cette petite couche d'abstraction supplémentaire nous permettra d'éventuellement changer de librairie si le besoin se fait sentir. La courbe d'apprentissage sera un peu plus abrupte pour les éditeurs, mais il pourront voir à même l'Éditeur Visuel le résultat de leurs manipulations.

Les paramètres passés au modèle pourraient avoir l'air de ceci:

{{Diagramme
          |Boîte 1<!-- Supporte du HTML arbitraire. -->
          |1_lien_1= Oui <!-- Supporte du HTML arbitraire. -->
          |1_lien_3=
          |groupe_1=1,2
          |2=Boîte 2
          |2_lien_3=
          |3=Boîte 3
          |raw=Passe la syntaxe directement à la librairie
          }}

Les éditeurs iraient donc construire les diagrammes en spécifiant des paramètres à un modèle à même l'éditeur visuel.

En syntaxe Mermaid:

 graph TD
  subgraph 1
  A[Boîte 1] -->|Oui|B(Boîte 2)
  end
  A --> |Non|C
  B --> C(Boîte 3)

Pour ce qui concerne spécifiquement Mermaid, il semblerait que ce soit une bibliothèque qui fonctionne uniquement côté client, ce qui imposerait de servir aux lecteurs les diagrammes sous forme de syntaxe Mermaid brute avec un script JS qui s’occupe de faire la transformation en SVG côté client. On avait déjà discuté d’un cas similaire précédemment et on était arrivés à la conclusion que ce n’était pas optimal que du JS soit nécessaire pour seulement voir les diagrammes.

Il y a bien mermaid-cli qui permet de convertir côté serveur la syntaxe Mermaid en SVG/PNG/PDF, mais elle fonctionne en lançant sur le serveur une instance de Chromium, en chargeant la bibliothèque Mermaid dans ce navigateur et en exportant le résultat. Ça me semble loin d’être optimal, et je n’ai rien trouvé d’autre pour convertir du Mermaid côté serveur. Bien sûr, ce serait possible de développer notre propre solution pour faire cette conversion, ce qui serait sans nul doute utile à la communauté, mais ça va prendre pas mal de temps.

Sinon, il nous reste GraphViz, mais le support de cette bibliothèque pour le HTML est pour le moins limité. Cela peut se comprendre puisqu’il devient d’autant plus difficile de calculer les dimensions externes d’une boîte (nécessaires pour savoir où placer chaque boîte) que le nombre de fonctionnalités HTML supportées croît. Finalement, pour supporter tout HTML, GraphViz aurait à réimplémenter tout un moteur de rendu HTML d’un navigateur, ce qui nous rapproche de la solution mermaid-cli.

Voici quelques ressources intéressantes relatives à GraphViz :

Si on voulait reproduire ton exemple fait avec Mermaid, voici ce que ça pourrait donner :

digraph {
    node[color="#9370DB", fillcolor="#ECECFF", fontname="Liberation Sans"];
    edge[fontname="Liberation Sans", color="#333333", arrowsize=.7];

    node[shape="box", style="filled"]; A;
    node[shape="box", style="rounded, filled"]; B; C;
    node[group="G1"]; A; B;

    A[label="Boîte 1"];
    B[label="Boîte 2", group="G1"];
    C[label="Boîte 3", group="G1"];

    A -> C [
        headlabel=<<TABLE BGCOLOR="#ECECFF" BORDER="0" CELLPADDING="0"><TR><TD>Non</TD></TR></TABLE>>,
        labeldistance=7, labelangle=0
    ];

    B -> C;

    subgraph cluster1 {
        label="1";
        style="filled";
        color="#AAAA33";
        fillcolor="#FFFFDE";

        A -> B [
            headlabel=<<TABLE BGCOLOR="#ECECFF" BORDER="0" CELLPADDING="0"><TR><TD>Oui</TD></TR></TABLE>>,
            labeldistance=2, labelangle=0
        ];
    }
}

Avec GraphViz, il n’est pas aisé de contrôler l’agencement des différentes boîtes, ce qui est lié au compromis qui est fait entre l’approche de Draw.io où tout le positionnement est fait à la main et l’approche déclarative où le positionnement est calculé par un algorithme.

Antoine Mercier-Linteau (discussioncontributions)

Embêtant...

Mermaid Graphviz
+
  • Extension simple et compatible avec MW 1.34
  • Positionnement automatisé
  • Support pour tout le HTML, incluant onClick en JS
  • Syntaxe simple
  • Rapide
  • Très flexible
  • Projet bien établi en activité depuis 2003
-
  • Interprétation de la syntaxe en JS
  • Utilisation headless possible, mais lourde
  • Extension incompatible avec MW 1.34
  • Une partie du positionnement est manuelle
  • Support pour un sous-ensemble réduit du HTML

J'ai l'impression que Mermaid est potentiellement la meilleure solution, principalement pour:

  • Support HTML: nous allons pouvoir lui fournir du wikicode arbitraire
  • Positionnement automatisé: on ne peut pas s'attendre à ce que les éditeurs soient en mesure de régler eux mêmes le positionnement
  • Syntaxe simple: la génération de graphes complexes sans passer par la couche d'abstraction en wikicode sera plus accessible
  • Nous demandera moins de travail d'intégration, car l'extension est d'emblée fonctionnelle et la syntaxe simple

J'ai testé mermaid-cli pour le rendu sur le serveur et c'est relativement rapide. Après, il serait possible ultérieurement d'améliorer l'extension pour qu'elle puisse faire du rendu sur le serveur et passer les SVG au client.

Tu en penses quoi? @Jppialasse?

Jppialasse (discussioncontributions)

graphviz fonctionnait pas mal avant l'update en 1.34. C'est un peu complexe comme syntaxe pour quelq'un faisant de l'édition visuelle, mais ce pense pas inapprochable. vous pouvez encore trouver des exemples sur mon ancienne page : https://wikimedi.ca/wiki/Utilisateur:Jppialasse/Archive

J'aurais juste précisé le point 5 du cahier des charges : par backend je pense que tu voulais dire pas de deamon complexe à installer à coté ou une myriade de librairies nodejs. En effet je suis vraiment pour que la génération se fasse coté serveur avec une librairie php ou quelque chose que php puisse utiliser directement sans avoir un service qui tourne pour cela. (eg l’éditeur visuel actuel, la génération de pdf)

Ainsi, je suis contre la génération coté client via JS: - on ne connais pas et on ne peut pas maitriser les capacités du client et donc garantir le résultat de l'affichage sur la diversité de client. Si quelqu'un désactive JS, ou se fait filtrer certaines url de librairies JS rien ne s'affiche ou n'importe comment. Idem probablement si on utilise un système comme Kiwix pour un acces hors ligne.


<graphviz caption="Hyponatrémie" alt="algorithme hyponatrémie" format="png" border='frame'> digraph hyponatremie {

node [shape=plaintext];
X0 [shape=point,label=""];
X1 [label="-Hyperglycémie\n-Mannitol"];
X2 [label="-Hypertriglycéridémie\n-Hyperprotéinémie"];
Euv1 [label="*Polydipsie primaire\n*Potomanie"];
Euv2 [label="*SIADH\n*Hypothyroidie\n*Insuff. surrénalienne\n(glucocorticoïdes)"];
Hypo1 [label="*vomissements\n*Diarrhée\n*3e espace\n*cutané\n*pulmomaire"];
Hypo2 [label="*Thiazides\n*Insuff surrénalienne\n(mineralocorticoïdes)\n*diurèse somotique"];
Hype1 [label="*Insuff. cardiaque\n*Insuff Hépatique\n*Sd néphrotique"];
Hype2 [label="*IRA\n*IRC"];
Extra_rénales [label="Extra-rénales"];
Euv1lab [label="Na(u)> 20mEq/L(variable)\n Osm(u) < Osm(P)"];
Euv2lab [label="Na(u)> 20mEq/L(variable)\n Osm(u) > Osm(P)"];
Hypo1lab [label="Na(u)< 10mEq/L(variable)\n Osm(u) > Osm(P)\n FE Na < 1%"];
Hypo2lab [label="Na(u)> 0mEq/L(variable)\n Osm(u) = Osm(P)\n FE Na >2%"];
Hype1lab [label="Na(u)< 10-20mEq/L(variable)\n Osm(u) > Osm(P)"];
Hype2lab [label="Na(u)> 0mEq/L(variable)\n Osm(u) = Osm(P)"];


Hyponatrémie->Isoosmolaire->X2
Hyponatrémie->Hypoosmolaire->X0
X0->Euvolémique->Euv1->Euv1lab
Euvolémique->Euv2->Euv2lab
X0->Hypovolémique->Rénales->Hypo1->Hypo1lab
Hypovolémique->Extra_rénales->Hypo2->Hypo2lab
X0->Hypervolémique->Hype1->Hype1lab
Hypervolémique->Hype2->Hype2lab
Hyponatrémie->Hyperosomolaire->X1


} </graphviz>

Antoine Mercier-Linteau (discussioncontributions)

Exact pour le backend! Également, la contrainte de la consultation du wiki offline en est une bonne et il faudra y penser bientôt.

Ceci dit, le support très limité pour le HTML dans graphviz (on a même pas accès aux liens, il faut passer par une image map...) est pour moi pas un killer. Tout comme le positionnement approximatif.

Mermaid nous permettrait d'avancer rapidement sur cette fonctionnalité et dans le futur, nous pourrons faire de la génération de diagrammes sur le serveur pour ne pas dépendre des clients.

Antoine Mercier-Linteau (discussioncontributions)

Dans tous les cas, il nous faut une couche d'abstraction que l'on choisisse Mermaid ou GraphViz. J'ai donc créé les page suivantes pour prendre cela en charge:

  • Modèle:Flowchart (plutôt que diagrammes, car nous allons potentiellement en créer d'autres types, comme les pie charts): ne fait surtout que rediriger au module afin de le rendre disponible dans l'éditeur visuel et bien le documenter
  • Module:Flowchart: module Lua dont la tâche sera d'adapter les paramètres du modèle Flowchart dans le langage de la librairie de visualisation
Mattéo Delabre (discussioncontributions)

J’ai repris le travail sur cette tâche après un hiatus de deux mois. Antoine vient de se charger de l’installation de l’extension Mermaid. Voici quelques éléments que j’ai relevés en programmant le module Lua.

  • Mermaid ne semble pas fournir une façon simple de forcer le niveau auquel un nœud est placé ou de demander à ce que deux nœuds soient au même niveau, pour obtenir des nœuds configurés de la même manière que l’exemple suivant donné par Antoine : . Une telle fonctionnalité est demandée dans cette issue et cette issue. Dans cette dernière, une solution de contournement est proposée à base de nœuds invisibles intermédiaires, à voir s’il est possible de générer automatiquement de tels nœuds dans le module Lua.
Mattéo Delabre (discussioncontributions)

Je viens d’ajouter une première version du Modèle:Flowchart. Voici par exemple comment pourrait être reproduit un flowchart que j’ai trouvé au hasard parmi les fichiers téléversés sur Wikimedica.

Version originelle

Version avec le modèle

Code

{{Flowchart
| non catég = Fichier<br>non catégorisé
| non catég.shape = rounded
| non catég -> perm inconnue = Ajout d’une description et<br>d’une condition d’utilisation

| perm inconnue = Fichier<br>de permission<br>inconnue
| perm inconnue.shape = rounded
| perm inconnue.style.fill = #ff9900
| perm inconnue -> peut utiliser ?

| peut utiliser ? = Ce fichier peut-il<br>être utilisé<br>sur Wikimedica?
| peut utiliser ?.shape = diamond
| peut utiliser ? -> perm inconnue = Je ne sais pas

| peut utiliser ? -> utilisable = Oui
  | utilisable = Fichier utilisable<br>(ou autre)
  | utilisable.style.fill = #00cc00

| peut utiliser ? -> académique ? = Non
  | académique ? = Ce fichier est-il<br>utilisé dans un<br>cadre académique?
  | académique ?.shape = diamond
  | académique ? -> exempté = Oui
    | exempté = Fichier exempté
    | exempté.shape = rounded
    | exempté.style.fill = #ff6666

  | académique ? -> remplaçable ? = Non
    | remplaçable ? = Est-il possible de<br>remplacer le fichier ?
    | remplaçable ?.shape = rounded
    | remplaçable ? -> à remplacer = Non
      | à remplacer = Fichier à remplacer
      | à remplacer.shape = rounded
      | à remplacer.style.fill = #ff6666

    | remplaçable ? -> remplacement = Oui
      | remplacement = Remplacer le fichier
      | remplacement.shape = rounded
      | remplacement.style.fill = #729fcf
}}

Rendu


J’ai pris un certain nombre de libertés avec la syntaxe des paramètres, j’ai essayé de faire quelque chose qui ne soit ni trop difficile à écrire ni trop difficile à parser. Une fois qu’on se sera accordé sur une syntaxe, je prendrai le temps d’écrire une documentation plus fournie du modèle. Dans l’état actuel, quatre types d’arguments sont possibles :

  • Paramètres du flowchart
    Préfixés d’un caractère $, ils se rapportent à l’apparence générale du flowchart.
    • $orientation=... définit la direction du flowchart, parmi to bottom (par défaut), to right, to top, to left.
    • $theme=... sélectionne un thème Mermaid, parmi neutral (par défaut), default, forest, dark.
    • $debug active le mode débogage dans lequel des informations sur la représentation interne et sur le code Mermaid généré sont affichées.


  • Définition d’un nœud du flowchart
    Sous la forme id nœud=Étiquette à afficher pour le nœud, quasiment tous les caractères sont autorisés dans l’identifiant du nœud, sauf ceux utilisés pour déclarer un lien, un paramètre général ou un groupe. On peut en plus définir des propriétés pour les nœuds, telles que :
    • id nœud.shape=... spécifie la forme du nœud, parmi rectangle (par défaut), rounded, circle, flag, diamond.
    • id nœud.group=... définit le groupe de nœuds auquel appartient ce nœud, par défaut aucun.
    • id nœud.style.fill=... couleur de remplissage du nœud, par défaut c’est celle du thème Mermaid actif.
    • id nœud.style.stroke=... idem pour la bordure du nœud


  • Ajout d’un lien entre deux nœuds
    Sous la forme id nœud d’origine -> id nœud d’arrivée, ou id nœud d’origine -> id nœud d’arrivée=Étiquette du lien pour ajouter une étiquette. Comme pour les définitions de nœuds, on peut définir des propriétés pour le lien, telles que :
    • id nœud d’origine -> id nœud d’arrivée.shape=... spécifie la forme du lien, parmi curve (par défaut), linear, step, step before, step after.
    • id nœud d’origine -> id nœud d’arrivée.ending=... peut valoir arrow pour ajouter une flèche au bout du lien (par défaut), ou plain pour ne rien y mettre.


  • Définition d’un groupe de nœuds
    Les nœuds sont ajoutés à un groupe en affectant leur propriété .group=id du groupe. On peut en plus spécifier l’étiquette du groupe avec la syntaxe group id du groupe=Étiquette du groupe (préfixé de group).
    Quelques remarques supplémentaires :
  • Les flowcharts Mermaid ne chargent pas dans l’éditeur visuel. On dirait un problème avec l’extension Mermaid.
  • Vue la nature dynamique des noms des arguments du modèle, je ne suis pas sûr qu’on puisse le documenter avec TemplateData, et ça risque de ne pas être évident à saisir depuis l’éditeur visuel. Voyez-vous une façon de modifier la syntaxe des arguments du modèle pour faciliter cela ?
  • À cause d’une limitation technique de Lua, l’ordre des paramètres du modèle (et donc des nœuds) n’est pas nécessairement préservé dans le flowchart. Peut-être qu’il serait possible de rajouter un paramètre du type order prenant une valeur numérique pour fixer l’ordre des nœuds, mais je pense que Mermaid aussi a tendance à réordonner les nœuds à sa guise.
  • Comme mentionné dans mon message précédent, il ne semble pas être possible de forcer le rang d’un nœud pour faire en sorte par exemple que deux nœuds apparaissent à la même hauteur. Ça se passe au niveau de bibliothèque de layout utilisée par Mermaid, Dagre.

Quel est votre avis là-dessus ?

Antoine Mercier-Linteau (discussioncontributions)

Excellent travail, voici mes commentaires:

  • J'irais avec la forme rounded par défaut.
  • L'intégration avec l'éditeur visuel n'est pas triviale, il va sûrement falloir coder un hook pour que le flowchart soit regénéré après chaque modification du modèle associé.
  • Pour la documentation des paramètres, on peut se contenter de les documenter de manière à afficher un exemple de départ.
    • Ensuite, il est simple de créer des paramètres arbitraires avec l'éditeur visuel alors je crois que ça va aller.
    • Reste qu'il y aura une petite courbe d'apprentissage dans l'utilisation de l'outil. Je m'occuperai d'écrire une page d'aide avec des exemples.
    • Pour les très gros Flowcharts, les éditeurs n'auront virtuellement pas le choix de passer par le wikicode.
  • Je crois que la syntaxe présente est très adéquate et suit la logique du Wikicode.
  • D'après mon analyse rapide, la génération de noeuds invisibles pour forcer le niveau d'un noeud est facilement codable. Je me suis d'ailleurs appliqué à le faire, mais ce n'est pas encore fonctionne.
Mattéo Delabre (discussioncontributions)

Merci pour ton retour Antoine.

  • Je suis d’accord pour rounded, j’ai modifié le défaut en ce sens.
  • Concernant l’éditeur visuel, je vais me renseigner, ce sera une bonne occasion pour moi d’en apprendre davantage sur son fonctionnement.
  • OK pour la documentation.
  • Merci pour les nœuds invisibles. J’ai fouillé un peu dans la bibliothèque sous-jacente qui s’occupe du layout, Dagre, et il y a un paramètre minlen qui permet de forcer un nombre d’« étages » minimum que doit occuper un lien. Seulement, ce paramètre n’est pas exposé par Mermaid, je vais voir si je peux contribuer un patch pour permettre ça. Je pense que ce serait une solution plus robuste à long terme que les nœuds invisibles.
Mattéo Delabre (discussioncontributions)
Antoine Mercier-Linteau (discussioncontributions)

Wow! Merci infiniment, je te donne mes commentaires bientôt. J'ai brûlé tout mon temps de codage sur la recherche d'une solution pour faire fonctionner les flowcharts dans les discussions. Il semble que ce soit un conflit entre l'extension Flow (pour la discussion) et Mermaid où Flow interfère avec les librairies chargées par Mermaid.

Afin de pallier à ce problème, j'ai forcé le chargement de l'extension dans MediaWiki:Common.js. Très bancale, vu que c'est exécuté pour chaque page. Et en plus, ça ne fonctionne même pas à tout coup. Un bogue a été soumis auprès des développeurs de l'extension.

Mattéo Delabre (discussioncontributions)

Merci d’avoir pris le temps de regarder ça ! Je ne sais pas si c’est lié à mon setup, mais on dirait que le texte du flowchart ne charge pas sur les pages de discussion. Voici comment ça s’affiche chez moi :

Antoine Mercier-Linteau (discussioncontributions)

Oui, j'ai vu ça à quelques reprises. De mon côté, avec un refresh ça finit par s'afficher correctement, ce qui laisse présager un problème de timing. D'autant plus que ça semble s'améliorer en ajoutant un délai sur l'exécution du code. Bref, ma solution est vraiment bancale. En espérant que les développeurs pourront régler le bogue. Entre-temps, j'ai fait passer le délai de 1 à 3 secondes, est-ce que ça corrige le problème chez toi?

Ceci dit, ce n'est pas très handicapant pour le moment vu que seulement les pages de discussion sont affectées. Je vais quand même garder un oeil ouvert.