Sujet, Prédicat, Object : valeurs, audit et historisation

Cet article est une réflexion faisant suite à l’article concernant l’explication de mon approche ontologique et de la structure des données du projet Folkopedia avec un retour d’expériences.

Initialement nous avons notre ligne ontologique telle que :

S, P, O, V, Qui, Quand, Visibilité, Ordre

Avec pour lecture :

  • Sujet
  • Prédicat
  • Objet au sens pointeur vers un sujet
  • Valeur primitive de l’objet (c’est valeur ou objet mais pas les 2 en même temps)
  • Qui a créé ou éditer la ligne en dernier
  • Quand est-ce que ça été créé ou édité en dernier
  • Visibilité au sens de qui peut consulter cette valeur
  • Ordre au sens de l’ordonnancement des prédicats et valeurs

Les problèmes rencontrés

  1. D’abord un truc gênant concernant l’ordre, celui-ci n’est pas toujours nécessaire et c’est donc une colonne qui n’a pas toujours de sens.
  2. Dans le cas où un prédicat a plusieurs valeurs, celle-ci ne sont pas ordonnées, excepté par l’ordre d’insertion.
  3. La valeur elle-même peut avoir une historisation comme discuté ce qui demande un Sujet, du coup quid de la valeur primitive ?
  4. Comment être SPO sans avoir 2 colonnes pour respecter les théories SGBD ? Comment gérer les 2 en 1 et différencier au bon moment ?
  5. L’audit Qui et Quand n’est pas historisé, ce qui est un manque à l’objectif, pareil pour Visibilité et Ordre.

Solutions ?

Déjà reprenons le point n° 2. Elles peuvent être ordonnée si on utilise la colonne Ordre. Là où elle ne le sont pas, c’est dans le système actuel, où le fait d’avoir un objet ayant pour clef le prédicat et les valeurs simplifiées en tableau perdent cette notion. De plus la requête actuelle ne l’extrait pas.

Ceci mis à part, le constat est que les colonnes Qui, Quand, Visibilité et Ordre ne sont pas correcte, supprimons les et regardons comment respecter l’énoncée de départ.

S, P, O, V

Si on projette un exemple, nous aurons :

SujetPrédicatObjetValeur
#entity1SurnomnullKillan

Là on a une Valeur primitive pour un Prédicat d’un Sujet. Mais si on veut rajouter les 4 colonnes retirées, en suivant le modèle ontologique, nous allons devoir faire ainsi :

SujetPrédicatObjetValeur
#entity1Surnom#entity2null
#entity2AuditQui#entity1null
#entity2AuditQuandnull2025-02-13 21:08…
#entity2Visibilité#entity39null
#entity2Ordrenull1

Visibilité pourrait même être défini au niveau du Prédicat, par défaut, et supplanté par l’utilisateur potentiellement. La valeur disparait au profit d’une nouvelle entité contenant les informations relatives, mais la valeur a disparu, on la rajoute :

SujetPrédicatObjetValeur
#entity2ValeurnullKillan

Dès lors nous avons tout à nouveau, on est complet et il n’y a plus qu’à faire une requête pour chaque prédicat, simple… POUR CHAQUE PRÉDICAT ! Déjà que la console du système n’est pas triste avec le peu qu’on a fait jusque là, mais si en plus on double… Et en plus il faudra potentiellement trier les lignes reçues pour les identifier d’une certaine manière par les prédicats.

Réflexions sur la valeur

Si on observe le résultat, dans ce cas-ci, Objet est utilisé 50% du temps et Valeur 50%, équilibré en soit, mais plus largement ça sera surtout la colonne Objet qui sera attribuée. La valeur primitive est largement moins sollicitée.

De plus, si nous avons comme contenu l’historique complet d’un groupe folklorique, qui plus est formatté par exemple, nous allons avoir une quantité folle de données dans ce champs de manière anecdotique et là je me dis qu’on passe à côté de quelque chose.

On est d’accord qu’on n’enregistre pas un document ou une image dans ce champs, du coup, les contenus potentiellement massif, ou identifiés comme tels, ne devraient pas y être non plus. Cela n’a pas d’intérêt ontologique ou structurel.

Nous avions imaginé qu’une image serait parsemée de légendes, de tags, et de commentaires, ce qui à la fois contient des informations larges et d’autres courtes. Le tout pouvant faire l’objet d’une recherche. On peut partir dans tous les sens alors car il faut indexer du contenu pour chercher dedans. Pour ce faire, ce n’est pas à l’ontologie de s’en occuper, elle ne garanti que le lien entre les informations et la structure générale par ses définitions.

On peut imaginer un système annexe comme une table de contenus, ou un système comme un Elasticsearch. Le système de recherche ferait une demande à ces systèmes qui permettrait de relier un (ou plusieurs) Sujet(s) de l’ontologie. Ça semble cohérent.

Si on extrait les contenus large comme des documents et des images, il va falloir penser à une technique pour les relier dans l’ontologie. Comment écrire que la Valeur ou l’Objet fait référence à une structure externe ?

subject UUID not null,
predicate UUID not null,
object UUID,
o_value TEXT,

Petit extrait de notre table ontology pour vous montrer que les colonnes sont fortement typées. On ne peut pas indiquer n’importe quoi dans object. Et o_value, sur base de ce que l’on vient de dire, devrait devenir un varchar dont la longueur X est encore à définir, en soit si inférieur à X alors valeur courte et si supérieur à X alors contenu large externalisé.

Soit on garde la théorie SGBD avec notre clé secondaire (FK) pointant vers une clé primaire (PK), en dur dans le marbre bien respectueux et on garde une colonne Valeur en tant que varchar. Soit on transforme les 2 en une seule, de type JSON ou varchar. L’avantage du varchar est l’indexation pour définir un index et/ou une PK globale, permettant de valider l’unicité du Triplet. Avantages et inconvénients de chaque approche.

On peut également partir sur le principe de diviser la table en 2-3 pour séparer les colonne du fait de l’exclusivité mutuelle. Une table SPO et une table SPV, voire une table SP de référence centrale. C’est le code qui doit alors rassembler les morceaux. (Clin d’œil à Julian pour le rappel)

Là où ça se corse, quelque soit la solution, c’est l’identification extérieure. Comment interpréter Valeur ? On pourrait préfixer le contenu, tel que :

  • doc:refdoc#1
  • img:refimg#1
  • txt:reftxtlarge#1
  • v:Killan

Le symbole « deux points » ( : ) nous permettant, telle une sérialisation, de faire la part des choses, sans empêcher l’usage dudit caractère dans sa partie droite, à vérifier bien sur ou a encapsuler dans des guillemets (  »  » ). Attention tout de même de ne pas devenir une usine pour peu d’intérêts. Les images, textes et documents font partie du cœur de l’objectif, donc jusque là pas de soucis, on en prend soin.

On peut imaginer que la partie ref (refdoc#1, refimg#1, reftxtlarge#1) pointe sur une table autre que ontology avec comme valeur un chemin vers le fichier avec peut-être des propriétés système de ce fichier (nom, taille, format) dont l’intérêt ne sera pas ontologique et plutôt commun au système de référence.

Dans le cas d’une valeur primitive ( v: ou v: » … « ), là on interprète la valeur directement. J’en rajoute une couche avec la traduction ? Doit-on traduire la valeur en elle-même ou est-ce que le prédicat sera démultiplier avec un attribut supplémentaire pour contenir cette différenciation, ça demandera pas mal de requêtes, mais ça fonctionnerait.

SujetPrédicatObjetValeur
#entity3Titre#entity4null
#entity4AuditQui#entity1null
#entity4AuditQuandnullv: »2025-02-13 21:45:27 … »
#entity4Visibilité#entity39null
#entity4Ordrenullv: »1″
#entity4Valeurnullv: »Un titre un peu long qi pourrait poser un certain soucis quelque part »
#entity4Langnullv: »fr_be »
#entity3Titre#entity5null
#entity5
#entity5Valeurnullv: »A somewhat long title which could cause some problems somewhere »
#entity5Langnullv: »en_en »

Mais philosophiquement, n’est pas la même entité qui est traduite ? Serait-ce alors plusieurs clefs Valeur avec un code de langue du texte ?

SujetPrédicatObjetValeur
#entity4
#entity4Valeurnullv:fr_fr: »Un titre un peu long qi pourrait poser un certain soucis quelque part »
#entity4Valeurnullv:en_en: »A somewhat long title which could cause some problems somewhere »

Le principe ontologique est respecté, un prédicat et plusieurs valeurs, mais notre colonne valeur demande toute une interprétation. Ou alors, on pousse le bouchon encore plus loin et quelque soit la valeur devient un Sujet.

SujetPrédicatObjetValeur
#entity4
#entity4Valeur#entity7null
#entity7Type#entity46null
#entity7ContenunullUn titre un peu long qi pourrait poser un certain soucis quelque part
#entity7Langnullfr_fr
#entity4Valeur#entity8null
#entity8Type#entity46null
#entity8ContenunullA somewhat long title which could cause some problems somewhere
#entity8Langnullen_en

Et là c’est top côté ontologie, il nous faut :

  • 1 requête pour avoir la référence du Sujet
  • 1 requête pour avoir tous les prédicats du Sujet
  • 1 requête par Prédicat pour déterminer sa nature (peut-être à conditionner selon que la colonne Objet ou Valeur est utilisée)
  • 1 requête par Prédicat ayant une valeur sous forme de Sujet

À savoir un minimum de 6 requêtes, au lieu de 1 en début d’article (1 pour trouver le Sujet puis 1 pour avoir ses Prédicats, ou les 2 d’un coup si on a déjà la première info).

D’où l’importance des choix avant même de penser aux optimisations techniques.

Historisation

Nous ne sommes pas revenu sur l’historisation et là il y a 2 possibilités. Soit on part de la valeur que l’on encapsule virtuellement par répétition du prédicat, il faudra trouver la valeur la plus récente :

SujetPrédicatObjetValeur
#entity3Titre#entity4null
#entity4AuditQuandnull/date1/
#entity3Titre#entity5null
#entity5AuditQuandnull/date2/
Solution de plusieurs valeurs pour l’historisation

Soit par ligne individuelle, ce qui nous demandera de faire un travail différenciel pour recomposer le cheminement de l’information dans le temps, mais au final on en revient à la structure précédent Qui, Quand, Valeur. On pourrait imaginer un chainage, modifier la valeur entraine un nouveau bloc Valeur qui chaine en s’intercalant, comme en C avec les listes chaînée.

SujetPrédicatObjetValeur
#entity3Titre#entity5null
#entity5HistPrécédant#entity4null
#entity4
Chaînage de l’historique

La valeur

Revenons maintenant sur la valeur (Objet, Valeur). Ils sont mutuellement exclusif et comme dit plus tôt, il y a plusieurs solutions.

  • Soit diviser la table ontology en 2-3, ce qui, pour moi, ne correspond pas à l’idée de l’ontologie.
  • Soit fusionner la colonne Objet et Valeur en une seul de type varchar dont le contenu peut être déterminé comme vu précédemment.

Si on considère la seconde option, il faut parler du Prédicat qui nous donne déjà des informations de Type, donc en soit le type de valeur n’est à faire que dans le cadre d’un Prédicat Valeur. J’y vois déjà une arborescence de Types de Valeurs et de Prédicats relatifs. Le but est de toujours savoir comment interpréter la valeur.

Transposer les solutions

Si on essaye de tout regrouper pour voir si ça fonctionne virtuellement, prenons l’exemple de mon surnom.

SujetPrédicatObjet (valeur)Explication
#entity1Surnom#entity8
#entity8Type#entity3Entité de type Valeur
#entity8Qui#entity1
#entity8Quand/date1/Quand est de type date
#entity8Visibilité#entity4
#entity8Ordre1Ordre est de type nombre
#entity8ValeurContenu#entity7Entité de type contenu/valeur
#entity7Type#entity6
#entity7Langfr_be
#entity7ContenuKillan
#entity8HistPrec#entity2
#entity2Type#entity3Ancienne capsule pour la valeur
#entity2Qui#entity1
#entity2Quand/date2/
#entity2Visibilité#entity4
#entity2Ordre1
#entity2ValeurContenu#entity5
#entity5Type#entity6Ancienne valeur
#entity5Langfr_fr
#entity5ContenuKilan

Traduisons tout cela :

  • On a un Sujet me représentant #entity1 avec un Prédicat Surnom qui a une valeur #entity8,
  • #entity8 est pourvu des Prédicats permettant d’auditer la valeur et d’obtenir la valeur #entity7,
  • #entity7 est un Sujet ayant la valeur finale pour une langue initiale,
  • #entity8 à une valeur précédente en #entity2 pour corriger une typo (ça aurait pu être un changement de visibilité ou d’ordre),
  • #entity2 est pourvu des Prédicats permettant d’auditer la valeur et d’obtenir la valeur #entity5 également, et ici elle n’a pas de valeur précédente,

Attention aux noms des Types : Surnom, Valeur, Contenu et ContenuValeur; il va falloir y prêter attention pour ne pas se perdre en chemin. Peut-être est-ce toute une chaîne typée héritée de types respectifs plus macro comme les modèles.

En gros on a fait x10 à notre ligne initiale, et on corrige nos soucis d’historisation, de stockage de valeur et de typage. C’est le programme qui va se perdre en requêtes et recomposition d’un information intelligible pour traitement. Dans un sens comme dans l’autre. :/

2 requêtes pour la valeur d’un Prédicat qui a déjà demandé 1-2 requête(s). Et l’historique, ainsi que l’audit, à la demande dans des requêtes annexes par 2n.

Optimisation

Comme vous l’aurez compris ça va coûter cher en requêtes et temps de processus. On a une table déjà énorme en prévision, si en plus on a l’historisation aussi verticale ça va devenir longuet. Vient une idée (merci Adrien), de sortir la partie historique de la table ontology, de ne garder que l’instant courant dans celle-ci et de sortir le reste en dehors, vu que son taux de consultation sera bien bien moindre. On aurait donc une table historique contenant une structure légère afin de pointer le Sujet adéquat dans l’ontologie, voire même un Prédicat, à voir, et à stocker sa valeur précédente. En gros une capture de l’objet et descendants directement liés et stocker sous format JSON.

Dans l’exemple précédent on aurait alors #entity2 et #entity5 de sorti dans ce format JSON et stocké en historique, pointant #entity1:Surnom. Le reste serait une ordonnancement par date par exemple.

Validation de contenu

De part la volonté communautaire du projet et d’autogestion/autorégulation par la communauté, une valeur doit être approuvée, toute ou partiellement selon le type d’information. Cette nouvelle structure nous permet d’ajouter des Prédicats de contrôles.

SujetPrédicatObjet (valeur)
#entity17Validation#entity1

Le Prédicat pouvant être multiple, on peut avoir plusieurs validateur et la validation peut être à son tour un objet déterminant un commentaire et une valeur d’approbation et date. Cela peut être un refus commenté par exemple. C’est au système ensuite de considérer ou non la valeur. Dans le cas d’un correctif refusé, le système doit aller chercher la valeur initiale si approuvée. Je vous laisse imaginer le bazar futur.

Conclusion

Nous voilà revenu à notre structure la plus pure et théorique :

S, P, O

Dont O est un V encapsulé et dont il faudra définir l’entité avec soin.

subject UUID not null,
predicate UUID not null,
object VARCHAR(X) not null,

Adieu facilité ! Bonjour complexité et flexibilité…

Il ne me reste plus qu’à réécrire la moulinette de conversion, créer les nouvelles structures d’entités et créer les outils nécessaires pour gérer ça plus facilement, et évidemment revoir le lien front OntologyMyAdmin en profondeur du coup.

Rien que ça… ^^

Folk·o·pedia – une histoire de techniques au fil du temps

Si on regarde Folk·o·pedia, même si Comitards date de 2006, on pourrait penser que le sujet ne date que d’il y a 7 ans, quand j’ai parlé de Comitards v3. Et là encore, vous me direz que c’est déjà pas mal. Mais que nenni, il y a une histoire plus ancienne qui prend forme aujourd’hui, dont je ne me suis rendu compte qu’il y a peu. Du coup, c’est parti pour un petit partage.

Folkipedia, juillet 2007 avec le FJDB (Fonds Jean-Denys Boussart), sans suite.
Reprise du projet en personnel, juin 2008.

Nous sommes d’accord que Folk·o·pedia repose sur une ontologie, une base de connaissances, structurée en triplet (Sujet, Prédicat, Objet), ça ok, mais saviez vous que tout début 2000, un de mes sites, MiZaMis, utilisait déjà une base de données structurée différemment ? Suivant le principe de la base de registre de Windows, en un peu plus simple, chaque utilisateur avait sa propre table, organisée dans l’équivalent du prédicat – valeur (objet). Bien sur, à l’époque, cela a valu son lot de railleries, mais ce système a montré ses avantages et inconvénients et a pu durer toute la vie du site.

Structure de la table d’un utilisateur et exemple de contenu.
MiZaMis, division de MesDocuments, successeur de LaToile, ~2005-2008.

Plus tard, suite à mon second graduat en Techniques Infographiques (2008), j’ai proposé un mémoire sur « Recherche, étude et mise en œuvre d’un générateur de formulaire extensible, configurable et exploitable dans divers contextes« , qui, en résumé, avait pour but de générer des formulaires sur base d’une description, avec contrôles et différentes sorties générées possibles (web, PDF, rtf, Word, …), évidemment c’est la version web avec JQuery qui était le plus poussé, même si générer du PDF en code natif était un beau défi en soi.

Mais, n’est-ce pas également le but des modèles ontologique que renferme Folk·o·pedia ? Une description d’entité dont les prédicats sont eux-mêmes décrit et peuvent indiquer des contrôles ? En effet, le but d’un modèle est de pouvoir proposer un formulaire généré via la description de son modèle ! Évidemment on ne vas pas réutiliser la solution du mémoire ^^ elle est légèrement dépassée, mais l’idée, le concept, lui, reste valable.

Dump YML des données générées par l’éditeur.
Résultat généré sur base des données précédente en XSL et d’un transformateur XSLT.
Le même résultat avec une couche de CSS et de JS.

Dans une autre mesure, il y a eu aussi le projet FGPI (Folk Groups and People Involved vocabulary) en 2015, dont le but était de créer un ensemble de règles descriptives afin d’ajouter au site Comitards.eu une couche metadata suivant certains principes, mais, malheureusement, cela s’est avéré infructueux. Cela étant dit, une ontologie, donne exactement une suite à ce projet, décrire des objets et les liens entre eux, des gens et des groupes folkloriques, ce qu’ils ont fait, porté et vécu.

Et enfin, durant mon mémoire de 2022, dont le sujet, en français, était : « Comprendre les exigences d’utilisation des citoyens concernant les fonctionnalités de visualisation de données libres sur les portails du gouvernement« , on traite de la visualisation de données dont on ignore comment les représenter de manière lisible par le premier venu. Folk·o·pedia aura également ce soucis, l’ontologie ne donne pas ces informations en premier plan, il faut prévoir un tel système, le fournir et le traduire, et c’est génial car c’est encore tout un défi ! On peut partir sur un dictionnaire des prédicats par exemple, d’ailleurs celui-ci est déjà nécessaire en l’état du développement courant.

Affichage des données sous format d'un graphique à barres et d'une tarte.

Vous avez là trois histoires qui se passent sur ~24 ans. Trois bout d’un problème qui se déroule en ce moment même. Je ne l’ai même pas vu venir, mais quelle surprise lors du constat ! D’où ce petit partage pour vous en faire part. 😉

Passer dans les backups et archives, pour vous trouver ces contenus, ravivent de beaux souvenirs. C’est toute une aventure, toute une partie de ma vie, de développeur web et de projets perso. Il y a tant à dire et à montrer pour saisir ce que cela peut représenter à mes yeux :).

Et en avant première, une partie des coulisses actuelle, l’état sans censure du développement de Folk·o·pedia, son outil de contrôle : l’explorateur d’entités. À l’image d’un autre outils qui m’a aidé toutes ces années : PHPMyAdmin.

OntologyMyAdmin WIP janvier 2025

Registre Ontologique

Comme annoncé en petit comité tout récemment, j’entreprend de recommencer le projet Folk·o·pedia. Ce sujet n’a de difficulté que de rassembler la connaissance, toute la connaissance folklorique… Et pour ce faire, je souhaite utiliser, me baser sur, ou m’inspirer du principe d’ontologie, de modèle ontologique et de triplet RDF, comme dit en 2019.

Cette année là, un modèle DB avait été conçus, correspondant et faisable, et un système a même été développé, tel un driver pour la suite du développement. Mais malheureusement ça n’aura pas été plus loin. Qu’à cela ne tienne, nous revoilà plus beau, plus malin, plus fort, plus motivé que jamais.

On récapépètte depuis le zébu. Suite à de nouvelles rencontres et d’événements, le projet évolue un chouilla plus avec l’ajout d’objets, même si la notion existait déjà, l’idée est plus aboutie (collection/fonds et métadonnées). De plus on reprend la base même des données, de leur structuration, de leur relation ou fonctionnement. On reprend tout.

Brainstorming SPO, problématiques et performance.

SPO

Tout part de là en fait. Ce fameux triplet, que l’on peut comparer à une base de registre (cf Windows par exemple). Un quelque chose qui a un attribut qui a une valeur (ou plusieurs). Soit le Sujet – Prédicat – Objet. Et il faut savoir que O est soit une valeur finie (un prénom, une date, un nombre, …), soit un lien vers un S.

Donc on peut imaginer qu’une entité me représentant (S) qualifie mon surnom (P) avec la valeur « Killan » (O), mais il y a un second surnom (P) de valeur « Pôlebreak » (O). Donc un ensemble de valeurs pour un même prédicat.

Là où certain se chiffonnent déjà le caberlot, c’est comment savoir quel prédicat est le premier, le principal, etc. Et la réponse est simple : on ne le sait pas.

Historisation

Un autre aspect des données sur un tel site est la vie de la valeur elle-même. Si un document a été révisé il y aura des versions, pourtant il s’agit de la même entité, du même sujet. Si une personne change de prénom, il ne s’agit pas d’ajouter un nouveau prénom mais de remplacer sa valeur avec l’historisation de « l’avant ». Ainsi, si on demande à Folkopedia de nous donner l’état des lieux en 2006, l’on pourra constater que des groupes existent ou ne sont pas encore là, que certains avaient un autre nom, un autre lieu de rassemblement, l’état du comité de gestion (qui et à quel rôle), etc. L’historisation pourra ainsi permettre de voyager dans le temps.

D’un autre côté, l’historisation a également pour but de contrôler les modifications faites par les contributeurs, pour revenir en arrière en cas d’erreur par exemple (principe d’audit et de contrôle par les pairs).

On peut également définir qu’une information n’est pas pertinente à historiser, tel un statut temporaire, la participation à un événement, une demande de contact, …

Visibilité

Un aspect important également avec toutes ces données, est de savoir ce que l’on partage et à qui. Cela peut-être le partage de son email pour être joignable, l’affichage de son deuxième prénom, ou pour un groupe, de donner accès à un document, ce dernier pourrait n’être disponible que pour les membres. Ainsi quelque soit la donnée, celle-ci bénéficie d’une visibilité.

Cette visibilité peut être parmi une liste proposée (publique, privée, membre inscrit, mes amis, les amis de mes amis, …) ou définie manuellement : visible par tel ou tel personne, tel ou tel groupe.

Métadonnées

Suite aux points mentionnés ci avant, de comment gérer les valeurs multiples pour un même prédicat, ou d’historisation, et en ajoutant la notion d’audit et de visibilité, la question du comment est bien légitime.

Une première possibilité en suivant le modèle SPO, est que la valeur (O) est elle-même un (S) contenant la valeur elle-même, ses métadonnées, c’est-à-dire la visibilité et son audit (qui a modifié et quand). Le soucis sera au niveau de la performance, pour chaque valeur (O) il faudra chercher son S correspondant et les prédicats résultants.

Si nous avons 6176 inscrits en ce moment, que l’on imagine une centaine de paramètres à renseigner et que chacun ai été révisé 3 fois, en tenant compte du modèle de métadonnées, nous sommes à 7.4M de lignes encodées (max.), sans ajouter quoique ce soit de spécial. Soit, si je souhaite consulter le profile d’une personne j’aurais un maximum de 100*4 requêtes individuelles (pour chaque O cherche le S et ses valeurs de prédicat). Et ce à chaque consultation. Actuellement, sur Comitards, il s’agit d’un lot de 5-10 requêtes.

Une seconde possibilité est de détourner le modèle S¨PO pour aplatir le besoin fixe (audit, visibilité), et tant qu’on y est, vu qu’on parle de table de données, O est un soucis, il ne peut en une même colonne correspondre à la fois à une valeur et à une clé étrangère vers S, il nous faut donc 2 O nullable. Nous aurions donc quelque chose du style :

S, P, O1, O2/valeur, qui, quand, visibilité

O1 est un lien vers un S potentiel, O2 est la valeur (string) et le reste tel qu’attendu. Donc nous réduisons la complexité de 7.4M à 1.8M en simplifiant le principe d’historisation.

Reste que nous n’avons pas résolu la question de quel surnom est avant l’autre. Métadonnée en plus ? Une colonne ordre dans la solution #2 ?

S, P, SL?, valeur, qui, quand, visibilité, ordre

O1 renommé SL (S link, l’enfant de ce prédicat, ou laisser O, à voir) en optionnel (?).

Multiple façons d’historiser

Si on prend une valeur finie tel qu’un prénom, la solution précédente fonctionne très bien.

S1, surnom, lien:h1, pseudoActuel, S1, 2008-01-01, publique, 1
h1, valeur, null, pseudoAvant, S1, 2006-03-04, privé, 1

S1, surnom, null, secondPseudo, S1, 2010-08-12, publique, 2

On peut également imaginer que la partie historisée se trouve dans une autre table pour les besoins de performances.

Mais quand est-il si on parle d’un document, ayant différents auteurs dans le temps, signataires, contributeurs, versions, etc. ? Imaginez un exemple avec un groupe folkorique quelconque (GFQ) ayant un document :

GFQ, document, GFQ-doc#1, null, GFQ, 2020-11-01, restreint, 1

GFQ-doc#1, url, null, "url du document sur serveur", GFQ, 2020-11-01, hérité, null
GFQ-doc#1, titre, null, "titre du document", GFQ, 2020-11-01, hérité, null
GFQ-doc#1, signataire, S1, null, GFQ, 2020-11-01, hérité, 1
GFQ-doc#1, signataire, S2, null, GFQ, 2020-11-01, hérité, 2
GFQ-doc#1, signataire, S3, null, GFQ, 2020-11-01, hérité, 3
GFQ-doc#1, auteur, S1, null, GFQ, 2020-11-01, hérité, 1
GFQ-doc#1, datePublication, null, "2020-10-05", GFQ, 2020-11-01, hérité, 1

Notez que « restreint » devrait à son tour être une clef vers un S (type connu ou personnalisé). L’url du document à un ordre à null car non pertinent et une visibilité héritée (à voir, ça reste éditable). La date de publication est un string qui sera interprété par la connaissance du modèle, donc permet la flexibilité.

Maintenant que se passe-t-il si le document change ? On ne peut pas tout simplement changer chaque valeur individuellement, la notion de groupe est importante. Bien que s’il s’agit d’une typo dans le titre cela fonctionne. Faut-il encore qu’il y ai un besoin d’historiser le changement.

Ce qu’il va nous falloir, c’est que pour un même S:GFQ-doc#1, il nous faudra une version de plus. On pourrait encapsuler GFQ-doc#1 dans une coquille permettant de regrouper les versions du même document. Ou établir un lien entre les versions comme une chaine entre document, mais là encore côté performance ce n’est pas idéal.

Une autre solution encore est de considérer l’ajout d’un document en le qualifiant spécifiquement de version antérieur d’un autre. Il faudra faire le tri ceci dit.

Évidemment tout ceci se discute, c’est un point de vue et les contributeurs devront se poser la question et le système les aidera dans leur démarche. Il faudra bien sur confronter d’avantages de cas de figure et amender le principe. Et nous n’avons pas parlé des informations additionnelles que chaque élément peut porter (prédicats).

Pour ma part je pense que l’encapsulation est une idée intéressante au seul moment ou une nouvelle version arrive. Ainsi, de manière polymorphique, un document est son information directe ou indirecte via l’encapsulation. Ce sera au système de gérer.

Modèle et extensions

Nous en parlions plus haut, au niveau des types de données sur base d’un stockage en string. Les utilisateurs ne vont pas « coder » leurs données, c’est une interface qui assistera les utilisateurs en fonction de leur choix et les guidera dans le méandres des possibles.

Pour ce faire, NOUS devons avoir une vue claire de ces possibles, ce que chaque entité peut avoir comme prédicat, autoriser l’ajout de prédicat avec validation collégiale, faire vivre le modèle et ses usages.

Prenons un exemple avec un couvre-chef, il aura un nom. Si nous l’étendons et définissons la penne, celle-ci aura d’avantages de paramètres, tel que la longueur de la visière, la couleur du feutre, son origine, sa date. Mais là encore on peut parler d’un comité de baptême décernant la penne et ayant sa version dudit chapeau, avec son liseret, ses pins de base, le folklore attaché à la cérémonie, etc. Enfin, l’utilisateur du même comité de baptême, déclarera posséder un tel couvre-chef et sa penne aura sa propre vie, respectant ou non les valeurs du comité.

Ainsi par cet exemple on observe le principe d’extension du modèle. Le modèle permet de définir un objet, qui, quand il sera instancié, permettra à l’utilisateur d’avoir des informations à remplir. Évidemment il faut aussi imaginer la variance possible dans le temps, avant on faisait ça, mais aujourd’hui… etc. De même que le modèle peut être à respecter obligatoirement (couleur, longueur), alors que certains paramètres peuvent varier, comme une suggestion, on met le pins ici mais tu l’as mis là car ton parrain fait autrement etc. .

On aura donc besoin d’un système permettant la définition des modèles et ainsi définir le type de prédicat, leur pluralité, leur valeur par défaut, le côté obligatoire. tel un patron de vêtement, une recette de cuisine, vous gardez votre liberté.

Dans l’ensemble d’une entité (S), un prédicat pourra alors nous dire de quel modèle l’entité est issu. Ce qui permettra de construire le formulaire de saisie, d’apporter l’assistance à l’utilisateur, de contrôler la mécanique et fonctionnement, voire même de définir la validation par les pairs.

Autogestion

Vous avez donc compris la taille du possible, la quantité d’informations potentielles et nous n’avons parlé ici que de quelques cas. Seul je ne saurais, ni ne veux, gérer ça, c’est à la communauté et aux contributeurs motivés que cela revient. Donc il faut définir, à l’image de StackOverflow, une mécanisme adéquate. C’est à dire, un moyen de mesurer l’implication ET la justesse de participation d’un utilisateur pour lui donner tel ou tel droit.

Évidemment vos données vous regarde, mais vos apports seront confirmés ou infirmés, un jugement par vos pairs pour donner crédit ou informer d’une erreur. L’idée restant que l’information proposée sur le site doit être de la meilleure qualité qu’il soit (comme Wikipedia).

De plus, vos titres de guindaille seront utilisés pour savoir à qui demander une validation d’accès à un groupe, ainsi il faudra que X personnes valide et ainsi le titre est validé et la personne à alors accès aux données du groupe et peut s’enorgueillir d’en faire partie. Comme pour les tags sur les contributions, certains seront officiels mais les nouvelles propositions feront l’objet de vote.

Ainsi vous pouvez imaginer, qu’au travers d’une structure SPO, tout ceci, et plus encore, seront encodés. Il va de soit qu’une recherche sur la performance et un détournement de modèle quant à son implémentation seront nécessaire.

Suppression et historique

Un soucis se pose quand on parle de suppression dans un contexte historisé. Doit-on réellement supprimer la donnée ou juste rompre l’accès ? Mais si l’entité est liée et à son tour supprimée, comment gérer l’ensemble fantomatique ? Empêcher une suppression peut être une solution, si après un délai et/ou si lié à d’autre.

S, P, SL?, valeur, qui, quand, visibilité, ordre, supprimé, supprimé par qui

On pourrait ajouter alors cette information pour connaître sa disponibilité, mais le plus évident en terme de performance sera de déplacer le contenu dans une table dédiée. Alors, suite à un acte spécifique une liaisons sera établie pour aller les chercher, par exemple restaurer une donnée.

Mise en cache

De but en blanc, l’idée est simple, au lieu de faire des centaines de requêtes sur un sujet à chaque consultation, on stock le résultat en mémoire pour aller le chercher et rendre l’opération plus légère et rapide. Mais, nous avons une notion de visibilité, ce qui nous oblige à en tenir compte et à ne stocker que la partie publique, et peut-être à mettre en cache des segments par visibilité.

Publier le schéma

À l’image de schema.org, il nous faudra également un aplatissement du modèle, à des fins de documentation, de contrôle et d’échange.

Dans cette optique et en nous basant sur les données existante de Comitards, nous pourrons créer des modèles et une moulinette pour migrer l’information, ensuite confronter les cas d’usages et les extensions.

Conclusion

Comme vous le voyez, il ne s’agit pas d’un « yapluka », mais d’une procédure empirique qui se confronte à chaque étape à des questions de « comment » on va gérer telle ou telle situation. Cependant, la direction semble claire, les impératifs sont définit, la notion de performance en cible également.

La prochaine étape sera de modéliser cette base de données et d’y décrire le principe de modèle, ce qui pourra être testé par des cas d’usages.

En comparaison du modèle de DB de 2019, plus éclaté, il reste valide, et c’est peut-être vers quoi on ira une fois que l’on aura les données plus en mains avec cette recherche de performance. Ou c’est via la notion de cache que le salut se fera.

L’enfer est pavé de bonnes intentions

Notre premier projet étant fait, et le second ayant des besoins commun, j’ai voulu créer ma première librairie et ainsi la partager entre mes projets comme le fait NPM si je publiais. Évidemment je suis sur un git privé, pas sur Github, ni sur NPM, du coup pour le déploiement etc. ben, c’est toute une histoire…

Donc on part d’un code fonctionnel inclus dans un folder de mon app, on crée un nouveau projet de type librairie et on colle ça dedans, on nettoie le premier projet et on fait le lien entre les libs, on compile l’un et l’autre, l’impatience est à son comble ! Et ça ne va pas…

Donc on part d’une lib de référence (Material Design) et on regarde comment ils font, on s’en inspire, on prend les conventions et on observe les liens, on compile etc. Le stress est palpable ! Et ça ne va pas…

Là, le doute s’installe, malgré les lectures officielles, des articles, des mails, un stackoverflow, un appel à l’aide général et de la patience… rien n’y fit. Et la raison reste un mystère.

Que ça soit l’inclusion d’une lib en local, ou via son git perso, que ça soit un module qui ne va pas alors que l’autre oui, la folie s’installe… et surtout le rejet de ce méchant projet, ce qui le met en péril de progression.

Après avoir accepté l’échec de cette noble tentative et un gros FUCK à Angular et leur système « y a qu’les hypsters qui croivent » (comprendre : une élite peu nombreuse et souvent prétentieuse). Ô rage et désespoir… Enfin donc une idée surgit de la bonne vieille école des barbus, à l’ancienne mais restons propre.

Donc on part d’un code fonctionnel inclus dans un folder de mon app et on crée un répertoire vide en dehors et on tape le code dedans sans lui faire de mal, on ajoute un index.ts et on check que tous les liens de notre point d’entrée sont bons et accessibles.

On adapte notre projet avec le tsconfig.json et le package.json. On ajoute un script de post installation et on peut résumer ainsi : je git clone mon projet ou un nouveau et je fais le npm install, il rapatrie les node_modules nécessaires, ça on connait, il lance tout seul comme un grand le script de postinstall et va cloner notre « lib maison » sous forme de répertoire à l’intérieur de notre projet (ajouté au .gitignore), ensuite grâce aux modif du tsconfig.json, il suffit de faire une référence unique vers cette lib « comme » si c’était une lib NPM.

On respecte donc l’énoncée :

  • Chaque projet doit avoir son repo git
  • La « librairie » est facilement maintenable
  • Les projets utilisent la « librairie » facilement
  • L’ensemble ne se gêne pas

Dans le détail, dans package.json :

"postinstall": "git clone ssh://user@server.net/blabla.git src/mon-folder" 

Ajouter votre répertoire au .gitignore de votre projet :

#mon folder
/src/mon-folder

Vient ensuite votre tsconfig.json :

"baseUrl": ".",
...
"paths": {
  "@ma-lib/*": ["src/mon-folder/*"]
}

Enfin, l’usage dans votre app.module.ts :

import { MonModule } from '@ma-lib/index'; 

Un mois… ça aura pris un mois, mais on peut enfin continuer…

Et de un, maintenant à nous deux !

Ça y est, Schema Definer a été fini le week-end passé ! Le temps de redescendre du dernier rush, de valider les points et tâches dans le Redmine (système de tickets). On respire et on prend du recul.

Nous avons désormais le moyen de paramétrer notre schéma, sa définition. Nous allons pouvoir l’utiliser et en faire Folkopedia, ce qui reste le but de tout ceci. Cependant, ce serait vendre une voiture sans avoir assembler les pièces, cela reste décousu, non-défini.

Quand on fabrique un site web manipulant des données, nous utilisons des outils pour regarder à quoi ressemble les données stockées et de les manipuler. Il nous permet de savoir à quel résultat on s’attend à la sortie, et si ce n’est pas le cas d’être aidé à comprendre.

Dans notre cas spécifique ce même outil ne peut nous aider qu’à voir notre schéma et, de manière éparse, nos entités et attributs. Si l’on doit travailler avec il nous faut un outil équivalent capable d’utiliser notre schéma. Ainsi, par équivalence, nous aurons un outils d’inspection et de manipulation des entités créées.

J’ai nommé ce projet Entity Manager car le but à ce niveau est bien de travailler au niveau des entités et de leur attributs. Par extension cela concerne également leurs relations et la visibilité des contenus, ce qui couvre la totalité du schéma dans son état présent.

Voici donc un projet qui se glisse entre deux, mais qui ne ralentira que peu Folkopedia, car ce qui sera développé dans Entity Manager, telle qu’une partie de Schema Definer, pourront être mis en commun et ne pas devoir être réécrit. Cela peut paraître évident, mais pas pour tout le monde, ni rendu simple par les technologies utilisées et dans notre contexte privé.

Je vais donc techniquement partir sur une librairie Angular partagée entre nos projets qui composent Folkopedia. Pour les connaisseurs, il ne s’agit pas ici d’un projet monolithique-monorepository mais bien d’app séparée par projet ayant une lib privée en commun. La documentation étant quasi inexistante, comme par hasard, nous revoilà en quête d’impossible et de challenges d’évidences.

Links to DRY véier

Deux blocs ont été fait entièrement, les deux plus simple pour commencer et concevoir les éléments nécessaires.

Tout d’abord Visibility Level, niveau de visibilité de l’entité associée, qui a permis de mettre en place le CRUD (Create, Read, Update, Delete) requis. Ainsi sur les 5 endpoints prévu, 4 sont utiles actuellement et ont été réalisé : lister l’ensemble, ajouter, éditer et supprimer.

Pour la suppression, comme vous le savez ou l’espérez, cliquer sur le bouton ne doit pas faire l’action sans une confirmation. Généralement on vous affiche une boîte de dialogue pour vous demander de confirmer, d’une manière ou d’une autre cela vous fait regarder ailleurs ou vous masque ce vous faites.

Material Angular n’a pas de mécanique de popover tel que Bootstrap a, il n’a qu’un tooltip discret. Du coup j’en ai conçus un.

Confirmation

Alors évidemment il est personnalisable : nombre de boutons, valeur, couleur, tooltip et texte (label). Il na pas encore la fonctionnalité de position modifiable (haut, gauche, droite), il va juste dessous celui qui en a besoin, ici un bouton de suppression à confirmer.

Il verrouille le bouton cliqué, affiche sa boite élégante et attend votre clique, à côté il ferme la boute et relâche le bouton verrouillé et sur un bouton renvoie la valeur. Simple, efficace et réutilisable ! Car oui j’en ai fait un module et une directive, tout en un tant qu’on y est et qu’en plus on l’a jamais fait 🙂 mouahahaha la blague…

Vous trouverez pas mal de tuto mais aucun avec les 2 ensembles, et évidemment, aucun complet… Pensez à regarder après ‘entryComponents‘ dans votre fichier app.module.ts à l’occasion quand vous ferez ça ;).


Ceci dit, j’ai également fait le bloc Link Types, les types de lien ! 🙂 Il a une petite particularité, une variable en plus, du coup ça permet d’y aller mollo en difficulté.

Par contre les 2 suivants seront d’un coup plus complexe : défis accepté !


Il y a aussi quelques changements côté back : des tests en plus suite à l’oubli du check d’unicité durant les updates, et évidement le code de test ajouté. On a +100 tests avec ~200 comparaisons ^^ et une couverture inchangée actuellement, toujours 95% !

Autre modification back : un complément pour la liste des Link Types qui ne répondait pas parfaitement à la description de l’API. Détails, mais c’est mieux avec.

Sur le Front rouge

Après plus de douze heures à creuser ma tranchée et à établir mon avant-poste, un bivouaque, un feu et ma radio, voici enfin venu le temps de faire le compte-rendu de ce week-end.

Nous y sommes, sur le Front du combat que l’on mène depuis bientôt presque un mois, au devant du combat, dans le feux de l’Action, loin du doux cocon de l’API. Enfin, pour ceux qui ont suivi, c’était loin d’être un simple doux cocon… ah ah !

Comme prévu, à l’image de schema.org, voici l’allure de notre Schema Definer fraîchement créé. Simple et concis, de quoi parcourir les définitions et, à terme, de les éditer.

Pour faire un point technique, et justifier les 12h dites en début d’article, il a fallu commencer par mettre mon poste à jour et comme toujours non sans mal avec @angular/cli; Node ça été tout seul, et une fois le CLI (Command Line Interface) installé à la bonne version c’est Angular qui a suivi. Nous voilà donc avec un environnement propre Angular 8 et Material Design pour le fun et l’inconnue.

Pour ceux que ça intéresse, faites gaffe avec Material Design, cela ne remplace pas Bootstrap (grille, typo, style etc), mais ne concerne que les composants (bouton, tableau, formulaire, …). En bref, faites attention.

S’en est suivis une mise en place du projet et de l’authentification avec service et gardien (guard). Je vous passe les emmerdes et vous redirige vers quelques articles qui m’ont bien aidé/inspiré.

La liste n’est pas exhaustive. C’est surtout le premier qui m’a bien aidé à comprendre la mise en place de la mécanique du gardien en Angular, ainsi que les interceptor et quelques opérateurs rxjs.

Vu la progression du week-end, voici un extrait représentatif du travail accompli depuis le début du mois (le détail serait beaucoup trop long), et qui, ce week-end, a fait un beau bon en avant.

Maintenant que la machine est en place, le reste devrait suivre sans nouvelle mauvaise surprise. Enfin, j’espère…

PatAPI et patapon

J’ai tout donné aujourd’hui et l’API du Schema Definer est finie ! enfin pour la version prévue sur le plan, on est d’accord. Du coup le titre…

J’ai ajouté le Code Coverage (couverture du code par les tests, ça montre ce qu’on a « testé » ou non) et on a +91% !

Les lignes rouges c’est où il n’y a rien, donc le pourcentage n’est pas exact, mais c’est pas grave, on est bon et ça fait plaisir, il n’a pas été aisé d’en arriver là (voir les 3 dernière lignes, c’est là que ça se passe en fait).

90 tests, sans compter les 10 de la base de données, comptant 187 comparaisons (assertions). Il y a de la redondance et des comparaisons de trop, c’est certains, mais quand on sait que rien que pour tester les Attributs et ses valeurs listées j’ai ~500 lignes de code de test…

J’ai aussi passé pas mal de temps sur la définition de l’API avec son fichier RAML dont je parlais dans un article précédent. Du coup je pense que la documentation est à jour et valable. Pour ceux que ça intéresse, ça prend pas mal temps, on négligera mais n’y penser même pas, ça vaut le coup. Pensez également à votre PHPDoc.

Le temps d’écrire cet article, de faire une pause et chipoter, on est passé à 96 tests et 193 comparaisons avec un résultat de ~94% de couverture ! Des commentaires en plus, du refactoring pour réaménager des méthodes qui pouvaient être mieux écrites, cibler les interdépendances des tests (@depends), etc.

Ça y est, notre API est prête, l’instant de vérité approche. Nous allons pouvoir commencer le « front » (site visible/pour les humains autorisés) et mettre à l’épreuve les appels, dans le sens « est-ce qu’on a pensé à tout ? », « est-ce suffisant ? » et/ou « est-ce utile/nécessaire ? ».

Et ainsi tu typeras

Une semaine plus tard voici un état stable comprenant l’authentification ET les types d’entité !

C’est avec un soulagement plus qu’une fierté que je vous annonce que les types d’entité, côté API du Schema Definer, sont réalisés ! Ye_ah (mou).

On a donc le set qui sera le même que pour les autres fonctionnalités prévues, soit : obtenir la liste de tous, un seul avec détails, suppression, édition et création.

On a un joli total de tests avec 29 tests et 63 validations. Épuisant mais enrichissant, tant pour la qualité du projet que pour les connaissances approfondies du système que cela donne.

En avant pour les 4 blocs restant, toujours côté API. Viendra ensuite le front-end, la partie visible sous forme de site.

Oh API BDay

Après un premier résultat et son échec, nous revoilà dans la course ! Au menu nous avons une authentification, un contrôle middleware de token (~session), un logout, et un logout de toutes les connexions. Le tout testé et non sans mal !

On respire, on se détend et on regarde le point suivant, toujours dans un contexte Schema Definer : la création de type d’entité. C’est parti !