tag:linuxfr.org,2005:/tags/base_de_donn%C3%A9es/publicLinuxFr.org : les contenus étiquetés avec « base_de_données »2024-03-11T10:53:13+01:00/favicon.pngtag:linuxfr.org,2005:News/419072024-03-06T10:58:04+01:002024-03-06T10:58:04+01:00École Inclusive: une application libre pour la prise en charge des élèves en situation de handicapLicence CC By‑SA http://creativecommons.org/licenses/by-sa/4.0/deed.fr<div><p>Directeur adjoint d’un collège en Occitanie, chargé de la SEGPA et de l’accueil des élèves en situation de handicap, je me suis retrouvé dans une situation où le suivi des élèves et de leurs accompagnants devenait difficile, notamment par manque d’outils adaptés.</p>
<p>Loin de me décourager, j’ai créé ma propre application de suivi, <em>École Inclusive</em>, en utilisant le cadriciel libre <a href="https://sql.ophir.dev">SQLPage</a> et la publie aujourd’hui sous licence GPLv3. Ce projet a été possible grâce au support proposé par la documentation en ligne et à de fréquents échanges avec Ophir Lojkine, créateur de SQLPage.</p>
<p>Sans aucune connaissance préalable en programmation, j’ai réalisé toute cette application en <em>SQL</em>. Cela permet un large panel de fonctionnalités pour <em>École Inclusive</em>, qui gère tout le suivi horaire des élèves, des classes et des accompagnants, les emplois du temps, les statistiques, les notifications, l’identification des utilisateurs avec plusieurs niveaux de permission. </p>
<p><img src="//img.linuxfr.org/img/68747470733a2f2f636f6c6c6567652d626f757272696c6c6f6e2e66722f73716c706167652f6c6f676f2e706e67/logo.png" alt="Logo" title="Source : https://college-bourrillon.fr/sqlpage/logo.png"></p>
</div><ul><li>lien nᵒ 1 : <a title="https://github.com/DSMejantel/Ecole_inclusive" hreflang="fr" href="https://linuxfr.org/redirect/113490">Le code source d'École Inclusive sur Github (licence GPLv3)</a></li><li>lien nᵒ 2 : <a title="https://sql.ophir.dev/" hreflang="en" href="https://linuxfr.org/redirect/113491">SQLPage, le framework pour construire des applications en SQL sur lequel s'appuie École Inclusive</a></li></ul><div><h2 class="sommaire">Sommaire</h2>
<ul class="toc">
<li>
<a href="#toc-lapplication-%C3%89cole-inclusive">L’application « École inclusive »</a><ul>
<li><a href="#toc-une-application-pour-am%C3%A9liorer-la-prise-en-charge-p%C3%A9dagogique-des-%C3%A9l%C3%A8ves-en-situation-de-handicap">Une application pour améliorer la prise en charge pédagogique des élèves en situation de handicap</a></li>
<li>
<a href="#toc-de-professeur-dhistoire-g%C3%A9o-%C3%A0-cr%C3%A9ateur-dapplications">De professeur d’histoire-géo à créateur d’applications</a><ul>
<li><a href="#toc-un-d%C3%A9but-de-carri%C3%A8re-d%C3%A9j%C3%A0-marqu%C3%A9-par-les-logiciels-libres">Un début de carrière déjà marqué par les logiciels libres</a></li>
<li><a href="#toc-changement-de-cap">Changement de cap</a></li>
<li>
<a href="#toc-rentrer-dans-une-nouvelle-logique">Rentrer dans une nouvelle logique</a><ul>
<li><a href="#toc-quelles-devront-%C3%AAtre-les-grandes-fonctions-de-la-nouvelle-application">Quelles devront être les grandes fonctions de la nouvelle application ?</a></li>
<li><a href="#toc-qui-sy-connectera">Qui s’y connectera ?</a></li>
<li><a href="#toc-comment-%C3%A7a-marche">Comment ça marche ?</a></li>
</ul>
</li>
</ul>
</li>
<li>
<a href="#toc-un-besoin-au-tout-d%C3%A9but--le-suivi-de-laide-humaine-dans-le-cadre-des-pial">Un besoin au tout début : le suivi de l’aide humaine dans le cadre des PIAL</a><ul>
<li>
<a href="#toc-le-suivi">Le suivi</a><ul>
<li><a href="#toc-quelles-donn%C3%A9es-est-il-utile-de-rassembler">Quelles données est-il utile de rassembler ?</a></li>
<li><a href="#toc-pourquoi-enregistrer-ces-donn%C3%A9es">Pourquoi enregistrer ces données ?</a></li>
</ul>
</li>
<li>
<a href="#toc-un-mod%C3%A8le-de-gestion-%C3%A0-perfectionner">Un modèle de gestion à perfectionner…</a><ul>
<li><a href="#toc-comment-se-faisait-le-suivi-avant-la-cr%C3%A9ation-du-nouvel-outil">Comment se faisait le suivi avant la création du nouvel outil ?</a></li>
<li><a href="#toc-quelles-%C3%A9taient-les-limitations">Quelles étaient les limitations ?</a></li>
<li><a href="#toc-pourquoi-cela-nexiste-t-il-pas">Pourquoi cela n’existe-t-il pas ?</a></li>
</ul>
</li>
<li><a href="#toc-sqlpage--cr%C3%A9er-une-application-web-rapidement-sans-exp%C3%A9rience-de-d%C3%A9veloppeur-web">SQLPage : créer une application web rapidement sans expérience de développeur web</a></li>
<li>
<a href="#toc-principes-g%C3%A9n%C3%A9raux-de-sqlpage">Principes généraux de SQLPage</a><ul>
<li><a href="#toc-un-outil-pertinent-pour-cr%C3%A9er-%C3%A9cole-inclusive">Un outil pertinent pour créer « école inclusive »</a></li>
<li><a href="#toc-un-sqlpager-averti-en-vaut-deux">Un SQLpager averti en vaut deux</a></li>
</ul>
</li>
</ul>
</li>
<li>
<a href="#toc-cr%C3%A9ation-de-lapplication">Création de l’application</a><ul>
<li>
<a href="#toc-les-grandes-%C3%A9tapes-du-d%C3%A9veloppement">Les grandes étapes du développement</a><ul>
<li><a href="#toc-principales-fonctionnalit%C3%A9s-et-rythme-de-d%C3%A9veloppement">Principales fonctionnalités et rythme de développement</a></li>
<li><a href="#toc-comment-est-structur%C3%A9e-lapplication">Comment est structurée l’application ?</a></li>
</ul>
</li>
</ul>
</li>
<li>
<a href="#toc-une-interface-utilisateur-simplifi%C3%A9e">Une interface utilisateur simplifiée</a><ul>
<li><a href="#toc-un-code-puissant-et-dynamique">Un code puissant et dynamique</a></li>
<li>
<a href="#toc-les-points-techniques-int%C3%A9ressants">Les points techniques intéressants</a><ul>
<li><a href="#toc-les-fonctionnalit%C3%A9s-de-sqlpage-utilis%C3%A9es">Les fonctionnalités de sqlpage utilisées</a></li>
<li><a href="#toc-publication-en-open-source">Publication en open-source</a></li>
</ul>
</li>
</ul>
</li>
<li>
<a href="#toc-r%C3%A9ception-de-lapplication">Réception de l’application</a><ul>
<li><a href="#toc-par-les-services-de-l%C3%89ducation-nationale">par les services de l’Éducation Nationale</a></li>
<li><a href="#toc-par-les-coll%C3%A8gues">par les collègues</a></li>
</ul>
</li>
<li>
<a href="#toc-le-futur-de-lapplication-%C3%89cole-inclusive">Le futur de l’application École Inclusive</a><ul>
<li><a href="#toc-%C3%A9volutions-techniques-envisag%C3%A9es">évolutions techniques envisagées</a></li>
<li><a href="#toc-vers-un-d%C3%A9ploiement-de-lapplication-dans-un-cadre-l%C3%A9gal">Vers un déploiement de l’application dans un cadre légal…</a></li>
<li><a href="#toc--et-dans-le-respect-des-donn%C3%A9es-priv%C3%A9es">… et dans le respect des données privées</a></li>
<li><a href="#toc-vers-un-%C3%A9largissement-de-lutilisation-%C3%A0-dautres-%C3%A9tablissements">Vers un élargissement de l’utilisation à d’autres établissements ?</a></li>
<li><a href="#toc-exemple-de-code-affichage-du-profil-dun-%C3%A9l%C3%A8ve-dans-lespace-aesh">Exemple de code: affichage du profil d’un élève dans l’espace AESH</a></li>
</ul>
</li>
</ul>
</li>
</ul>
<h2 id="toc-lapplication-École-inclusive">L’application « École inclusive »</h2>
<h3 id="toc-une-application-pour-améliorer-la-prise-en-charge-pédagogique-des-élèves-en-situation-de-handicap">Une application pour améliorer la prise en charge pédagogique des élèves en situation de handicap</h3>
<p>Tout a commencé pendant un match de la dernière coupe du monde de rugby, un dimanche soir à une heure déjà tardive.</p>
<p>Énième appel à l’arbitre vidéo dans cette compétition. Les données et leur gestion, leur analyse, leur partage, l’aide à la décision. Cette problématique m’a rappelé que, dans le cadre d’une de mes missions, j’étais moi aussi confronté à une vague d’informations pas toujours très claires et toujours plus nombreuses ne rendant pas mes arbitrages très faciles dans la prise en charge des élèves que je suivais.</p>
<p>Profitant de cet arrêt de jeu, je tapotais sur le site de Framasoft à la recherche d’une Webapp libre pour la gestion de mes données. C’est ainsi que je laissais les Springboks s’envoler au score et que je fis la connaissance de SQLpage.</p>
<h3 id="toc-de-professeur-dhistoire-géo-à-créateur-dapplications">De professeur d’histoire-géo à créateur d’applications</h3>
<h4 id="toc-un-début-de-carrière-déjà-marqué-par-les-logiciels-libres">Un début de carrière déjà marqué par les logiciels libres</h4>
<p>Le problème à résoudre avant tout, c’est que je ne suis pas un programmeur et que je n’ai suivi aucune formation dans ce domaine. Formé dans les dernières années du XXᵉ siècle au métier de professeur d’histoire-géographie, j’avais intégré l’usage d’outils numériques à mes pratiques dès le départ. Dans le cadre de mes missions de référent numérique dans mon collège, j’avais déjà mis la main à la pâte pour monter un logiciel de traitement de texte collaboratif (<a href="https://framapad.org/abc/fr/">Framapad</a>) sur un petit serveur privé, installer des logiciels en ligne libres comme <a href="https://moodle.com/fr/">Moodle</a>, <a href="https://www.joomla.fr/">Joomla</a> ou <a href="https://wordpress.com/fr/">Wordpress</a>, adapter de-ci de-là quelques lignes de PHP ou de CSS. À titre personnel, fervent adepte du libre, je ne travaille plus que sur des versions d’Ubuntu depuis 2005 et il m’arrive d’utiliser régulièrement la ligne de commande.</p>
<h4 id="toc-changement-de-cap">Changement de cap</h4>
<p>Retour au collège. La crise sanitaire est passée par là et les restrictions ne me permettent plus de travailler de manière collaborative entre élèves de niveaux ou de profils différents. C’est notamment le cas pour les travaux par groupes de compétences que j’organisais avec le logiciel libre <a href="https://sacoche.sesamath.net/">Sacoche</a>, projet très actif sur l’évaluation par compétences et l’analyse des résultats des élèves. </p>
<p>Mes missions vont alors se diversifier encore et je n’enseigne plus directement depuis l’automne 2020. Toujours dans le même établissement, en raison de la vacance de ces postes, je vais remplir les fonctions de principal-adjoint ou de directeur de SEGPA (Section d’Enseignement Général et Professionnel Adapté pour des élèves ayant des difficultés d’apprentissages importantes). Parmi les dossiers suivis dans ces postes figure la gestion des « pôles inclusifs d’accompagnement localisé » (PIAL) et de leur AESH, les Accompagnants d’Élèves en Situation de Handicap. Le sujet de l’École Inclusive pour tous les élèves ayant besoin d’aménagements est ainsi devenu un axe majeur dans mon implication sur notre collège. Avec des élèves toujours plus nombreux… </p>
<h4 id="toc-rentrer-dans-une-nouvelle-logique">Rentrer dans une nouvelle logique</h4>
<p>Et cette année, j’ai rajouté une corde à mon arc en m’essayant à la programmation d’applications. Pendant les quelques minutes où les directeurs de jeu visionnaient l’action au ralenti, j’ai concentré ma réflexion sur trois points importants, sur trois arbitrages plus personnels : </p>
<h5 id="toc-quelles-devront-être-les-grandes-fonctions-de-la-nouvelle-application">Quelles devront être les grandes fonctions de la nouvelle application ?</h5>
<p>Je cherchais avant tout un outil qui fournisse des informations claires et précises en m’échappant des documents de tableur reçus tous les mois avec des actualisations ou des suppressions, des erreurs possibles, de transcription de nom, des propositions de responsables impossibles à respecter et surtout des données non croisées avec la réalité du terrain. Des collègues avaient tenté de revenir au classeur papier avec un côté élève et un autre adulte. Mais tout changement d’un côté demandait la même charge de travail de l’autre.</p>
<h5 id="toc-qui-sy-connectera">Qui s’y connectera ?</h5>
<p>Si à l’origine j’envisageai un usage mono-utilisateur avec un outil hors-ligne, la possibilité d’avoir un outil collaboratif avait son charme et une utilité justifiée pour suivre les différents besoins de nos élèves. Je n’oublie pas non plus le sentiment de frustration en tant que professeur quand – au début des inclusions dans les années 2000 – j’accueillais des élèves en situation de handicap sans avoir suffisamment de précisions ou de solutions d’aides à ma disposition. Élargir la communauté d’utilisateur n’est pas un sujet à exclure.</p>
<h5 id="toc-comment-ça-marche">Comment ça marche ?</h5>
<p>Je devais pouvoir croiser plusieurs données qui ne se recouvraient qu’en partie : celles reçues via les parents d’élèves notifiés par la MDA-MDPH (Maison De l’Autonomie – Maison des Personnes Handicapées), celles transmises par les services de la DSDEN (Direction des Services De l’Éducation Nationale), avec nos informations ou décisions internes provenant de plusieurs coordonnateurs. Au vu de l’augmentation incessante des demandes et des aménagements attribués, un outil puissant et numérique ne pouvait être que la solution pour éviter des erreurs et rationaliser les suivis. En quelques clics Framasoft me suggérait SQLpage.</p>
<h3 id="toc-un-besoin-au-tout-début--le-suivi-de-laide-humaine-dans-le-cadre-des-pial">Un besoin au tout début : le suivi de l’aide humaine dans le cadre des PIAL</h3>
<ul>
<li>
<strong>Qu’est-ce que le PIAL ?</strong> Cela correspond pour nous à une zone géographique avec des écoles primaires urbaines et rurales, un collège et deux lycées. À la dernière rentrée de septembre 2023 cela représente 85 élèves et 42 accompagnants.</li>
<li>
<strong>Les accompagnants</strong>, terme que je préfère à l’acronyme AESH ou AVS, sont devenus des pièces essentielles à la bonne scolarisation d’élèves toujours plus nombreux. Sur notre collège de 700 élèves, 12 accompagnants interviennent. Certains ont une quotité de travail de 35h mais les plus récents n’ont des contrats que de 24h. Deux d’entre eux exercent une partie de leur mission sur un lycée ou sur une école.</li>
<li>
<strong>Leurs missions.</strong> Ils aident les élèves dans leurs apprentissages mais parfois pour des actes de la vie quotidienne ou sociale (repas, toilette, relations sociales…). Ils suivent en général plusieurs élèves, de 2 à 4, individuellement ou de manière mutualisée quand les élèves à besoin ont pu être placés dans la même classe. La plupart des élèves du PIAL font la totalité de leur temps scolaire sur une classe de référence avec un accompagnement qui ne cible que quelques matières. Les AESH positionnés sur les dispositifs ULIS peuvent suivre sur la semaine la totalité des élèves de leur groupe : 10 maximum d’après les textes, de 13 à 15 en réalité. Les élèves de ces dispositifs ont une scolarité partagée entre la classe de référence et l’enseignement d’un coordonnateur de l’ULIS.</li>
<li>
<strong>Comment sont attribuées les aides ?</strong> Toutes les notifications d’aménagements ou accompagnements attribués en compensation d’un handicap sont issues d’un long parcours administratif de plusieurs mois. Ce délai qui part du dépôt de la demande par la famille se termine par une commission qui a pris l’avis de professionnels du monde médico-social et de l’éducation. Dans notre collège 84 élèves ont une notification MDPH, pas nécessairement un accompagnant. Cela représente 1 élève sur 8. Si l’on ajoute les autres dispositifs d’aides attribués en interne ou par la médecine scolaire nous arrivons à 1 élève sur 4 soit 6 élèves par classe en moyenne.</li>
</ul>
<h4 id="toc-le-suivi">Le suivi</h4>
<h5 id="toc-quelles-données-est-il-utile-de-rassembler">Quelles données est-il utile de rassembler ?</h5>
<p>Au-delà des données basiques d’identification des élèves, il est important de noter la nature des aménagements (AESH, ULIS, Ordinateur, etc.), la date de fin d’attribution et le nom de l’enseignant-référent auprès de la MDPH. Ensuite, nous devons relier l’élève à un (ou plusieurs dans quelques cas) accompagnants sur un certain nombre d’heures. De plus, au début de l’année les coordonnateurs des ULIS ou du PIAL donnent des conseils de mise en œuvre des aides et des objectifs progressifs à atteindre. Ces éléments peuvent être réactualisés régulièrement.</p>
<h5 id="toc-pourquoi-enregistrer-ces-données">Pourquoi enregistrer ces données ?</h5>
<p>Les premières données sont indispensables pour programmer des réunions de suivis et de renouvellement dans les temps. Les suivantes ont tout leur intérêt pour donner du sens et du contenu à l’accompagnement. En cas de remplacement ponctuel d’un AESH, on pourrait ainsi facilement lui transmettre les informations essentielles.</p>
<p>Enfin dans un cadre plus administratif, les services de l’Éducation Nationale nous contactent afin de vérifier que les accompagnants sont bien sollicités à la hauteur de leur quotité de travail et pour des statistiques comparant les aides individuelles ou mutualisées. Cela permet aussi de motiver des demandes de recrutement.</p>
<h4 id="toc-un-modèle-de-gestion-à-perfectionner">Un modèle de gestion à perfectionner…</h4>
<h5 id="toc-comment-se-faisait-le-suivi-avant-la-création-du-nouvel-outil">Comment se faisait le suivi avant la création du nouvel outil ?</h5>
<p>Pendant trois ans, les coordonnateurs du PIAL mettaient à jour un lutin<sup id="fnref1"><a href="#fn1">1</a></sup> avec les emplois du temps élèves et ceux des accompagnants. Mais aussi, quand on les recevait, les notifications de la MDPH ; en effet, ce n’est pas automatique voire souvent non autorisé par certains inspecteurs. Ces derniers préférant que l’information soit donnée par les parents, ce qui n’est pas toujours le cas et ce qui ne permet pas d’anticipation des besoins.</p>
<h5 id="toc-quelles-étaient-les-limitations">Quelles étaient les limitations ?</h5>
<p>Si la mise à jour d’emploi du temps peut se faire régulièrement dans le classeur, la diffusion de l’information auprès de l’ensemble des acteurs n’est pas forcément rapide quand il y a plusieurs acteurs pédagogiques dans le suivi. Enfin, si une gestion classique peut suffire sur de tous petits effectifs, elle ne permet pas de vue d’ensemble dès que l’on atteint des effectifs d’élèves et d’AESH importants et elle ne permet pas de rationaliser certaines aides. Transmettre rapidement des informations précises restait un défi dans le cas de remplacements de dernière minute.</p>
<p>Un détail qui a également son importance, la fonction de pilotage et de coordination du PIAL reste une mission qui s’ajoute aux tâches de sa fonction d’origine. Cela est rémunéré à hauteur d’une indemnité correspondant très rarement au temps réellement passé sur cette gestion de plus en plus lourde.</p>
<p>Comme dans l’exemple de notre arbitre, avoir un outil moderne, réactif, croisant les regards ne peut qu’être la solution !</p>
<h5 id="toc-pourquoi-cela-nexiste-t-il-pas">Pourquoi cela n’existe-t-il pas ?</h5>
<p>J’ai débuté comme enseignant l’année où le ministre de l’Éducation Nationale comparait notre institution à un mammouth. Nous avons (souvent) changé de dirigeant mais pas forcément de rythme. Et j’ai parfois l’impression que nous n’avançons pas très vite. J’ai posé la question en 2020 et l’on m’a répondu qu’un logiciel était en préparation pour la gestion des AESH. Depuis, rien. Cela bouge un peu côté suivi des élèves avec le Livret de Parcours Inclusif. J’ai bien vu un menu apparaître dans notre Intranet mais aucune directive ne nous est parvenue pour l’activer. La MDPH devrait pouvoir nous communiquer les notifications via cette interface, en contradiction d’ailleurs avec les recommandations actuelles. Depuis octobre dernier, rien de plus. Cela reste une coquille vide…</p>
<h4 id="toc-sqlpage--créer-une-application-web-rapidement-sans-expérience-de-développeur-web">SQLPage : créer une application web rapidement sans expérience de développeur web</h4>
<h4 id="toc-principes-généraux-de-sqlpage">Principes généraux de SQLPage</h4>
<p>J’ai tout de suite été séduit par l’idée de pouvoir me concentrer sur les données et sur la personnalisation de leur traitement sans avoir à perdre du temps sur de la mise en page. SQLpage fonctionne comme un petit serveur web. Le binaire de l’application pèse un peu moins de 20 Mo. Quant aux fichiers créés, l’ensemble reste vraiment très léger</p>
<h5 id="toc-un-outil-pertinent-pour-créer-école-inclusive">Un outil pertinent pour créer « école inclusive »</h5>
<p>Maitriser ses propres données et avoir le choix dans la mise en relation et l’affichage des informations me semblait primordial. De plus SQLpage apparait être un outil léger dont on peut utiliser plusieurs briques au choix suivant ses besoins. Et, en tant qu’adepte du logiciel libre, le fait de pouvoir utiliser un programme ouvert, avec une communauté naissante et active correspondait bien à ma philosophie. Détail important à mes yeux, pouvoir retrouver ses données en cas de changement de support à l’avenir était plutôt rassurant. En effet les données stockées dans un fichier de base de données peuvent être facilement exportées au format tableur.</p>
<h5 id="toc-un-sqlpager-averti-en-vaut-deux">Un SQLpager averti en vaut deux</h5>
<p>Comme je vais le détailler dans la partie suivante, s’engager sur SQLpage ne s’est pas révélé aussi simple que cela pour quelqu’un qui n’est pas habitué à coder et qui ne maitrise pas le langage SQL. Ceci dit, je ne regrette pas d’avoir franchi le cap et cela m’a permis de me familiariser avec la plateforme github et de faire d’indéniables progrès tant dans le langage SQL, très accessible au demeurant, que dans la langue de Shakespeare. Si on est prêt à perdre un peu de son temps sur la documentation de SQLpage et quelques tutoriels sur le SQL, on gagne en rapidité de codage par la suite…</p>
<h3 id="toc-création-de-lapplication">Création de l’application</h3>
<h4 id="toc-les-grandes-étapes-du-développement">Les grandes étapes du développement</h4>
<h5 id="toc-principales-fonctionnalités-et-rythme-de-développement">Principales fonctionnalités et rythme de développement</h5>
<p>Lorsque j’ai suivi le lien de Framasoft, je m’attendais à trouver un logiciel avec une interface utilisateur qui permette par glisser déposer de construire des formulaires, un peu sur le modèle d’extensions que j’avais parfois utilisées sur Joomla ou Wordpress. Se retrouver devant un dossier avec un fichier nommé index.sql à rédiger soi-même est plutôt déstabilisant quand, comme moi, on ne maitrise pas le langage SQL. J’ai testé pendant deux jours en fonctionnant par copier-coller depuis la documentation ou depuis les exemples mis à disposition sur Github. Mon inexpérience dans le domaine du codage et ma connaissance de l’anglais sommaire dans ce domaine ont failli me pousser à abandonner SQLpage très rapidement. Heureusement, j’ai trouvé ce tutoriel dans la langue de Molière : <a href="//linuxfr.org/news/ecrire-une-appli-web-en-une-journee-avec-sqlpage">Écrire une appli web en une journée avec SQLPage <em>(publié sur linuxfr)</em></a>. Il m’a permis de bien comprendre les rudiments à la fois du langage SQL et du fonctionnement de SQLpage.</p>
<p>Après ces deux jours de tâtonnements, je me suis donné quatre semaines pour parvenir à un logiciel basé autour de trois pages principales en SQL, une pour recenser les élèves, une autre pour leurs accompagnants et une dernière pour mettre en relation les notifications et aménagements accordés. En ne travaillant qu’à temps perdu, c’est-à-dire très tard le soir ou très tôt le matin, j’ai pu parvenir en deux semaines à un premier logiciel, encore imparfait mais répondant à une grande partie du cahier des charges que je m’étais fixé. Pour cela, je me suis appuyé principalement sur des fonctionnalités de base comme les composants <a href="https://sql.ophir.dev/documentation.sql?component=form#component"><code>form</code></a> pour insérer des informations via un formulaire, et <a href="https://sql.ophir.dev/documentation.sql?component=list#component"><code>list</code></a>, <a href="https://sql.ophir.dev/documentation.sql?component=card#component"><code>card</code></a> ou <a href="https://sql.ophir.dev/documentation.sql?component=table#component"><code>table</code></a> pour afficher les données et <a href="https://sql.ophir.dev/documentation.sql?component=csv#component"><code>csv</code></a> pour les exporter. On se prend au jeu et on progresse très vite. Il est possible de voir très rapidement le résultat de ses requêtes et d’affiner les composants à utiliser ainsi que leurs paramétrages. </p>
<p>Pour un débutant, comme pour un programmeur plus chevronné, on apprécie grandement l’interprétation des erreurs de code éventuelles que ce soit dans la syntaxe SQL (Ah, les virgules oubliées par-ci par-là !) ou dans la mauvaise utilisation des composants de SQLpage…</p>
<p>La mise en place de mon projet s’est déroulée en parallèle d’une phase de développement intense de SQLpage avec une version nouvelle par semaine et une documentation enrichie au même rythme. Plusieurs nouvelles fonctionnalités sont ainsi venues enrichir le code d’École Inclusive. Au bout de quatre semaines, je tenais un logiciel fonctionnel, enrichi par des composants mis à jour comme <a href="https://sql.ophir.dev/documentation.sql?component=map#component"><code>map</code></a>, <a href="https://sql.ophir.dev/documentation.sql?component=datagrid#component"><code>datagrid</code></a> ou nouveaux comme <a href="https://sql.ophir.dev/documentation.sql?component=button#component"><code>button</code></a>.</p>
<p>Entre-temps, j’ai opté pour une version en ligne du logiciel et des données. Cela m’a obligé à me pencher sur les composants <a href="https://sql.ophir.dev/documentation.sql?component=authentication#component"><code>authentication</code></a> et <a href="https://sql.ophir.dev/documentation.sql?component=cookie#component"><code>cookie</code></a>.</p>
<p>Huit semaines après ma découverte de SQLpage, je pouvais déployer une version aboutie, collaborative et en ligne via un protocole HTTPS grâce à <a href="https://sql.ophir.dev/blog.sql?post=SQLPage%20v0.17">la version majeure 0.17 de SQLpage</a>.</p>
<h5 id="toc-comment-est-structurée-lapplication">Comment est structurée l’application ?</h5>
<p>La <strong>structure de la base de données</strong>, c’est l’étape la plus importante avant de débuter le codage. Même s’il reste possible de modifier, rajouter des tables ou des champs en cours de projet, établir un schéma clair et détaillé de la structure des données utiles aide à anticiper la construction future du logiciel.</p>
<p>Pour ma part, j’avais besoin de plusieurs tables pour respectivement les élèves, les accompagnants, les enseignants-référents, les établissements scolaires, les notifications, les aménagements et enfin une pour rassembler les suivis. Cela se calquait sur le fonctionnement classique des procédures.</p>
<p>Au fil de l’avancée du projet, j’ai ajouté des tables pour gérer les utilisateurs et leurs sessions. Et afin de faciliter la gestion des notifications ou aménagements, j’ai construit deux tables "many to many" pour enregistrer de manière plus lisible les notifications multiples (par exemples AESH et Matériel pédagogique) ainsi que les pluri-dispositifs qui peuvent en découler (comme SEGPA et AESH). Cette étape a bénéficié du développement du composant 'form' et de sa fonction multi-select.</p>
<p>Enfin, j’ai créé des tables supplémentaires pour pouvoir utiliser les fonctions 'upload' du composant 'form' et stocker des fichiers images contenant des photos des élèves et les emplois du temps de chaque accompagnant.</p>
<p><img src="//img.linuxfr.org/img/68747470733a2f2f6769746875622e636f6d2f44534d656a616e74656c2f45636f6c655f696e636c75736976652f6173736574732f3535323632392f31613431626435372d663930342d346535652d613066312d376466363438653337623938/1a41bd57-f904-4e5e-a0f1-7df648e37b98" alt="Schéma de la base de données d’École Inclusive" title="Source : https://github.com/DSMejantel/Ecole_inclusive/assets/552629/1a41bd57-f904-4e5e-a0f1-7df648e37b98"></p>
<h3 id="toc-une-interface-utilisateur-simplifiée">Une interface utilisateur simplifiée</h3>
<p>Le principal avantage de SQLpage est de pouvoir se focaliser sur le travail de codage du contenu sans se soucier de la mise en page. Pas de temps perdu sur des fichiers css ou html pour organiser la présentation, ceci est délégué à SQLpage qui propose une mise en forme par défaut pour chaque composant. Ceci est très appréciable et le rendu est sobre et élégant dès le début de la construction du projet. </p>
<p>Au niveau de la charte graphique d’<em>École Inclusive</em>, j’ai choisi d’avoir un menu horizontal en haut de page pour accéder aux pages consacrées à chaque catégorie d’acteurs. L’autre choix a été sur le code couleur où j’ai opté pour des tonalités vertes et orange.</p>
<p><img src="//img.linuxfr.org/img/68747470733a2f2f6769746875622e636f6d2f6c6f7661736f612f53514c706167652f6173736574732f3535323632392f63303261303437652d356563342d343436382d383562612d386333613630646565656630/c02a047e-5ec4-4468-85ba-8c3a60deeef0" alt="page_Classes" title="Source : https://github.com/lovasoa/SQLpage/assets/552629/c02a047e-5ec4-4468-85ba-8c3a60deeef0"></p>
<p>Au fur et à mesure de l’avancement du logiciel et de son enrichissement en fonctionnalités, j’ai prévu d’autres outils de navigation. Si au départ je m’étais focalisé sur des onglets, qui renvoient en réalité sur des pages différentes. Il est possible d’utiliser des variables et de construire un système d’onglet sur un seul et même fichier sql. Le composant <code>button</code> a grandement facilité la tâche. Ceci d’autant plus que l’on peut générer des boutons de façon dynamique. Ainsi, je peux avoir des listes de boutons qui reprennent l’ensemble des classes ou des dispositifs créés pour chaque collège ou lycée du PIAL.</p>
<p>Vers la fin du projet, j’ai mis en place l’appel à un menu stocké dans un fichier json ce qui évite d’avoir à modifier le composant shell sur chacune des pages, ce qui est - pour l’avoir testé à mes dépens - une tâche très fastidieuse.</p>
<h4 id="toc-un-code-puissant-et-dynamique">Un code puissant et dynamique</h4>
<p>La seule limite à l’interface et au codage est celle de notre imagination, en effet SQLpage m’a permis de mettre en œuvre chacune de mes idées à chaque fois que je cherchais à améliorer les fonctionnalités « d’École Inclusive ». Ainsi d’une structure prévue sur 4 fichiers sql je suis passé à une structure de 94 fichiers dans la version actuelle. Maintenant que je maîtrise mieux SQLpage, je pense qu’il serait possible de réduire le nombre de pages, mais, dans ma découverte du code à mes débuts, il était plus facile d’écrire des pages plus courtes.</p>
<p>En s’appuyant sur la documentation en ligne, il est facile d’utiliser les composants de bases pour rentrer les données et les afficher sous forme de listes, de tables (avec fonction de recherche) ou de cartes (avec l’ajout d’images ou de photos) de manière dynamique en étant redirigé vers un contenu spécifique grâce à l’écriture de requêtes sur une variable comme l’id d’un élève ou d'un établissement scolaire ou d’une classe. <br>
<img src="//img.linuxfr.org/img/68747470733a2f2f6769746875622e636f6d2f6c6f7661736f612f53514c706167652f6173736574732f3535323632392f37323661363933662d313433342d343861372d613462312d383563363065666232666532/726a693f-1434-48a7-a4b1-85c60efb2fe2" alt="page_Eleve_ajout" title="Source : https://github.com/lovasoa/SQLpage/assets/552629/726a693f-1434-48a7-a4b1-85c60efb2fe2"><br>
<img src="//img.linuxfr.org/img/68747470733a2f2f6769746875622e636f6d2f6c6f7661736f612f53514c706167652f6173736574732f3535323632392f34653964636334312d656135352d343561382d613364652d616630336666376461356636/4e9dcc41-ea55-45a8-a3de-af03ff7da5f6" alt="page_Eleve" title="Source : https://github.com/lovasoa/SQLpage/assets/552629/4e9dcc41-ea55-45a8-a3de-af03ff7da5f6"></p>
<p>Ce qui m’a demandé davantage de réflexion a été de me lancer dans l’édition et la modification de données existantes. Depuis une icône présente sur une ligne de données d’un tableau, je voulais pouvoir, suivant les cas, éditer ou supprimer une entrée. J’avoue qu’il m’a fallu quelques jours pour arriver à un résultat correct pour dans l’ordre : afficher le formulaire, appeler les données concernées et lancer une mise à jour de la table dans la base. Pour cela j’ai contourné certaines difficultés en faisant appel à des variables afin de stocker certaines données et les réutiliser plusieurs fois sur la page, par exemple pour créer des liens dynamiques.</p>
<p>Dans les tables, j’ai souhaité mettre en évidence des situations demandant une vigilance comme une date d’expiration de notification proche de l’échéance ou l’ayant dépassée ou une fiche incomplète. Il est possible de mettre en place des conditions pour jouer soit sur la couleur d’une ligne soit sur l’affichage d’une icône particulière.</p>
<p>Enfin, la sécurisation du site dans le cadre d’une authentification avec des droits d’accès, des codes d’activation et des mots de passe forts a demandé une réflexion plus poussée et l’aide du concepteur de SQLpage.</p>
<h4 id="toc-les-points-techniques-intéressants">Les points techniques intéressants</h4>
<h5 id="toc-les-fonctionnalités-de-sqlpage-utilisées">Les fonctionnalités de sqlpage utilisées</h5>
<p>Au-delà des fonctionnalités alliant formulaires et données en liste ou en tableau, SQLpage offre des possibilités puissantes à la fois sur le plan fonctionnel et sur le plan esthétique.</p>
<p>Ainsi, il est possible de générer un trombinoscope ou des fiches de synthèse des élèves de chaque AESH. Cela se base sur le composant 'card' qui permet une présentation claire et concise des informations.</p>
<p>Le composant 'map' permet de situer chaque établissement scolaire, de différencier par des icônes les différents types de structures et bien évidemment de créer un lien vers leur page respective.</p>
<p>La visualisation des données sous forme de graphiques avec le composant 'chart' est un des points que je voulais pouvoir afficher pour analyser le temps de suivi de chaque élève et la répartition des missions des AESH.</p>
<p><img src="//img.linuxfr.org/img/68747470733a2f2f6769746875622e636f6d2f6c6f7661736f612f53514c706167652f6173736574732f3535323632392f31643531383164302d643531642d343561342d386231612d613863313362646436393735/1d5181d0-d51d-45a4-8b1a-a8c13bdd6975" alt="page_AESH" title="Source : https://github.com/lovasoa/SQLpage/assets/552629/1d5181d0-d51d-45a4-8b1a-a8c13bdd6975"><br>
<img src="//img.linuxfr.org/img/68747470733a2f2f6769746875622e636f6d2f6c6f7661736f612f53514c706167652f6173736574732f3535323632392f34323662613261362d336461332d346138352d396239312d373866626362313063306264/426ba2a6-3da3-4a85-9b91-78fbcb10c0bd" alt="page_AESH_2" title="Source : https://github.com/lovasoa/SQLpage/assets/552629/426ba2a6-3da3-4a85-9b91-78fbcb10c0bd"></p>
<p>En termes d’import/export, SQLpage permet de récupérer le résultat de requêtes sous forme de fichiers au format csv avec un composant dédié. L’importation à travers le composant 'form' autorise des envois de fichiers uniquement ou des traitements par lots comme dans le cas d’importations d’utilisateurs.</p>
<p>Enfin, les composants 'autenthication' et 'cookie' sont très efficaces pour mettre en place un site sécurisé.</p>
<p>Dernier point fondamental dans le cadre de la sécurisation des données, SQLpage qui reste un mini-serveur web supporte directement le protocole https.</p>
<h5 id="toc-publication-en-open-source">Publication en open-source</h5>
<p>Un simple outil comme SQLpage permet ainsi de développer relativement facilement des applications en open-source qui sont facilement fonctionnelles et attrayantes d’un point de vue graphique. De plus, l’ensemble logiciel, fichiers et base de données reste très léger et l’affichage des pages est instantanée même dans le cas de requêtes complexes.</p>
<h3 id="toc-réception-de-lapplication">Réception de l’application</h3>
<h4 id="toc-par-les-services-de-lÉducation-nationale">par les services de l’Éducation Nationale</h4>
<p>L’École Inclusive me parait naturellement devoir se pencher sur le suivi des élèves. Aussi ai-je fait part rapidement de mon projet aux enseignants-référents qui suivent les dossiers des élèves sur l’ensemble du département. Ils ont été séduits par l’idée car, eux aussi, font face à une échelle encore plus vaste à l’augmentation du nombre d’enfants à besoins particuliers. Intégrer leur rôle dans l’application était une évidence car ce sont eux qui programment et dirigent les réunions de suivis de la scolarité. Chaque début d’année, nous organisons une rencontre pour croiser nos données qui peuvent être parfois divergentes quant à des dates de fin de notification ou des aménagements multiples.</p>
<p>Mais les services de l’École Inclusive revêtent également des aspects administratifs à travers le déploiement et la gestion des accompagnements humains. Je suis rentré en contact avec les services administratifs de la DSDEN de la Lozère. L’accueil du logiciel (encore en version de test) a été bon, notamment sur son volet administratif avec les possibilités de quantifier en heures les accompagnements et la différenciation entre les accompagnements individuels ou mutualisés mais aussi sur son volet de traitement et de croisement des bases de données élèves et AESH.</p>
<h4 id="toc-par-les-collègues">par les collègues</h4>
<p>Dans notre collège, où plusieurs dispositifs coexistent et où un quart des élèves bénéficient d’un aménagement particulier, nous avions l’habitude depuis trois ans de distribuer une fiche A4 par classe avec la liste des élèves et trois colonnes recensant de manière synthétique le constat des difficultés, les aménagements et les objectifs. La mise à jour du tableur était complexe avant la rentrée ou en cours d’année. Certains renseignements sur les suivis manquaient sans parler des oublis ou petites erreurs d’actualisation ou problèmes de mise en page qui pouvaient se glisser dans les listes.</p>
<p>Aussi, proposer à tous les coordonnateurs de dispositifs un outil en ligne, collaboratif, plus complet et toujours à jour les a convaincus immédiatement. Sans tutoriel, ni formation, la prise en main a été très facile du fait de la navigation simplifiée et très intuitive. En moins de trois semaines, l’ensemble des fiches de 184 élèves a été mis à jour.</p>
<p>Cela a permis d’avoir un retour constructif de mes collègues et de recueillir des suggestions pour améliorer le logiciel. L’ajout d’une icône pour ajouter un premier aménagement, masquer des onglets inutiles pour des élèves sans accompagnement, la création d’un champ précisant le rôle de l’accompagnant ou encore l’import des emplois du temps des AESH.</p>
<p>Le logiciel a été testé lors de remplacements d’AESH, dans un premier temps en faisant des captures d’écran des pages des consignes de suivis puis avec un compte actif pour une AESH. Cela s’est révélé très pratique et très facile d’utilisation.</p>
<p>Il reste à franchir le pas de l’ouverture à l’ensemble des équipes pédagogiques et cela sera facilité par les récentes fonctionnalités d’importation permise par SQLPage.</p>
<h3 id="toc-le-futur-de-lapplication-École-inclusive">Le futur de l’application École Inclusive</h3>
<h4 id="toc-évolutions-techniques-envisagées">évolutions techniques envisagées</h4>
<p>Pour répondre à une utilisation pratique pour tous et plus particulièrement pour les enseignants, une sortie au format PDF pour chaque classe permettrait une diffusion claire aux équipes pédagogiques. Une gestion plus fine des droits avec un mode d’édition intermédiaire est à envisager pour que chaque professeur principal puisse intervenir sur les informations des élèves de sa classe.</p>
<h4 id="toc-vers-un-déploiement-de-lapplication-dans-un-cadre-légal">Vers un déploiement de l’application dans un cadre légal…</h4>
<p>La mise en ligne d’École Inclusive sur un serveur reste une démarche relativement simple chez un hébergeur qui offrirait une solution dédiée ou virtualisée. Il est possible de déployer un serveur Linux sur lequel on lance SQLpage comme service. Pour une utilisation sur un seul établissement et par un seul coordonnateur, 'École Inclusive' peut tourner hors-réseau sur différents systèmes d’exploitation. Pour rester dans un strict cadre légal, il faut que le logiciel soit déployé sur une machine ou un hébergement pris en charge par l’Éducation Nationale.</p>
<h4 id="toc--et-dans-le-respect-des-données-privées">… et dans le respect des données privées</h4>
<p>Les services du Rectorat chargés de la Protection des Données nous accompagnent dans cette démarche afin de respecter les préconisations de la CNIL. Le type de données utilisé par le logiciel ne pose pas de problème. Pour les utilisateurs de l’application dont des données personnelles sont conservées dans la base de donnée, il faut prévoir un droit de regard et de rectification conformes au standard du RGPD. Mais, rappelons que cet outil permet déjà une gestion des droits à plusieurs niveaux en tant qu’administrateur, éditeur ou consultant. Dans ce dernier cas, certaines données comme les numéros de téléphone personnels sont masqués.</p>
<h4 id="toc-vers-un-élargissement-de-lutilisation-à-dautres-établissements">Vers un élargissement de l’utilisation à d’autres établissements ?</h4>
<p>Le logiciel 'École Inclusive' va être présenté mi-mars aux chefs d’établissements publics de Lozère (13 collèges et 3 lycées). Cette démarche trouve sa pertinence dans le fait que les PIAL regroupent plusieurs établissements et que les enseignants-référents sont déployés à l’échelle du département. Cependant il sera difficile de mettre en concurrence cette application avec le Livret de Parcours Inclusif quand il sera un jour opérationnel. École Inclusive devra peut-être se recentrer sur la gestion des accompagnants et sur le suivi horaire dans une optique plus administrative que pédagogique. Mais il faut noter une plus grande souplesse et une saisie plus simple et plus directe dans École Inclusive qui ne se limite pas aux situations classiques des PPRE, PAP et Gevasco mais qui peut s’adapter à la physionomie des établissements. Chaque accueil réalisé dans la structure peut être suivi avec par exemple les élèves inclus venant d’établissements médico-sociaux, les élèves allophones, les PAI pour situations médicales…</p>
<p>En conclusion, le plus important n’est pas l’arrivée mais la quête. Celle qui m’a conduit à me poser des questions et à construire École Inclusive dans l’intérêt des élèves à besoins particuliers et des membres des équipes éducatives qui les suivent. Cette application est disponible en open-source sur Github (<a href="https://github.com/DSMejantel/Ecole_inclusive">https://github.com/DSMejantel/Ecole_inclusive</a>). Elle reste encore en évolution et elle se perfectionne au fur et à mesure de l’apparition de nouvelles fonctionnalités de SQLpage. Elle demeure très perfectible : code et interface pourront évoluer en fonction des retours des utilisateurs et de mes progrès en programmation… Pour cela SQLpage reste un allié puissant et très didactique dans les exemples de sa documentation.</p>
<h4 id="toc-exemple-de-code-affichage-du-profil-dun-élève-dans-lespace-aesh">Exemple de code: affichage du profil d’un élève dans l’espace AESH</h4>
<pre><code class="sql"><span class="c1">-- Résumé de suivis des élèves</span>
<span class="k">SELECT</span> <span class="s1">'card'</span> <span class="k">AS</span> <span class="n">component</span><span class="p">,</span> <span class="mi">4</span> <span class="k">AS</span> <span class="n">columns</span> <span class="k">WHERE</span> <span class="err">$</span><span class="n">tab</span> <span class="o">=</span> <span class="s1">'Profils'</span><span class="p">;</span>
<span class="k">SELECT</span> <span class="n">eleve</span><span class="p">.</span><span class="n">nom</span> <span class="o">||</span> <span class="s1">' '</span> <span class="o">||</span> <span class="n">eleve</span><span class="p">.</span><span class="n">prenom</span> <span class="o">||</span> <span class="s1">' ('</span> <span class="o">||</span> <span class="n">eleve</span><span class="p">.</span><span class="n">classe</span> <span class="o">||</span> <span class="s1">') '</span> <span class="k">AS</span> <span class="n">title</span><span class="p">,</span>
<span class="s1">'green'</span> <span class="k">AS</span> <span class="n">color</span><span class="p">,</span>
<span class="k">CASE</span>
<span class="k">WHEN</span> <span class="k">EXISTS</span> <span class="p">(</span><span class="k">SELECT</span> <span class="n">eleve</span><span class="p">.</span><span class="n">id</span> <span class="k">FROM</span> <span class="n">image</span> <span class="k">WHERE</span> <span class="n">eleve</span><span class="p">.</span><span class="n">id</span> <span class="o">=</span> <span class="n">image</span><span class="p">.</span><span class="n">eleve_id</span><span class="p">)</span> <span class="k">THEN</span> <span class="n">image_url</span>
<span class="k">ELSE</span> <span class="s1">'./icons/profil.png'</span>
<span class="k">END</span> <span class="k">AS</span> <span class="n">top_image</span><span class="p">,</span>
<span class="n">COALESCE</span><span class="p">(</span><span class="s1">'Mission de l''AESH : '</span> <span class="o">||</span> <span class="n">suivi</span><span class="p">.</span><span class="n">mission</span><span class="p">,</span>
<span class="s1">'non saisi'</span><span class="p">)</span> <span class="k">AS</span> <span class="n">description</span><span class="p">,</span>
<span class="n">group_concat</span><span class="p">(</span><span class="k">DISTINCT</span> <span class="n">dispositif</span><span class="p">.</span><span class="n">dispo</span><span class="p">)</span> <span class="k">AS</span> <span class="n">footer</span><span class="p">,</span>
<span class="s1">'[</span>
<span class="s1"> ![](./icons/list-check.svg)</span>
<span class="s1"> ](notification.sql?id='</span> <span class="o">||</span> <span class="n">eleve</span><span class="p">.</span><span class="n">id</span> <span class="o">||</span> <span class="s1">'&tab=Profil)</span>
<span class="s1"> [</span>
<span class="s1"> ![](./icons/user-plus.svg)</span>
<span class="s1"> ](notification.sql?id='</span> <span class="o">||</span> <span class="n">eleve</span><span class="p">.</span><span class="n">id</span> <span class="o">||</span> <span class="s1">'&tab=Suivi)'</span> <span class="k">AS</span> <span class="n">footer_md</span><span class="p">,</span>
<span class="s1">'notification.sql?id='</span> <span class="o">||</span> <span class="n">eleve</span><span class="p">.</span><span class="n">id</span> <span class="o">||</span> <span class="s1">'&tab=Profil'</span> <span class="k">AS</span> <span class="n">link</span>
<span class="k">FROM</span> <span class="n">eleve</span>
<span class="k">INNER</span> <span class="k">JOIN</span> <span class="n">affectation</span> <span class="k">ON</span> <span class="n">eleve</span><span class="p">.</span><span class="n">id</span> <span class="o">=</span> <span class="n">affectation</span><span class="p">.</span><span class="n">eleve_id</span>
<span class="k">LEFT</span> <span class="k">JOIN</span> <span class="n">amenag</span> <span class="k">ON</span> <span class="n">amenag</span><span class="p">.</span><span class="n">eleve_id</span> <span class="o">=</span> <span class="n">eleve</span><span class="p">.</span><span class="n">id</span>
<span class="k">JOIN</span> <span class="n">dispositif</span> <span class="k">ON</span> <span class="n">dispositif</span><span class="p">.</span><span class="n">id</span> <span class="o">=</span> <span class="n">affectation</span><span class="p">.</span><span class="n">dispositif_id</span>
<span class="k">JOIN</span> <span class="n">etab</span> <span class="k">ON</span> <span class="n">eleve</span><span class="p">.</span><span class="n">etab_id</span> <span class="o">=</span> <span class="n">etab</span><span class="p">.</span><span class="n">id</span>
<span class="k">JOIN</span> <span class="n">suivi</span> <span class="k">ON</span> <span class="n">suivi</span><span class="p">.</span><span class="n">eleve_id</span> <span class="o">=</span> <span class="n">eleve</span><span class="p">.</span><span class="n">id</span>
<span class="k">LEFT</span> <span class="k">JOIN</span> <span class="n">image</span> <span class="k">ON</span> <span class="n">eleve</span><span class="p">.</span><span class="n">id</span> <span class="o">=</span> <span class="n">image</span><span class="p">.</span><span class="n">eleve_id</span>
<span class="k">JOIN</span> <span class="n">aesh</span> <span class="k">ON</span> <span class="n">suivi</span><span class="p">.</span><span class="n">aesh_id</span> <span class="o">=</span> <span class="n">aesh</span><span class="p">.</span><span class="n">id</span>
<span class="k">WHERE</span> <span class="n">aesh_id</span> <span class="o">=</span> <span class="err">$</span><span class="n">id</span> <span class="k">AND</span> <span class="err">$</span><span class="n">tab</span> <span class="o">=</span> <span class="s1">'Profils'</span>
<span class="k">GROUP</span> <span class="k">BY</span> <span class="n">eleve</span><span class="p">.</span><span class="n">id</span>
<span class="k">ORDER</span> <span class="k">BY</span> <span class="n">eleve</span><span class="p">.</span><span class="n">nom</span> <span class="k">ASC</span><span class="p">;</span></code></pre>
<p><img src="//img.linuxfr.org/img/68747470733a2f2f6769746875622e636f6d2f6c6f7661736f612f53514c706167652f6173736574732f3535323632392f30366134343430662d636632612d346563342d393161652d653736333165623561353532/06a4440f-cf2a-4ec4-91ae-e7631eb5a552" alt="carte_eleve" title="Source : https://github.com/lovasoa/SQLpage/assets/552629/06a4440f-cf2a-4ec4-91ae-e7631eb5a552"></p>
<p>Cet élément fait partie de la page du profil d’un AESH. Dans un premier paragraphe, on appelle le composant 'card' avec ses paramètres. Ici, il y aura quatre cartes affichées par ligne si l’on se trouve sur l’onglet nommé « Profils ».<br>
Ensuite on trouve le contenu de chaque fiche élève. Son identité et sa photo si elle existe dans la base ; cela est déterminé par le <code>CASE WHEN</code>. Dans le cas inverse une image par défaut est affichée.<br>
Si le rôle de l’AESH a été renseigné, il est affiché en dessous. Puis viennent les dispositifs d’aide auxquels l’élève est rattaché.<br>
Enfin, trois icônes renvoient vers différents onglets de la fiche personnelle de l’élève.<br>
Cet affichage est dynamique et s’adapte au profil de chaque AESH comme le définit la condition <code>WHERE aesh_id=$id</code>. Le contenu de la fiche va piocher les différentes informations dans les tables.</p>
<p>Exemple d’alertes et d’informations personnalisées</p>
<pre><code class="sql"><span class="c1">-- Liste des notifications</span>
<span class="k">SELECT</span> <span class="s1">'table'</span> <span class="k">as</span> <span class="n">component</span><span class="p">,</span>
<span class="s1">'actions'</span> <span class="k">as</span> <span class="n">markdown</span><span class="p">,</span>
<span class="mi">1</span> <span class="k">as</span> <span class="n">sort</span><span class="p">,</span>
<span class="mi">1</span> <span class="k">as</span> <span class="k">search</span><span class="p">;</span>
<span class="k">SELECT</span> <span class="n">eleve</span><span class="p">.</span><span class="n">nom</span> <span class="k">as</span> <span class="n">Nom</span><span class="p">,</span>
<span class="n">eleve</span><span class="p">.</span><span class="n">prenom</span> <span class="k">as</span> <span class="n">Pr</span><span class="err">é</span><span class="n">nom</span><span class="p">,</span>
<span class="n">notification</span><span class="p">.</span><span class="n">Departement</span> <span class="k">as</span> <span class="n">Dpt</span><span class="p">,</span>
<span class="n">group_concat</span><span class="p">(</span><span class="k">DISTINCT</span> <span class="n">modalite</span><span class="p">.</span><span class="k">type</span><span class="p">)</span> <span class="k">as</span> <span class="n">Droits</span><span class="p">,</span>
<span class="n">etab</span><span class="p">.</span><span class="n">nom_etab</span> <span class="k">as</span> <span class="err">É</span><span class="n">tablissement</span><span class="p">,</span>
<span class="n">strftime</span><span class="p">(</span><span class="s1">'%d/%m/%Y'</span><span class="p">,</span> <span class="n">datefin</span><span class="p">)</span> <span class="k">AS</span> <span class="n">Fin</span><span class="p">,</span>
<span class="k">CASE</span>
<span class="k">WHEN</span> <span class="n">notification</span><span class="p">.</span><span class="n">datefin</span> <span class="o"><</span> <span class="n">datetime</span><span class="p">(</span><span class="nb">date</span><span class="p">(</span><span class="s1">'now'</span><span class="p">,</span> <span class="s1">'+1 day'</span><span class="p">))</span> <span class="k">THEN</span> <span class="s1">'red'</span>
<span class="k">WHEN</span> <span class="n">notification</span><span class="p">.</span><span class="n">datefin</span> <span class="o"><</span> <span class="n">datetime</span><span class="p">(</span><span class="nb">date</span><span class="p">(</span><span class="s1">'now'</span><span class="p">,</span> <span class="s1">'+350 day'</span><span class="p">))</span> <span class="k">THEN</span> <span class="s1">'orange'</span>
<span class="k">ELSE</span> <span class="s1">'green'</span>
<span class="k">END</span> <span class="k">AS</span> <span class="n">_sqlpage_color</span><span class="p">,</span>
<span class="n">coalesce</span><span class="p">(</span><span class="s1">'[ ![](./icons/user-plus.svg) ](aesh_suivi.sql?id='</span> <span class="o">||</span> <span class="n">suivi</span><span class="p">.</span><span class="n">aesh_id</span> <span class="o">||</span> <span class="s1">'&tab=Profils)'</span><span class="p">,</span> <span class="s1">'[ ![](./icons/user-off.svg) ]()'</span><span class="p">)</span> <span class="k">AS</span> <span class="n">actions</span><span class="p">,</span>
<span class="s1">'[ ![](./icons/briefcase.svg) ](notification.sql?id='</span> <span class="o">||</span> <span class="n">eleve</span><span class="p">.</span><span class="n">id</span> <span class="o">||</span> <span class="s1">'&tab=Profil)'</span> <span class="k">as</span> <span class="n">actions</span>
<span class="k">FROM</span> <span class="n">notification</span>
<span class="k">INNER</span> <span class="k">JOIN</span> <span class="n">eleve</span> <span class="k">on</span> <span class="n">notification</span><span class="p">.</span><span class="n">eleve_id</span> <span class="o">=</span> <span class="n">eleve</span><span class="p">.</span><span class="n">id</span>
<span class="k">LEFT</span> <span class="k">JOIN</span> <span class="n">suivi</span> <span class="k">on</span> <span class="n">eleve</span><span class="p">.</span><span class="n">id</span> <span class="o">=</span> <span class="n">suivi</span><span class="p">.</span><span class="n">eleve_id</span>
<span class="k">LEFT</span> <span class="k">join</span> <span class="n">notif</span> <span class="k">on</span> <span class="n">notif</span><span class="p">.</span><span class="n">notification_id</span> <span class="o">=</span> <span class="n">notification</span><span class="p">.</span><span class="n">id</span>
<span class="k">LEFT</span> <span class="k">join</span> <span class="n">modalite</span> <span class="k">on</span> <span class="n">modalite</span><span class="p">.</span><span class="n">id</span> <span class="o">=</span> <span class="n">notif</span><span class="p">.</span><span class="n">modalite_id</span>
<span class="k">JOIN</span> <span class="n">referent</span> <span class="k">on</span> <span class="n">eleve</span><span class="p">.</span><span class="n">referent_id</span> <span class="o">=</span> <span class="n">referent</span><span class="p">.</span><span class="n">id</span>
<span class="k">JOIN</span> <span class="n">etab</span> <span class="k">on</span> <span class="n">eleve</span><span class="p">.</span><span class="n">etab_id</span> <span class="o">=</span> <span class="n">etab</span><span class="p">.</span><span class="n">id</span>
<span class="k">Where</span> <span class="n">referent</span><span class="p">.</span><span class="n">id</span> <span class="o">=</span> <span class="err">$</span><span class="n">id</span>
<span class="k">GROUP</span> <span class="k">BY</span> <span class="n">notification</span><span class="p">.</span><span class="n">eleve_id</span>
<span class="k">ORDER</span> <span class="k">BY</span> <span class="n">eleve</span><span class="p">.</span><span class="n">nom</span> <span class="k">ASC</span><span class="p">;</span></code></pre>
<p><img src="//img.linuxfr.org/img/68747470733a2f2f636f6c6c6567652d626f757272696c6c6f6e2e66722f73716c706167652f706167655f5265666572656e742e706e67/page_Referent.png" alt="Tableau de suivi par référent" title="Source : https://college-bourrillon.fr/sqlpage/page_Referent.png"></p>
<p>Cet élément fait partie de la page du profil d’un enseignant-référent de la MDA-MDPH. <br>
Dans un premier paragraphe, on appelle le composant <code>table</code> avec ses paramètres. On trouve le formatage d’une colonne en markdown et les options de recherche et de tri qui sont activées.<br>
Ensuite on trouve le contenu de chaque ligne avec l’élève dont le dossier est suivi par ce référent. <br>
Afin de planifier avec lui les priorités pour les dates de réunions de suivi, il est possible d’attribuer une couleur de ligne en fonction de la date de fin de notification avec : <br>
<code>CASE<br>
WHEN notification.datefin < datetime(date('now', '+1 day')) THEN 'red'<br>
WHEN notification.datefin < datetime(date('now', '+350 day')) THEN 'orange'<br>
ELSE 'green'</code><br>
De même les icônes sont personnalisables pour indiquer si l’élève bénéficie d’un AESH ou non. Et bien sûr des liens permettent de passer sur la fiche de l’élève ou de son AESH.</p>
<p>Licence de l’article: <a href="https://creativecommons.org/publicdomain/zero/1.0/">CC0</a></p>
<div class="footnotes">
<hr>
<ol>
<li id="fn1">
<p>[NDM] : lutin est le nom d’une marque de protège-documents à pochettes en plastique, par extension celui des protège-documents similaires d’autres marques. <a href="#fnref1">↩</a></p>
</li>
</ol>
</div>
</div><div><a href="https://linuxfr.org/news/ecole-inclusive-une-application-libre-pour-la-prise-en-charge-des-eleves-en-situation-de-handicap.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/135028/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/news/ecole-inclusive-une-application-libre-pour-la-prise-en-charge-des-eleves-en-situation-de-handicap#comments">ouvrir dans le navigateur</a>
</p>
lovasoaDSMejantelYsabeau 🧶 🧦https://linuxfr.org/nodes/135028/comments.atomtag:linuxfr.org,2005:Bookmark/79752024-02-25T22:31:35+01:002024-02-25T22:31:35+01:00"La meilleure base de données multi-modèles"<a href="https://surrealdb.com/">https://surrealdb.com/</a> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/134979/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/users/pas_pey/liens/la-meilleure-base-de-donnees-multi-modeles#comments">ouvrir dans le navigateur</a>
</p>
pas_peyhttps://linuxfr.org/nodes/134979/comments.atomtag:linuxfr.org,2005:Diary/409982023-12-22T17:57:20+01:002023-12-29T09:49:32+01:00Tour d'horizon de l'état des bases NoSQLLicence CC By‑SA http://creativecommons.org/licenses/by-sa/4.0/deed.fr<h2 class="sommaire">Sommaire</h2>
<ul class="toc">
<li>
<a href="#toc-bases-nosql-disponibles"> Bases NoSQL disponibles</a><ul>
<li><a href="#toc-accumulo"> Accumulo</a></li>
<li><a href="#toc-berkeley-db"> Berkeley DB</a></li>
<li><a href="#toc-bigtable">BigTable</a></li>
<li><a href="#toc-hypertable"> Hypertable</a></li>
<li><a href="#toc-cassandra"> Cassandra</a></li>
<li><a href="#toc-couchdb">CouchDB</a></li>
<li><a href="#toc-dexsparksee"> DEX/Sparksee</a></li>
<li><a href="#toc-documentdb"> DocumentDB</a></li>
<li><a href="#toc-dynamodb"> DynamoDB</a></li>
<li><a href="#toc-elassandra">Elassandra</a></li>
<li><a href="#toc-hbase"> HBase</a></li>
<li><a href="#toc-mongodb"> MongoDB</a></li>
<li><a href="#toc-neo4j"> Neo4j</a></li>
<li><a href="#toc-orientdb"> OrientDB</a></li>
<li><a href="#toc-projet-voldemort"> projet Voldemort</a></li>
<li><a href="#toc-redis"> Redis</a></li>
<li><a href="#toc-riak"> Riak</a></li>
<li><a href="#toc-scylladb"> ScyllaDB</a></li>
<li><a href="#toc-simpledb"> SimpleDB</a></li>
<li><a href="#toc-oracle-nosql"> Oracle NoSQL</a></li>
<li><a href="#toc-mentdb-weak"> MentDB Weak</a></li>
</ul>
</li>
<li><a href="#toc-une-autre-solution-non-nosql">Une autre solution, non NoSQL ?</a></li>
<li><a href="#toc-conclusion">Conclusion</a></li>
</ul>
<p>Les bases NoSQL sont passées de mode depuis longtemps (<a href="//linuxfr.org/news/petit-etat-des-lieux-du-nosql">dernière dépêche en 2012</a>), remplacées par la Blockchain, les NFT, les IA génératives et probablement plein d'autres choses déjà oubliées entre les deux. À l'origine, l'avantage était de pouvoir mieux passer à l'échelle en distribuant les données sur de multiples serveurs. Un autre des avantages avancés (et qui m'intéresse plus) était de simplifier les développements en éliminant la rigidité des bases de données relationnelles, permettant de livrer plus rapidement en s'économisant des contraintes liées aux bases relationnelles.</p>
<p>Historiquement, nous utilisions <a href="https://zodb.org/en/latest/">ZODB/ZEO</a> au bureau qui n'est pas une base NoSQL mais une base objet Python. Étant donné que nous écrivons nos services en Python, cela était adapté à notre besoin. Cependant, suite à des problèmes de performance sur de grosses quantités de données (sans qu'il y ait besoin de les répartir sur plusieurs machines), j'enquête sur d'autres possibilités. Cet article résume les recherche effectuées.</p>
<p>Pour notre besoin, ce serait une base libre, plutôt orientée colonne ou document et idéalement facile à installer.</p>
<h2 id="toc-bases-nosql-disponibles"> Bases NoSQL disponibles</h2>
<p>La liste des bases provient de la page <a href="https://fr.wikipedia.org/wiki/NoSQL">Wikipedia NoSQL</a>. Les informations résumées sur chaque base est issue de sites, présentations et dépôts de sources. Les assertions de performances, de compatibilités ou d'installation n'ont pas été vérifiées.</p>
<h3 id="toc-accumulo"> Accumulo</h3>
<p>Accumulo est sous licence Apache ; la dernière version est sortie en août 2023. Accumulo est basé sur Hadoop avec des fonctions de sécurité augmentées et Zookeeper. La base semble être de type clef/valeur.</p>
<h3 id="toc-berkeley-db"> Berkeley DB</h3>
<p>Aujourd'hui propriété d'Oracle Corporation, deux versions sont disponibles : une licence libre et une propriétaire. La dernière version publiée est la 18.1.40 sortie en 29 mai 2020. C'est une base clef/valeur. La version libre est empaquetée dans de nombreuses distributions. Debian fournit Berkeley DB dans le paquet db5.3.</p>
<h3 id="toc-bigtable">BigTable</h3>
<p>BigTable est produit par Google. Cette base est sous licence propriétaire.</p>
<h3 id="toc-hypertable"> Hypertable</h3>
<p>Hypertable est sous licence GPL 2.0 et supporte Hadoop, GlusterFS ou encore Cloudstore. Le dernier commit visible sur <a href="https://github.com/hypertable/hypertable">github</a> date de 8 ans.<br>
Le logiciel est soit mort, soit devenu propriétaire.</p>
<h3 id="toc-cassandra"> Cassandra</h3>
<p>Cassandra est sous Licence Apache 2.0 et est utilisé (ou a été utilisé) par Twitter et Digg. La dernière version est la 4.1.3 qui date du 24 juillet 2023.</p>
<h3 id="toc-couchdb">CouchDB</h3>
<p>CouchDB est sous licence Apache 2.0. C'est une base orientée document. La dernière version est la 3.3.2 qui date du 25 avril 2023. Les données enregistrées sont une collection de documents JSON. C'est le format reçu lors de requêtes faites à CouchDB. La dépêche de 2012 indique que CouchDB était passé de mode lors de sa publication. Il semble aussi que cet état n'ait pas changé depuis.</p>
<h3 id="toc-dexsparksee"> DEX/Sparksee</h3>
<p>Base orientée graphe dont la dernière version est la 6.0 (2021). Cette base est sous licence propriétaire.</p>
<h3 id="toc-documentdb"> DocumentDB</h3>
<p>DocumentDB est un produit Microsoft. Cette base est sous licence propriétaire.</p>
<h3 id="toc-dynamodb"> DynamoDB</h3>
<p>DynamoDB est un produit Amazon. Cette base est sous licence propriétaire.</p>
<h3 id="toc-elassandra">Elassandra</h3>
<p>Elassandra est une version augmentée de Cassandra intégrant un moteur de recherche Elasticsearch. Elassandra est sous licence Apache mais le projet est mort :</p>
<ul>
<li>le site elassandra.io est un site parking</li>
<li>le dernier commit dans le <a href="https://github.com/strapdata/elassandra">dépôt</a> a 3 ans </li>
<li>la documentation indique la fourniture de paquets .deb et .rpm, mais sur d'anciennes versions de la distribution et le domaine les hébergeant a disparu depuis.</li>
</ul>
<h3 id="toc-hbase"> HBase</h3>
<p>HBase est sous licence Apache 2.0. Cette base est utilisée (ou a été utilisée) par Facebook. C'est la base de données d'Hadoop. La dernière version est la 2.5.6 sorti le 25 octobre 2023. Une version 3 est en préparation .</p>
<h3 id="toc-mongodb"> MongoDB</h3>
<p>MongoDB est historiquement sous licence AGPL mais un changement de licence du serveur a eu lieu en 2018 vers une licence non-libre pour limiter l'exploitation par des fournisseurs SaaS sans en récupérer des subsides. Les outils annexes restent libres. Le site pousse à l'utilisation d'une version cloud (Mongo DB Atlas). Suite au changement de licence, MongoDB a été exclu de certaines distributions linux.<br>
MongoDB Inc. (l'entreprise derrière MonDB) fournit une image sur DockerHub et des paquets pour RedHat, Debian et Ubuntu et Suse (<a href="https://www.mongodb.com/docs/current/administration/install-on-linux/">documentation d'installation</a>).<br>
MongoDB est la base NoSQL la plus populaire selon <a href="https://db-engines.com/en/ranking">db-engines.com</a>.</p>
<h3 id="toc-neo4j"> Neo4j</h3>
<p>Neo4j est une base orientée graphe sous licences GPLv3 et GNU AGPLv3. La base est hébergeable soi-même mais le site pousse à un usage en SaaS (nommé Neo4j AuraDB). Le dépôt <a href="https://github.com/neo4j/neo4j">neo4j</a> est sous licence GPL3 est encore actif, mais calme. Le <a href="https://github.com/neo4j/graphql">dépôt graphQL</a> est sous licence Apache 2 et regroupe neo4j et graphql. Il est plus actif que le précédent.</p>
<h3 id="toc-orientdb"> OrientDB</h3>
<p>OrientDB est sous licence Apache 2.0 et est une base multiparadigme (graphe, documents et clef/valeur).<br>
La dernière version est la 3.0.43, sortie le 09 Août 2022. La publication de nouvelles versions semble ralentie mais le <a href="https://github.com/orientechnologies/orientdb">dépôt reste actif</a>.<br>
La documentation promet une <a href="https://orientdb.org/docs/3.0.x/gettingstarted/Tutorial-Installation.html">installation rapide</a> (docker, ansible et binaire fourni).<br>
Un <a href="https://orientdb.org/docs/3.0.x/pyorient/PyOrient.html">pilote pour Python</a> existe aussi.</p>
<h3 id="toc-projet-voldemort"> projet Voldemort</h3>
<p>Originellement maintenu par LinkedIn et publié sous licence Apache, le projet est officiellement arrêté (cf. le README sur le <a href="https://github.com/voldemort/voldemort/blob/master/README.md">dépôt</a>). Il est suggéré de passer à <a href="https://github.com/linkedin/venice">Venice</a> qui est activement maintenu. La base est basé sur du clef/valeur.</p>
<h3 id="toc-redis"> Redis</h3>
<p>Redis est sous licence BSD. La dernière version est la 7.2.3, sortie le 1er novembre 2023. La base est de type clef/valeur, mais est utilisable en <a href="https://redis.io/docs/get-started/document-database/">mode document</a>.<br>
Redis semble aujourd'hui utilisé surtout pour du cache plutôt que pour du stockage persistant.</p>
<h3 id="toc-riak"> Riak</h3>
<p>Le lien est cassé mais la version descendante semble correspondre à <a href="https://docs.riak.com/riak/kv/latest/index.html">Riak KV</a> aujourd'hui.<br>
Riak est sous licence apache selon les en-têtes de fichier mais il n'y a pas de fichier de licence à la <a href="https://github.com/basho/riak_kv/blob/develop-3.0/README.org">raçine du dépôt</a>.<br>
L'entreprise derrière Riak a fourni des paquets qui ne sont plus à jour. Le service semble plus simple d'installation que des bases de données basées sur Hadoop.<br>
Une seule personne fait partie de l'organisation basho sur github.</p>
<h3 id="toc-scylladb"> ScyllaDB</h3>
<p>ScyllaDB est sous licence AGPL et est utilisé (ou a été utilisé) par Discord20 et Rakuten. La base est en mode colonne et est compatible avec Cassandra, avec plus de performance. La dernière version stable est la 5.2.11, sortie le 28 novembre 2023. Une offre cloud est aussi proposée.<br>
Des images docker sont disponibles. ScyllaDB est écrit en C++ et semble assez simple à compiler si on en croit les commandes listées dans la documentation.</p>
<h3 id="toc-simpledb"> SimpleDB</h3>
<p>SimpleDB est un produit Amazon, mis à disposition via Amazon Web Services.</p>
<h3 id="toc-oracle-nosql"> Oracle NoSQL</h3>
<p>Oracle NoSQL est un produit Oracle Corporation. Cette base est sous licence propriétaire.</p>
<h3 id="toc-mentdb-weak"> MentDB Weak</h3>
<p>Originellement sous licence GPLv3, comme base NoSQL et gestionnaire de processus. Je n'ai pas trouvé de lien vers le code source.</p>
<p><a href="https://www.mentdb.org/index.jsp?app_page=mentdb_weak_server">MentDB WEak</a> est devenu une fusion d'ETL (Extract Transform and Load), ESB (Enterprise Service Bus), SOA (Service Oriented Architecture), WebAPP (Web Application Framework) et AI(Weak) (pour Weak Artificial Intelligence). Le schéma de présentation montre une base SQL comme composant de MentDB.</p>
<h2 id="toc-une-autre-solution-non-nosql">Une autre solution, non NoSQL ?</h2>
<p>Une piste pas encore explorée serait l'usage d'une base temporelle comme <a href="https://kafka.apache.org/">Kafka</a> et la reconstruction des objets en mémoire. Apparemment, <a href="https://www.kai-waehner.de/blog/2020/03/12/can-apache-kafka-replace-database-acid-storage-transactions-sql-nosql-data-lake/">ça se fait</a>. Cela suppose aussi un changement d'architecture vers de l'Event Sourcing, donc probablement plus invasif en terme de changement des services.</p>
<h2 id="toc-conclusion">Conclusion</h2>
<p>Cette recherche a permis un premier tri permettant en particulier d'éliminer les projets abandonnés ou non pertinents (uniquement SaaS, etc.) et de découvrir certaines bases que je ne connaissais pas et qui semblent intéressantes (OrientDB et ScyllaDB par exemple).</p>
<div><a href="https://linuxfr.org/users/srb/journaux/tour-d-horizon-de-l-etat-des-bases-nosql.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/134312/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/users/srb/journaux/tour-d-horizon-de-l-etat-des-bases-nosql#comments">ouvrir dans le navigateur</a>
</p>
srbhttps://linuxfr.org/nodes/134312/comments.atomtag:linuxfr.org,2005:Post/439052023-10-26T13:30:18+02:002023-10-26T13:41:23+02:00Lister les livres/DVD/... présents sur une étagère à partir d'une simple photos<p>Salut,</p>
<p>Quand on commence à avoir beaucoup de livres/BD/DVD/… sur des étagères il devient galère de les ranger et surtout de savoir si on a une œuvre et où elle se trouve.</p>
<p>Pour simplifier et automatiser cette gestion des œuvres physiques je voulais prendre en photos chaque étagère et faire/utiliser un logiciel de reconnaissance de caractère pour lire les titre sur les tranches.</p>
<p>Pensez vous que ça soit faisable/facile et est-ce que ça existe déjà ?</p>
<p>Typiquement, est-ce que c'est facile d'établir la liste des DVD présent dans cette image :</p>
<p><img src="//img.linuxfr.org/img/68747470733a2f2f7777772e66616269656e6d2e65752f706172746167652f6476642e6a7067/dvd.jpg" alt="photo de pochettes de DVD rangées dans une étagère" title="Source : https://www.fabienm.eu/partage/dvd.jpg"></p>
<p>thx</p>
<div><a href="https://linuxfr.org/forums/programmationautre/posts/lister-les-livres-dvd-presents-sur-une-etagere-a-partir-d-une-simple-photos.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/133738/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/forums/programmationautre/posts/lister-les-livres-dvd-presents-sur-une-etagere-a-partir-d-une-simple-photos#comments">ouvrir dans le navigateur</a>
</p>
martonihttps://linuxfr.org/nodes/133738/comments.atomtag:linuxfr.org,2005:Bookmark/72462023-09-27T18:37:08+02:002023-09-27T18:37:08+02:00Pony is a Python ORM with beautiful query syntax<a href="https://ponyorm.org/">https://ponyorm.org/</a> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/133464/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/users/gilcot/liens/pony-is-a-python-orm-with-beautiful-query-syntax#comments">ouvrir dans le navigateur</a>
</p>
Gil Cot ✔https://linuxfr.org/nodes/133464/comments.atomtag:linuxfr.org,2005:Bookmark/70662023-09-03T12:48:12+02:002023-09-03T12:48:12+02:00database of Databases<a href="https://dbdb.io/">https://dbdb.io/</a> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/133172/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/users/gilcot/liens/database-of-databases#comments">ouvrir dans le navigateur</a>
</p>
Gil Cot ✔https://linuxfr.org/nodes/133172/comments.atomtag:linuxfr.org,2005:News/416272023-08-17T08:05:41+02:002023-08-17T08:05:41+02:00ConFoo Montreal 2024 : L'appel aux conférenciers est ouvertLicence CC By‑SA http://creativecommons.org/licenses/by-sa/4.0/deed.fr<div><p><img src="//img.linuxfr.org/img/68747470733a2f2f636f6e666f6f2e63612f696d616765732f64657369676e2f6c6f676f2e676966/logo.gif" alt="Titre de l'image" title="Source : https://confoo.ca/images/design/logo.gif"> </p>
<p>La conférence <a href="https://confoo.ca/fr/2024">ConFoo</a> est de retour pour sa 22<sup>e</sup> édition ! Du 21 au 23 février 2024 à l’Hôtel Bonaventure de Montréal, venez découvrir pourquoi ConFoo est devenu l’un des événements phares pour les développeurs de partout en Amérique du Nord !</p>
<p>Nous sommes présentement à la recherche de conférenciers avides de partager leur expertise et leur savoir dans une multitude de domaine des hautes technologies ; PHP, Ruby, Java, DotNet, JavaScript, bases de données, intelligence artificielle et plus encore !</p>
</div><ul><li>lien nᵒ 1 : <a title="https://confoo.ca/fr/2024/call-for-papers" hreflang="fr" href="https://linuxfr.org/redirect/112565">Appel aux conférenciers</a></li><li>lien nᵒ 2 : <a title="https://confoo.ca/fr/2024" hreflang="fr" href="https://linuxfr.org/redirect/112566">ConFoo.ca</a></li></ul><div><p>Pouvant être offertes en français ou en anglais, nos présentations sont généralement dans un format de 45 minutes, incluant un 10 minutes de questions des participants. Nos conférenciers invités profitent d’un traitement privilégié ; avec notamment la couverture de leurs frais de déplacement et d’hébergement, en plus de l’accès à l’expérience complète de l’événement (présentations, repas, etc.).</p>
<p>Vous avez jusqu’au <strong>22 septembre</strong> prochain pour <a href="https://confoo.ca/fr/2024/call-for-papers">soumettre votre projet de présentation</a> !</p>
<p>Si vous souhaitez simplement vous inscrire en tant que participant, profitez dès maintenant d’un rabais de <strong>400$</strong> en <a href="https://confoo.ca/fr/2024">réservant votre inscription</a> d’ici le 16 octobre prochain !</p>
</div><div><a href="https://linuxfr.org/news/confoo-montreal-2024-l-appel-aux-conferenciers-est-ouvert.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/133001/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/news/confoo-montreal-2024-l-appel-aux-conferenciers-est-ouvert#comments">ouvrir dans le navigateur</a>
</p>
Yann LarriveeNÿcoBenoît SibaudXavier Teyssierhttps://linuxfr.org/nodes/133001/comments.atomtag:linuxfr.org,2005:Bookmark/69542023-08-10T08:26:29+02:002023-08-10T08:26:29+02:00Chiffrement sous PostgreSQL : mais sous quelles conditions ?<a href="https://blog.capdata.fr/index.php/chiffrement-des-donnees-sous-postgresql/">https://blog.capdata.fr/index.php/chiffrement-des-donnees-sous-postgresql/</a> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/132957/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/users/capdata_consulting/liens/chiffrement-sous-postgresql-mais-sous-quelles-conditions#comments">ouvrir dans le navigateur</a>
</p>
capdata_consultinghttps://linuxfr.org/nodes/132957/comments.atomtag:linuxfr.org,2005:News/415632023-07-04T09:28:24+02:002023-07-04T10:57:44+02:00Écrire une appli web en une journée avec SQLPageLicence CC By‑SA http://creativecommons.org/licenses/by-sa/4.0/deed.fr<div><p>Aujourd'hui, je souhaite vous présenter le logiciel <a href="https://sql.ophir.dev">SQLPage</a>, un outil open-source (MIT) qui permet de développer des applications web complètes, avec une belle interface graphique et une base de données, entièrement en SQL.</p>
<p>Le SQL est un langage très simple, qui permet de faire des recherches dans des base de données. Il est utilisé depuis les années 80, et est encore omniprésent aujourd'hui. Contrairement aux langages de programmation traditionnels, on peut apprendre les bases de SQL en une journée, et commencer à faire des requêtes complexes croisant plusieurs tables de données très rapidement. </p>
<p>Dans une application web traditionnelle, on développe aujourd'hui en général trois composants :</p>
<ul>
<li>un <em>front-end</em>, qui gère uniquement l'interface utilisateur,</li>
<li>un <em>back-end</em>, qui traite les requêtes du <em>front-end</em> et contient le cœur de la logique de l'application lorsque celle-ci est complexe,</li>
<li>une <em>base de données</em> qui va stocker et structurer les données, s'assurant de leur cohérence et de leur bonne organisation. </li>
</ul>
<p>Les deux premiers éléments sont en général ceux sur lesquels les programmeurs passent le plus de temps lors du développement d'une application. Et pourtant, c'est souvent le dernier, la base de données, qui contient la substantifique moelle de l'application !</p>
<p>Ce que propose SQLPage, c'est de s'abstraire complètement du <em>back-end</em> et du <em>front-end</em>, et générer toute une application entièrement en SQL. Nous allons voir ici comment c'est possible, avec un exemple concret d'application que nous allons construire ensemble en SQL : à la Tricount.com, une petite application qui permet de gérer ses comptes entre amis.</p>
</div><ul><li>lien nᵒ 1 : <a title="https://sql.ophir.dev" hreflang="en" href="https://linuxfr.org/redirect/112360">Site officiel du projet SQLPage</a></li><li>lien nᵒ 2 : <a title="https://github.com/lovasoa/sqlpage" hreflang="en" href="https://linuxfr.org/redirect/112361">Code source du projet sur Github </a></li></ul><div><h2 class="sommaire">Sommaire</h2>
<ul class="toc">
<li><a href="#toc-est-ce-de-la-sorcellerie">Est-ce de la sorcellerie ?</a></li>
<li><a href="#toc-comment-%C3%A7a-marche">Comment ça marche ?</a></li>
<li>
<a href="#toc-construisons-une-application">Construisons une application</a><ul>
<li>
<a href="#toc-notre-application--une-application-opensource-pour-faire-ses-comptes-entre-amis">Notre application : une application opensource pour faire ses comptes entre amis</a><ul>
<li><a href="#toc-premi%C3%A8re-%C3%A9tape--choisir-un-sch%C3%A9ma-pour-notre-base-de-donn%C3%A9es">Première étape : choisir un schéma pour notre base de données</a></li>
<li><a href="#toc-deuxi%C3%A8me-%C3%A9tape--cr%C3%A9ation-de-la-base-de-donn%C3%A9es-et-lancement-de-sqlpage">Deuxième étape : création de la base de données et lancement de SQLPage</a></li>
<li><a href="#toc-troisi%C3%A8me-%C3%A9tape--cr%C3%A9ation-de-notre-premi%C3%A8re-page-web">Troisième étape : création de notre première page web</a></li>
<li><a href="#toc-insertion-de-donn%C3%A9es-dans-la-base-de-donn%C3%A9es">Insertion de données dans la base de données</a></li>
</ul>
</li>
<li>
<a href="#toc-am%C3%A9lioration-de-lapplication-cr%C3%A9ation-de-nouvelles-pages">Amélioration de l'application, création de nouvelles pages</a><ul>
<li><a href="#toc-cerise-sur-le-g%C3%A2teau--calcul-des-dettes">Cerise sur le gâteau : calcul des dettes</a></li>
</ul>
</li>
</ul>
</li>
<li>
<a href="#toc-conclusion">Conclusion</a><ul>
<li>
<ul>
<li><a href="#toc-pour-r%C3%A9sumer-ce-que-nous-avons-vu">Pour résumer ce que nous avons vu</a></li>
<li><a href="#toc-pour-aller-plus-loin">Pour aller plus loin</a></li>
</ul>
</li>
</ul>
</li>
</ul>
<h2 id="toc-est-ce-de-la-sorcellerie">Est-ce de la sorcellerie ?</h2>
<p>Tout d'abord, mettons les choses au clair : votre application aura bien un backend et un frontend, il n'y a pas de miracle. Mais pour les applications simples, le frontend est souvent juste un assemblage de composants standards, et le backend qu'une sorte de passe-plats entre le frontend et la base de données. Ce que permet SQLPage, et que nous allons étudier ici c'est :</p>
<ul>
<li>d'invoquer des composants prédéfinis d'interface graphique en donnant simplement leur nom et quelques paramètres,</li>
<li>de faire le lien entre l'interface graphique et la base de données avec de simples fichiers SQL qui sont exécutés automatiquement lorsque l'utilisateur charge une page. </li>
</ul>
<h2 id="toc-comment-ça-marche">Comment ça marche ?</h2>
<p>SQLPage est un simple serveur web : c'est un programme qui tourne en continu, attend des requêtes HTTP, et dès qu'il en reçoit une, fournit une réponse.</p>
<p>Si SQLPage reçoit une requête vers <code>/site/contenu.sql?article=42</code>, il va chercher un fichier nommé <code>contenu.sql</code>, dans un dossier nommé <code>site</code>. Il va ensuite lire le contenu du fichier, et l'interpréter comme une série de requêtes SQL, qui vont être préparées. Elles seront ensuite exécutées une par une. Si l'une de ces requêtes fait référence à une variable nommée <code>$article</code>, la valeur <code>42</code> venant de la requête de l'utilisateur lui sera associée.</p>
<p><img src="//img.linuxfr.org/img/68747470733a2f2f6769746875622e636f6d2f6c6f7661736f612f53514c706167652f626c6f622f6d61696e2f646f63732f6172636869746563747572652e706e673f7261773d74727565/architecture.png?raw=true" alt="architecture sqlpage" title="Source : https://github.com/lovasoa/SQLpage/blob/main/docs/architecture.png?raw=true"></p>
<p>Les requêtes sont envoyées à la base de données, et celle-ci commence à retourner des lignes de données, une par une.</p>
<p>Les lignes vont ensuite être analysées <em>au fil de l'eau</em> par SQLPage, qui va décider quel composant graphique renvoyer au navigateur web, et quelles données utiliser pour remplir le composant. </p>
<h2 id="toc-construisons-une-application">Construisons une application</h2>
<p>Pour rendre tout ce discours plus concret, créons ensemble une petite application, entièrement en SQL, et en vingt minutes. </p>
<p>Pour vous donner un avant-goût, voilà ce à quoi nous allons arriver au final</p>
<table>
<thead>
<tr>
<th>Page d'accueil</th>
<th>Gestion d'utilisateurs</th>
<th>Liste de dépenses</th>
<th>Graphique de dettes</th>
</tr>
</thead>
<tbody>
<tr>
<td><img src="//img.linuxfr.org/img/68747470733a2f2f6769746875622e636f6d2f6c6f7661736f612f53514c706167652f6173736574732f3535323632392f33373762326161632d656332372d343238322d383962392d626432366664373737643961/377b2aac-ec27-4282-89b9-bd26fd777d9a" alt="image" title="Source : https://github.com/lovasoa/SQLpage/assets/552629/377b2aac-ec27-4282-89b9-bd26fd777d9a"></td>
<td><img src="//img.linuxfr.org/img/68747470733a2f2f6769746875622e636f6d2f6c6f7661736f612f53514c706167652f6173736574732f3535323632392f37613564343666312d666564382d346532392d613862362d336638633139656136336165/7a5d46f1-fed8-4e29-a8b6-3f8c19ea63ae" alt="image" title="Source : https://github.com/lovasoa/SQLpage/assets/552629/7a5d46f1-fed8-4e29-a8b6-3f8c19ea63ae"></td>
<td><img src="//img.linuxfr.org/img/68747470733a2f2f6769746875622e636f6d2f6c6f7661736f612f53514c706167652f6173736574732f3535323632392f33646339306138352d613563322d343464392d616133332d646265383634373061653339/3dc90a85-a5c2-44d9-aa33-dbe86470ae39" alt="image" title="Source : https://github.com/lovasoa/SQLpage/assets/552629/3dc90a85-a5c2-44d9-aa33-dbe86470ae39"></td>
<td><img src="//img.linuxfr.org/img/68747470733a2f2f6769746875622e636f6d2f6c6f7661736f612f53514c706167652f6173736574732f3535323632392f31613866623865342d336461642d343030612d626535342d656235623534656339303162/1a8fb8e4-3dad-400a-be54-eb5b54ec901b" alt="image" title="Source : https://github.com/lovasoa/SQLpage/assets/552629/1a8fb8e4-3dad-400a-be54-eb5b54ec901b"></td>
</tr>
</tbody>
</table>
<p>Il n'y a pas toutes les fonctionnalités de l'application originelle, mais c'est seulement 83 lignes de code, grâce à tout ce que SQLPage gère automatiquement. Et le résultat est quand même plus joli que l'original.</p>
<h3 id="toc-notre-application--une-application-opensource-pour-faire-ses-comptes-entre-amis">Notre application : une application opensource pour faire ses comptes entre amis</h3>
<p>Nous allons créer une application pour faire ses comptes entre amis. Elle aura les fonctionnalités suivantes :</p>
<ul>
<li>créer un nouveau compte de dépenses partagé</li>
<li>ajouter des participants et visualiser la liste des participants existants</li>
<li>pour chaque participant :
<ul>
<li>ajouter une dépense</li>
<li>voir les dépenses des autres</li>
<li>voir combien il doit au reste du groupe ou combien lui est dû</li>
</ul>
</li>
</ul>
<h4 id="toc-première-étape--choisir-un-schéma-pour-notre-base-de-données">Première étape : choisir un schéma pour notre base de données</h4>
<p>Et oui, on ne va pas passer quatre jours à choisir un framework JavaScript, un framework CSS, un ORM, ou autres choses compliquées que l'on fait quand on commence une application web classique. Avec SQLPage, on rentre tout de suite dans le cœur du sujet, et ce qui sera important pour la suite: quelles données stockerons-nous, et sous quelle forme. </p>
<p>Ici, je propose le schéma suivant :</p>
<ul>
<li>une table <code>expense_group</code> pour nos comptes de dépenses partagés, avec un identifiant numérique et un nom. </li>
<li>une table <code>group_member</code> pour les utilisateurs, avec un identifiant numérique, un nom, et l'identifiant du compte partagé auquel il appartient. </li>
<li>une table <code>expense</code> pour les dépenses, avec l'identifiant de l'utilisateur ayant fait la dépense, une description, et un montant. Pour cet exemple, nous ne prendrons pas en compte le cas où une dépense peut ne concerner qu'une partie du groupe; ce sera simple à ajouter dans un second temps. </li>
</ul>
<h4 id="toc-deuxième-étape--création-de-la-base-de-données-et-lancement-de-sqlpage">Deuxième étape : création de la base de données et lancement de SQLPage</h4>
<p>C'est parti ! <a href="https://github.com/lovasoa/SQLpage/releases">Téléchargeons SQLPage sur le site officiel</a>. </p>
<p>Créons un dossier pour notre application, et dans ce dossier créons la structure de fichiers suivante:</p>
<pre><code>├── sqlpage
│ ├── migrations
│ │ └── 000_base.sql
│ └── sqlpage.json
└── sqlpage.bin
</code></pre>
<p>Nous créons donc les fichiers suivants:</p>
<ul>
<li>
<code>sqlpage/migrations/000_base.sql</code> dans lequel nous définirons la structure de notre base de données</li>
<li>
<code>sqlpage/sqlpage.json</code> dans lequel nous mettrons pour l'instant simplement la ligne suivante: <code>{"database_url": "sqlite://:memory:"}</code>. Cela nous permet de travailler avec une base de données temporaire en mémoire. Nous le modifierons plus tard pour nous connecter à une base de données plus pérenne.</li>
</ul>
<p>Intéressons-nous d'abord à <code>sqlpage/migrations/000_base.sql</code>. Pour créer la structure de base de données définie plus tôt, utilisons quelques <a href="https://www.sqlite.org/lang_createtable.html">instructions de création de table</a> :</p>
<pre><code class="sql"><span class="k">CREATE</span> <span class="k">TABLE</span> <span class="n">expense_group</span><span class="p">(</span>
<span class="n">id</span> <span class="nb">INTEGER</span> <span class="k">PRIMARY</span> <span class="k">KEY</span> <span class="n">AUTOINCREMENT</span><span class="p">,</span>
<span class="n">name</span> <span class="nb">TEXT</span>
<span class="p">);</span>
<span class="k">CREATE</span> <span class="k">TABLE</span> <span class="n">group_member</span><span class="p">(</span>
<span class="n">id</span> <span class="nb">INTEGER</span> <span class="k">PRIMARY</span> <span class="k">KEY</span> <span class="n">AUTOINCREMENT</span><span class="p">,</span>
<span class="n">group_id</span> <span class="nb">INTEGER</span> <span class="k">REFERENCES</span> <span class="n">expense_group</span><span class="p">(</span><span class="n">id</span><span class="p">),</span>
<span class="n">name</span> <span class="nb">TEXT</span>
<span class="p">);</span>
<span class="k">CREATE</span> <span class="k">TABLE</span> <span class="n">expense</span><span class="p">(</span>
<span class="n">id</span> <span class="nb">INTEGER</span> <span class="k">PRIMARY</span> <span class="k">KEY</span> <span class="n">AUTOINCREMENT</span><span class="p">,</span>
<span class="n">spent_by</span> <span class="nb">INTEGER</span> <span class="k">REFERENCES</span> <span class="n">group_member</span><span class="p">(</span><span class="n">id</span><span class="p">),</span> <span class="c1">-- identifiant du membre qui a fait la dépense</span>
<span class="nb">date</span> <span class="k">TIMESTAMP</span> <span class="k">DEFAULT</span> <span class="k">CURRENT_TIMESTAMP</span><span class="p">,</span> <span class="c1">-- date et heure de la dépense</span>
<span class="n">name</span> <span class="nb">TEXT</span><span class="p">,</span> <span class="c1">-- intitulé</span>
<span class="n">amount</span> <span class="nb">DECIMAL</span> <span class="c1">-- montant en euros</span>
<span class="p">);</span></code></pre>
<p>On peut maintenant lancer l'exécutable <code>sqlpage.bin</code> (ou <code>sqlpage.exe</code> sous Windows 😬) depuis le dossier de notre site.</p>
<p>Il doit se lancer, et afficher dans le terminal le message suivant : <code>Applying migrations from 'sqlpage/migrations [...] Found 1 migrations</code>. Cela signifie qu'il a créé avec succès notre base de données selon le schéma demandé.</p>
<p>En ouvrant la page <code>http://localhost:8080</code> sur notre navigateur web, nous devrions voir le message suivant:</p>
<p><img src="//img.linuxfr.org/img/68747470733a2f2f6769746875622e636f6d2f6c6f7661736f612f53514c706167652f6173736574732f3535323632392f34616533643165372d303366322d343230302d616537632d373061636134303537346238/4ae3d1e7-03f2-4200-ae7c-70aca40574b8" alt="Screenshot 2023-06-28 at 16-46-40 SQLpage" title="Source : https://github.com/lovasoa/SQLpage/assets/552629/4ae3d1e7-03f2-4200-ae7c-70aca40574b8"></p>
<h4 id="toc-troisième-étape--création-de-notre-première-page-web">Troisième étape : création de notre première page web</h4>
<p>Le moment tant attendu est arrivé : nous allons créer notre première page web et pouvoir l'ouvrir dans notre navigateur.</p>
<p>Pour cela, créons un fichier nommé <code>index.sql</code> à la racine du dossier de notre site web. À l'intérieur, nous allons écrire une série de requêtes SQL.</p>
<p>SQLPage marche de la manière suivante : on fait une première requête pour invoquer un composant graphique, comme une liste, un formulaire, du texte, ou un graphique. Ensuite, on fait une seconde requête pour définir comment peupler notre composant : les éléments de la liste, les champs du formulaire, les paragraphes de texte, ou les points de notre graphique.</p>
<p>Dans notre cas, notre premier composant sera un formulaire pour créer un nouveau groupe de dépenses à partager entre amis. Pour cela, nous allons invoquer le composant <a href="https://sql.ophir.dev/documentation.sql?component=form#component"><code>form</code></a>. Dans <code>index.sql</code>, écrivons :</p>
<pre><code class="sql"><span class="k">SELECT</span>
<span class="s1">'form'</span> <span class="k">as</span> <span class="n">component</span><span class="p">,</span>
<span class="s1">'Nouveau compte partagé'</span> <span class="k">as</span> <span class="n">title</span><span class="p">,</span>
<span class="s1">'Créer le compte de dépenses partagé !'</span> <span class="k">as</span> <span class="n">validate</span><span class="p">;</span></code></pre>
<p>Cela crée un formulaire, vide, que l'on peut déjà voir dans notre navigateur ! Maintenant, ajoutons un champ dans le formulaire. Immédiatement à la suite de la requête précédente, ajoutons:</p>
<pre><code class="sql"><span class="k">SELECT</span> <span class="s1">'Nom du compte'</span> <span class="k">AS</span> <span class="n">label</span><span class="p">,</span> <span class="s1">'shared_expense_name'</span> <span class="k">AS</span> <span class="n">name</span><span class="p">;</span></code></pre>
<p>Rouvrons notre navigateur, et nous devrions maintenant voir cela :</p>
<p><img src="//img.linuxfr.org/img/68747470733a2f2f6769746875622e636f6d2f6c6f7661736f612f53514c706167652f6173736574732f3535323632392f34643333636562302d343830642d346633342d616465362d356561666333303233383839/4d33ceb0-480d-4f34-ade6-5eafc3023889" alt="sqlpage form" title="Source : https://github.com/lovasoa/SQLpage/assets/552629/4d33ceb0-480d-4f34-ade6-5eafc3023889"></p>
<h4 id="toc-insertion-de-données-dans-la-base-de-données">Insertion de données dans la base de données</h4>
<p>Pour l'instant, lorsque l'on clique sur le bouton <em>Créer le compte de dépenses partagées</em>, il ne se passe rien. Corrigeons cela !</p>
<p>Toujours dans <code>index.sql</code>, à la fin de notre fichier, ajoutons une nouvelle requête SQL :</p>
<pre><code class="sql"><span class="k">INSERT</span> <span class="k">INTO</span> <span class="n">expense_group</span><span class="p">(</span><span class="n">name</span><span class="p">)</span>
<span class="k">SELECT</span> <span class="p">:</span><span class="n">shared_expense_name</span> <span class="k">WHERE</span> <span class="p">:</span><span class="n">shared_expense_name</span> <span class="k">IS</span> <span class="k">NOT</span> <span class="k">NULL</span><span class="p">;</span></code></pre>
<p>Ici, on utilise une requête de type <a href="https://www.sqlite.org/lang_insert.html"><code>INSERT INTO ... SELECT</code></a> pour insérer une nouvelle ligne dans la table <code>expense_group</code>. On ajoute une clause <code>WHERE</code> pour qu'une ligne ne soit insérée que lorsque l'utilisateur a rempli une valeur dans le formulaire, et pas à chaque fois que la page se charge.</p>
<p>La variable SQL <code>:shared_expense_name</code> sera associée à la valeur que l'utilisateur aura rentré dans le champ de texte que nous avons appelé <code>shared_expense_name</code> à l'étape précédente.</p>
<p>Maintenant, chaque validation de formulaire crée une nouvelle ligne dans notre base de données. Il est temps de créer notre premier composant dynamique, dont le contenu va dépendre de ce qu'il y a dans notre base de données. Toujours à la suite, dans <code>index.sql</code>:</p>
<pre><code class="sql"><span class="k">SELECT</span> <span class="s1">'list'</span> <span class="k">as</span> <span class="n">component</span><span class="p">;</span>
<span class="k">SELECT</span>
<span class="n">name</span> <span class="k">AS</span> <span class="n">title</span><span class="p">,</span>
<span class="s1">'group.sql?id='</span> <span class="o">||</span> <span class="n">id</span> <span class="k">AS</span> <span class="n">link</span>
<span class="k">FROM</span> <span class="n">expense_group</span><span class="p">;</span></code></pre>
<p>Ici, nous utilisons un nouvel élément issu de la <a href="https://sql.ophir.dev/documentation.sql">bibliothèque standard de SQLPage</a>: le composant <a href="https://sql.ophir.dev/documentation.sql?component=list#component"><code>list</code></a>. Après l'avoir sélectionné, nous le peuplons avec des données qui viennent de la table <code>expense_group</code> de notre base de données. Pour chaque élément de la liste, nous spécifions un lien vers lequel l'utilisateur sera emmené lorsqu'il cliquera dessus. Pour créer ce lien, nous <a href="https://www.geeksforgeeks.org/sql-concatenation-operator/">concaténons</a> le nom d'un nouveau fichier SQL que nous allons créer, avec une variable qui contient l'identifiant du groupe à afficher.</p>
<p><img src="//img.linuxfr.org/img/68747470733a2f2f6769746875622e636f6d2f6c6f7661736f612f53514c706167652f6173736574732f3535323632392f38653331626633632d663132362d343738392d393231652d376134303364643164333166/8e31bf3c-f126-4789-921e-7a403dd1d31f" alt="Liste dynamique avec SQLPage" title="Source : https://github.com/lovasoa/SQLpage/assets/552629/8e31bf3c-f126-4789-921e-7a403dd1d31f"></p>
<h3 id="toc-amélioration-de-lapplication-création-de-nouvelles-pages">Amélioration de l'application, création de nouvelles pages</h3>
<p>Nous avons maintenant vu tous les éléments nécessaires à la construction d'une application. Il ne nous reste plus qu'à les appliquer à la création des pages restantes de notre application opensource.</p>
<p>Dans <code>group.sql</code>, réutilisons les composants from et list que nous connaissons maintenant :</p>
<pre><code class="sql"><span class="k">SELECT</span> <span class="s1">'title'</span> <span class="k">as</span> <span class="n">component</span><span class="p">,</span> <span class="n">name</span> <span class="k">as</span> <span class="n">contents</span>
<span class="k">FROM</span> <span class="n">expense_group</span> <span class="k">WHERE</span> <span class="n">id</span> <span class="o">=</span> <span class="err">$</span><span class="n">id</span><span class="p">;</span>
<span class="k">INSERT</span> <span class="k">INTO</span> <span class="n">group_member</span><span class="p">(</span><span class="n">group_id</span><span class="p">,</span> <span class="n">name</span><span class="p">)</span>
<span class="k">SELECT</span> <span class="err">$</span><span class="n">id</span><span class="p">,</span> <span class="p">:</span><span class="n">new_member_name</span> <span class="k">WHERE</span> <span class="p">:</span><span class="n">new_member_name</span> <span class="k">IS</span> <span class="k">NOT</span> <span class="k">NULL</span><span class="p">;</span>
<span class="k">SELECT</span> <span class="s1">'list'</span> <span class="k">as</span> <span class="n">component</span><span class="p">,</span> <span class="s1">'Membres'</span> <span class="k">as</span> <span class="n">title</span><span class="p">;</span>
<span class="k">SELECT</span> <span class="n">name</span> <span class="k">AS</span> <span class="n">title</span> <span class="k">FROM</span> <span class="n">group_member</span> <span class="k">WHERE</span> <span class="n">group_id</span><span class="o">=</span><span class="err">$</span><span class="n">id</span><span class="p">;</span>
<span class="k">SELECT</span> <span class="s1">'form'</span> <span class="k">as</span> <span class="n">component</span><span class="p">,</span> <span class="s1">'Ajouter un membre au groupe'</span> <span class="k">as</span> <span class="n">validate</span><span class="p">;</span>
<span class="k">SELECT</span> <span class="s1">'Nom du membre'</span> <span class="k">AS</span> <span class="s1">'label'</span><span class="p">,</span> <span class="s1">'new_member_name'</span> <span class="k">AS</span> <span class="n">name</span><span class="p">;</span>
<span class="k">SELECT</span> <span class="s1">'title'</span> <span class="k">as</span> <span class="n">component</span><span class="p">,</span> <span class="s1">'Dépenses'</span> <span class="k">as</span> <span class="n">contents</span><span class="p">;</span>
<span class="k">SELECT</span> <span class="s1">'form'</span> <span class="k">as</span> <span class="n">component</span><span class="p">,</span> <span class="s1">'Ajouter une dépense'</span> <span class="k">as</span> <span class="n">title</span><span class="p">,</span> <span class="s1">'Ajouter'</span> <span class="k">as</span> <span class="n">validate</span><span class="p">;</span>
<span class="k">SELECT</span> <span class="s1">'Description'</span> <span class="k">AS</span> <span class="n">name</span><span class="p">;</span>
<span class="k">SELECT</span> <span class="s1">'Montant'</span> <span class="k">AS</span> <span class="n">name</span><span class="p">,</span> <span class="s1">'number'</span> <span class="k">AS</span> <span class="k">type</span><span class="p">;</span>
<span class="k">SELECT</span> <span class="s1">'select'</span> <span class="k">as</span> <span class="k">type</span><span class="p">,</span> <span class="s1">'Dépensé par'</span> <span class="k">AS</span> <span class="n">name</span><span class="p">,</span>
<span class="n">json_group_array</span><span class="p">(</span><span class="n">json_object</span><span class="p">(</span><span class="ss">"label"</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="ss">"value"</span><span class="p">,</span> <span class="n">id</span><span class="p">))</span> <span class="k">as</span> <span class="k">options</span>
<span class="k">FROM</span> <span class="n">group_member</span> <span class="k">WHERE</span> <span class="n">group_id</span> <span class="o">=</span> <span class="err">$</span><span class="n">id</span><span class="p">;</span>
<span class="k">INSERT</span> <span class="k">INTO</span> <span class="n">expense</span><span class="p">(</span><span class="n">spent_by</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">amount</span><span class="p">)</span>
<span class="k">SELECT</span> <span class="p">:</span><span class="ss">"Dépensé par"</span><span class="p">,</span> <span class="p">:</span><span class="n">Description</span><span class="p">,</span> <span class="p">:</span><span class="n">Montant</span> <span class="k">WHERE</span> <span class="p">:</span><span class="n">Montant</span> <span class="k">IS</span> <span class="k">NOT</span> <span class="k">NULL</span><span class="p">;</span>
<span class="k">SELECT</span> <span class="s1">'card'</span> <span class="k">as</span> <span class="n">component</span><span class="p">,</span> <span class="s1">'Dépenses'</span> <span class="k">as</span> <span class="n">title</span><span class="p">;</span>
<span class="k">SELECT</span>
<span class="n">expense</span><span class="p">.</span><span class="n">name</span> <span class="k">as</span> <span class="n">title</span><span class="p">,</span>
<span class="s1">'Par '</span> <span class="o">||</span> <span class="n">group_member</span><span class="p">.</span><span class="n">name</span> <span class="o">||</span> <span class="s1">', le '</span> <span class="o">||</span> <span class="n">expense</span><span class="p">.</span><span class="nb">date</span> <span class="k">as</span> <span class="n">description</span><span class="p">,</span>
<span class="n">expense</span><span class="p">.</span><span class="n">amount</span> <span class="o">||</span> <span class="s1">' €'</span> <span class="k">as</span> <span class="n">footer</span><span class="p">,</span>
<span class="k">CASE</span> <span class="k">WHEN</span> <span class="n">expense</span><span class="p">.</span><span class="n">amount</span> <span class="o">></span> <span class="mi">100</span> <span class="k">THEN</span> <span class="s1">'red'</span> <span class="k">WHEN</span> <span class="n">expense</span><span class="p">.</span><span class="n">amount</span> <span class="o">></span> <span class="mi">50</span> <span class="k">THEN</span> <span class="s1">'orange'</span> <span class="k">ELSE</span> <span class="s1">'blue'</span> <span class="k">END</span> <span class="k">AS</span> <span class="n">color</span>
<span class="k">FROM</span> <span class="n">expense</span>
<span class="k">INNER</span> <span class="k">JOIN</span> <span class="n">group_member</span> <span class="k">on</span> <span class="n">expense</span><span class="p">.</span><span class="n">spent_by</span> <span class="o">=</span> <span class="n">group_member</span><span class="p">.</span><span class="n">id</span>
<span class="k">WHERE</span> <span class="n">group_member</span><span class="p">.</span><span class="n">group_id</span> <span class="o">=</span> <span class="err">$</span><span class="n">id</span><span class="p">;</span></code></pre>
<p>Nous avons ici créé une seule page, qui contient plusieurs listes et plusieurs formulaires, juste en écrivant nos requêtes SQL les unes après les autres dans notre fichier. </p>
<p>Le seul point particulier à noter, qui est différent de ce que nous avons vu avant, est l'utilisation de la fonction sql <a href="https://www.sqlite.org/json1.html#jgrouparray"><code>json_group_array</code></a> pour remplir la valeur du champ de formulaire à choix multiple, qui prend un tableau json comme valeur.</p>
<p>Nous arrivons au résultat suivant :</p>
<p><img src="//img.linuxfr.org/img/68747470733a2f2f6769746875622e636f6d2f6c6f7661736f612f53514c706167652f6173736574732f3535323632392f31316338366331652d336261642d343364312d393162622d326538396465313731613037/11c86c1e-3bad-43d1-91bb-2e89de171a07" alt="screenshot sqlpage" title="Source : https://github.com/lovasoa/SQLpage/assets/552629/11c86c1e-3bad-43d1-91bb-2e89de171a07"></p>
<h4 id="toc-cerise-sur-le-gâteau--calcul-des-dettes">Cerise sur le gâteau : calcul des dettes</h4>
<p>Une fonctionnalité pratique de l'application originelle est le calcul du tableau de dette. L'application fait elle-même le calcul final de qui doit combien. C'est un peu moins trivial que les requêtes classiques de listage de données que l'on a vues jusqu'ici, mais on peut aussi implémenter cela entièrement en SQL.</p>
<p>On crée quelques <a href="https://fr.wikipedia.org/wiki/Vue_(base_de_donn%C3%A9es)">vues</a> qui nous seront utiles pour nos calculs. </p>
<p>Dans SQLPage, on ne crée en général pas de fonctions, et on n'importe pas des bibliothèques. Pour construire une fonctionnalité complexe, le plus simple est de construire des vues successives de nos données, dans lesquelles on les groupe et les filtre comme on le suite. Ici, on construit les trois vues simples suivantes, chacune avec sa fonction SQL :</p>
<ul>
<li>
<a href="https://github.com/lovasoa/SQLpage/blob/main/examples/splitwise/sqlpage/migrations/0001_views.sql#L3-L9"><code>members_with_expenses</code></a>, qui va lier nos tables entre elles pour associer les noms des membres à leurs montants de dépenses.</li>
<li>
<a href="https://github.com/lovasoa/SQLpage/blob/main/examples/splitwise/sqlpage/migrations/0001_views.sql#L12-L16"><code>average_debt_per_person</code></a> qui va diviser le montant total dépensé par le groupe par le nombre de participants.</li>
<li>
<a href="https://github.com/lovasoa/SQLpage/blob/main/examples/splitwise/sqlpage/migrations/0001_views.sql#L19-L27"><code>individual_debts</code></a> qui va soustraire la dépense moyenne aux dépenses personnelles de chacun, pour savoir combien il doit ou combien on lui doit. </li>
</ul>
<p>Ici c'est du SQL classique, il n'y a rien qui soit propre à SQLPage. Je vous laisse lire <a href="https://github.com/lovasoa/SQLpage/blob/main/examples/splitwise/sqlpage/migrations/0001_views.sql">les 27 lignes de code sur github</a>. </p>
<h2 id="toc-conclusion">Conclusion</h2>
<p>Nous avons vu comment construire une application web complète entièrement en SQL grâce à SQLPage. Nous pouvons maintenant la faire tourner sur un tout petit serveur chez nous, dans le cloud, ou même <a href="https://github.com/lovasoa/SQLpage#serverless">en mode sans-serveur</a>. SQLPage est écrit dans le langage de programmation <em>rust</em> et consomme très peu de resources par rapport à une application web classique, l'application sera donc très peu chère à héberger.</p>
<h4 id="toc-pour-résumer-ce-que-nous-avons-vu">Pour résumer ce que nous avons vu</h4>
<p>Nous avons tout d'abord <a href="#toc-premi%C3%A8re-%C3%A9tape-choisir-un-sch%C3%A9ma-pour-notre-base-de-donn%C3%A9es">créé une structure de base de données grâce aux migrations</a>.</p>
<p>Ensuite, nous avons affiché des composants graphiques grâce à la <a href="https://sql.ophir.dev/documentation.sql">bibliothèque de composants intégrés</a> de SQLPage.</p>
<p>Enfin, nous avons inséré des données dynamiquement dans notre base de données grâce au système de variables de SQLPage.</p>
<h4 id="toc-pour-aller-plus-loin">Pour aller plus loin</h4>
<p>SQLPage est un logiciel libre et gratuit. Si vous rencontrez des problèmes lors de son utilisation, n'hésitez pas à <a href="https://github.com/lovasoa/SQLpage/issues">rapporter un bug ou demander une fonctionnalité sur github</a>, ou à discuter de son utilisation sur les <a href="https://github.com/lovasoa/SQLpage/discussions">pages de discussion</a>.</p>
<p>Et si vous cherchez une idée pour vous entraîner… Pourquoi pas un <a href="//linuxfr.org/wiki/taptempo">TapTempo</a> entièrement en SQL ?</p>
</div><div><a href="https://linuxfr.org/news/ecrire-une-appli-web-en-une-journee-avec-sqlpage.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/131690/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/news/ecrire-une-appli-web-en-une-journee-avec-sqlpage#comments">ouvrir dans le navigateur</a>
</p>
lovasoaNÿcoBenoît SibaudgUIBAudbobble bubblehttps://linuxfr.org/nodes/131690/comments.atomtag:linuxfr.org,2005:Diary/406682023-04-22T22:31:52+02:002023-04-22T22:31:52+02:00RPCDataloader: chargement et pré-traitement de données distribué pour l'IALicence CC By‑SA http://creativecommons.org/licenses/by-sa/4.0/deed.fr<h2 class="sommaire">Sommaire</h2>
<ul class="toc">
<li><a href="#toc-introduction">Introduction</a></li>
<li><a href="#toc-contexte--entra%C3%AEnement-de-r%C3%A9seaux-de-neurones-avec-pytorch">Contexte : Entraînement de réseaux de neurones avec PyTorch</a></li>
<li><a href="#toc-le-pitch">Le pitch</a></li>
<li><a href="#toc-initialisation">Initialisation</a></li>
<li><a href="#toc-communication">Communication</a></li>
<li><a href="#toc-gestion-des-variables-distantes">Gestion des variables distantes</a></li>
<li><a href="#toc-conclusion">Conclusion</a></li>
</ul>
<h2 id="toc-introduction">Introduction</h2>
<p>On continue la lignée des utilitaires pour une grappe de calcul IA (cluster en bon franglais).<br>
Cette fois-ci, l'objectif est de déplacer le chargement et le pré-traitement des données sur des serveurs différents de ceux qui gèrent le modèle en lui-même.</p>
<p>On s'intéresse plus précisément à un algorithme d’entraînement de réseau de neurones avec le framework <a href="https://www.pytorch.org">PyTorch</a>. Bien que la librairie que je présente (<a href="https://github.com/CEA-LIST/RPCDataloader">RPCDataloader</a>) n'ait pas une très forte dépendance à PyTorch, son API est conçue pour s'intégrer avec.</p>
<h2 id="toc-contexte--entraînement-de-réseaux-de-neurones-avec-pytorch">Contexte : Entraînement de réseaux de neurones avec PyTorch</h2>
<p>Algorithmiquement, l’entraînement d'un réseau de neurones suit les étapes suivantes en boucle :</p>
<ol>
<li>On prépare un petit ensemble d'exemples (textes, images, sons, etc.) tiré d'un grand corpus.</li>
<li>On applique un modèle sur ces données.</li>
<li>On applique une métrique entre la prédiction et la vérité terrain attendue.</li>
<li>On calcule la dérivée de cette métrique par rapport aux paramètres du modèle.</li>
<li>On fait un pas de gradient sur les paramètres dans le sens qui maximise la métrique (ou la minimise selon l'objectif).</li>
</ol>
<p>La première étape est réalisée sur CPU et comprend des pré-traitements potentiellement lourds. Le reste est fait sur GPU car les opérations à effectuer s'y prêtent bien.</p>
<p>Par "sur GPU", on veut en fait dire qu'un processus (en l'occurrence un script python) suit les étapes algorithmiques, les décompose en calculs élémentaires, puis soumet ces calculs au GPU (via une file d'attente). Pour maximiser le taux d'utilisation du GPU, il faut donc que la partie logique (le script) reste en avance sur les calculs.</p>
<p>Le script est écrit par le chercheur, mais PyTorch fournit une énorme bibliothèque d'algorithmes allant de la simple addition à des opérations composées très haut niveau, par exemple le calcul de la dérivée en un point de la sortie d'un modèle par rapport à ses paramètres.</p>
<p>PyTorch fournit aussi des outils pour le chargement et la préparation des données. Comme évoqué précédemment, ces calculs ne sont pas déportés sur un GPU mais dans des processus séparés. Le script principal là encore le travail (i.e. les indices des éléments à préparer) via une file d'attente.</p>
<p>Le schéma ci-dessous donne une vue simplifiée de l'ensemble.</p>
<p><img src="//img.linuxfr.org/img/68747470733a2f2f7261772e67697468756275736572636f6e74656e742e636f6d2f676973742f6e6c6772616e6765722f33303166346462633131343463333036373863303466653337336439383237652f7261772f376266393937373363613361326662383562323134643563383766393736316262303432343262622f7079746f7263685f6f766572766965772e737667/pytorch_overview.svg" alt="Fonctionnement général de PyTorch" title="Source : https://raw.githubusercontent.com/gist/nlgranger/301f4dbc1144c30678c04fe373d9827e/raw/7bf99773ca3a2fb85b214d5c87f9761bb04242bb/pytorch_overview.svg"></p>
<h2 id="toc-le-pitch">Le pitch</h2>
<p>Le mode de fonctionnement présenté ci-dessus fonctionne plutôt pas mal dans la mesure où la puissance de calcul du GPU est bien souvent le facteur limitant. Le script principal, bien qu'il soit en python, parvient à garder de l'avance sur le GPU.</p>
<p>Là où ça coince, c'est plutôt au niveau des prétraitements. Comment faire si toute la puissance des CPUs n'est pas suffisante pour alimenter le reste de la chaîne en continu ?<br>
On gâche alors de la ressource GPU et on occupe un serveur plus longtemps. C'est aussi mauvais pour le taux d'utilisation d'un cluster multi-utilisateur. Une expérience qui réserve 75% des CPUs mais seulement 50% des GPUs bloquera souvent un nœud de calcul entier, car les expériences avec des besoins complémentaires sont rares en pratique.</p>
<p>Vous allez me dire qu'on aurait dû acheter des serveurs avec plus de CPUs ou moins de GPUs. Je vous répondrai que c'est un exercice difficile, prévoir de la marge coûte très cher et on dimensionne pour le cas général, pas l'exception.</p>
<p>L'idée pour résoudre ce problème, c'est tout simplement de <strong>déplacer les processus de chargement et de préparation des données sur un autre nœud de calcul</strong>.</p>
<h2 id="toc-initialisation">Initialisation</h2>
<p>Dans PyTorch, les processus pour les données sont obtenus en forkant le processus principal. C'est ainsi qu'ils récupèrent un objet décrivant le dataset et qui contient généralement la liste des fichiers ainsi que les données qui peuvent rentrer en mémoire. C'est simple, mais peu efficace en utilisation de RAM car les processus enfants gardent les autres variables du parent, tandis que le script principal garde inutilement les données relatives au dataset.</p>
<p>Dans RPCDataLoader, on démarre des petits serveurs TCP qui attendent des commandes à exécuter. C'est de l'exécution de procédure distante (<a href="https://fr.wikipedia.org/wiki/Remote_procedure_call">RPC</a>) très simplifiée. Le script principal leur ordonne initialement d'instancier un objet pour le dataset, puis leur ordonne de charger et de traiter tel ou tel indice.</p>
<h2 id="toc-communication">Communication</h2>
<p>À l'origine, les processus données reçoivent leurs instructions du script principal via un <a href="https://docs.python.org/fr/3/library/multiprocessing.html?highlight=pipe#multiprocessing.Pipe">Pipe</a>.<br>
Comme le résultat renvoyé dans l'autre sens est plutôt lourd, seule une description de la réponse est renvoyée via un Pipe, tandis que les buffers de données sont placés en mémoire partagée pour économiser une copie… Mais une copie des buffers sera quand même nécessaire, car il faut placer ces données en mémoire verrouillée (<a href="https://engineering.purdue.edu/%7Esmidkiff/ece563/NVidiaGPUTeachingToolkit/Mod14DataXfer/Mod14DataXfer.pdf">pinned memory</a>). En gros ça garantit au GPU que Linux ne va pas déplacer ces plages de la mémoire, ce qui permet de faire des transferts plus rapides et asynchrones.</p>
<p>Dans RPCDataLoader, les pipes sont remplacés par des sockets TCP et l'optimisation de la copie grâce à la mémoire partagée est perdue… ou presque !<br>
En effet, le module de sérialisation de données de python, appelé <em>pickle</em>, propose une fonctionnalité très intéressante bien qu'un peu obscure : on peut sérialiser et désérialiser les buffers de données séparément de l'objet qui les encapsule (arguments <a href="https://docs.python.org/3/library/pickle.html#pickle.Pickler">buffer_callback</a> et <a href="https://docs.python.org/3/library/pickle.html#pickle.Unpickler">buffers</a>). Dans RPCDataloader j'en profite donc pour allouer les buffers directement en <em>pinned memory</em> avant de les réceptionner, ce qui économise une copie.</p>
<h2 id="toc-gestion-des-variables-distantes">Gestion des variables distantes</h2>
<p>Parmi les commandes que le script envoie au processus distants, il y a la création de variables. Cela pose la question de savoir quand elles devront être détruites. Le script distant ne pouvant pas prendre cette initiative, j'alloue pour chaque variable distante une référence locale qui sert de témoin. Avec la fonction <a href="https://docs.python.org/3/library/weakref.html#weakref.finalize"><code>weakref.finalize</code></a> de python, on peut faire en sorte d'être notifié à la destruction du témoin local et d'ordonner simultanément la suppression de la variable distante.<br>
C'est l'API <a href="https://pytorch.org/docs/stable/rpc.html">RPC PyTorch</a> qui m'a inspiré cette manière de faire.</p>
<p>En surchargeant la procédure de sérialisation/dé-sérialisation de cet objet, on peut aussi obtenir un comportement assez élégant lors de l'envoi d'une commande à appliquer sur l'objet distant :</p>
<pre><code class="py"><span class="n">rpc_async</span><span class="p">(</span>
<span class="n">host</span><span class="o">=</span><span class="s2">"node1:8888"</span><span class="p">,</span>
<span class="n">func</span><span class="o">=</span><span class="n">somefunc</span><span class="p">,</span>
<span class="n">args</span><span class="o">=</span><span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="n">my_rref</span><span class="p">])</span></code></pre>
<p>Lorsque le processus distant réceptionne la commande, <code>my_rref</code> devient l'objet référencé durant la dé-sérialisation. Ça fonctionne même si la référence est encapsulée à l'intérieur d'une liste ou d'un objet.</p>
<p>Le code est <a href="https://github.com/CEA-LIST/RPCDataloader/blob/master/rpcdataloader/rpc.py#L266-L301">ici</a> si jamais mes explications ne sont pas claires.</p>
<h2 id="toc-conclusion">Conclusion</h2>
<p>La librairie fonctionne et les performances sont plutôt satisfaisantes. Je craignais que le passage par le réseau et par pickle entraînerait de la latence et un surcoût CPU, mais il n'en est rien. Il a fallu par contre prendre garde à quelques détails d'implémentation sur la partie réseaux pour ne pas voir s'effondrer les performances. Et il faut bien entendu un réseau très rapide.</p>
<p>Sur l'exemple en lien ci-dessous, on peut occuper 8 NVidia A100 à plus de 80% en utilisant l'équivalent de 10 CPUs sur le nœud principal. La configuration typique pour accompagner ces 8 GPUs prévoit 128 cœurs, précisément pour absorber la charge des pré-traitements de données localement.</p>
<p>À l'avenir, j'aimerais ajouter une forme d'authentification, car n'importe qui peut envoyer une commande aux workers actuellement, ce qui restreint l'utilisation à un environnement de confiance.</p>
<p>Pour aller plus loin :</p>
<ul>
<li><a href="https://github.com/CEA-LIST/RPCDataloader">Le dépôt</a></li>
<li><a href="https://cea-list.github.io/RPCDataloader/index.html">La doc</a></li>
<li><a href="https://cea-list.github.io/RPCDataloader/examples.html">Un exemple de script d'apprentissage avec son script slurm</a></li>
</ul>
<div><a href="https://linuxfr.org/users/nlgranger/journaux/rpcdataloader-chargement-et-pre-traitement-de-donnees-distribue-pour-l-ia.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/131018/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/users/nlgranger/journaux/rpcdataloader-chargement-et-pre-traitement-de-donnees-distribue-pour-l-ia#comments">ouvrir dans le navigateur</a>
</p>
nlgrangerhttps://linuxfr.org/nodes/131018/comments.atomtag:linuxfr.org,2005:Bookmark/54062022-11-16T08:40:59+01:002022-11-16T08:40:59+01:00DuckDB: une base de données embarquée pour ceux qui en ont mare de sqlite<a href="https://duckdb.org/">https://duckdb.org/</a> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/129308/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/users/devnewton/liens/duckdb-une-base-de-donnees-embarquee-pour-ceux-qui-en-ont-mare-de-sqlite#comments">ouvrir dans le navigateur</a>
</p>
devnewton 🍺https://linuxfr.org/nodes/129308/comments.atomtag:linuxfr.org,2005:Diary/403532022-08-20T19:38:18+02:002022-08-20T19:38:18+02:00J'ai demandé le multi-database à Neo4JLicence CC By‑SA http://creativecommons.org/licenses/by-sa/4.0/deed.fr<p>Pour ceux qui ne connaissent pas <a href="https://github.com/neo4j/neo4j">Neo4J</a> est un système de base de donnée de type graphe populaire.</p>
<p>Pour un <a href="https://github.com/openfoodfacts/taxonomy-editor/">projet d'éditeur de taxonomies</a>, j'ai proposé de l'utiliser car il semblait pleinement correspondre à notre besoin. Et de fait pour le moment ça marche bien.</p>
<p>Mais comme notre but est d'éditer différents graphes collaborativement (et différentes versions de celui-ci), j'aurais aimé utiliser une base de donnée pour chacun de ces graphes (dans mon cas pour chaque <a href="https://wiki.openfoodfacts.org/Global_taxonomies">taxonomie</a>, dans une déclinaison particulière, c'est à dire un ensemble d'améliorations). </p>
<p>Or au moment où je teste la création d'une nouvelle base de donnée en suivant <a href="https://neo4j.com/docs/cypher-manual/current/databases/">la doc</a> je me rends compte que la création de base de données distincts n'est possible que dans la version payante !</p>
<p>Je trouve ça un peu exagéré (surtout qu'il s'agit essentiellement d'avoir deux dossiers distincts avec les données). Je comprends bien le modèle freemium (édition communautaire vs édition d'entreprise) mais là le curseur me semble quand même un peu bas… si on peut avoir une seule base de donnée, ça ressemble plus à un produit de démo.</p>
<p>Je sais que j'ai la possibilité d'utiliser des labels pour "séparer les choses", mais tout de même ça ne me semble pas très sain (par exemple, là après une itération sur une taxonomie, j'aurais voulu pourvoir jeter la base et en créer une nouvelle, si j'ai plusieurs itérations en parallèle et une seule base, je ne pourrais jamais la "jeter").</p>
<p>Comme c'est un projet libre, je veux que ce soit réplicable par tout un chacun, donc il est pour moi hors de question de dépendre de manière centrale d'une fonction de la version entreprise.</p>
<p>Du coup j'ai ouvert <a href="https://github.com/neo4j/neo4j/issues/12920">une demande d'amélioration</a> en espérant avoir gain de cause ! (qui sait).</p>
<p>J'ai posté ici pour savoir:</p>
<ul>
<li>si certains veulent voter pour :-) et se manifester sur le bug</li>
<li>si certains ont des retours d'expérience sur ça, ou m'expliquent que je n'en ai pas besoin</li>
<li>si d'autres me conseillent de carrément utiliser autre chose (ça m'embêterait beaucoup, le fit étant très bon pour ce qu'on veut faire)</li>
</ul>
<div><a href="https://linuxfr.org/users/alexg/journaux/j-ai-demande-le-multi-database-a-neo4j.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/128544/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/users/alexg/journaux/j-ai-demande-le-multi-database-a-neo4j#comments">ouvrir dans le navigateur</a>
</p>
Alex G.https://linuxfr.org/nodes/128544/comments.atomtag:linuxfr.org,2005:Bookmark/45822022-04-21T11:42:30+02:002022-04-22T07:41:07+02:00YDB : la base de Yandex réussit enfin son passage sous licence libre<a href="https://ydb.tech/">https://ydb.tech/</a> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/127528/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/users/devnewton/liens/ydb-la-base-de-yandex-reussit-enfin-son-passage-sous-licence-libre#comments">ouvrir dans le navigateur</a>
</p>
devnewton 🍺https://linuxfr.org/nodes/127528/comments.atomtag:linuxfr.org,2005:Diary/402362022-04-18T11:40:15+02:002022-04-19T19:18:54+02:00Fusionner deux profils signal-desktop pour retrouver ses conversationsLicence CC By‑SA http://creativecommons.org/licenses/by-sa/4.0/deed.fr<h2 class="sommaire">Sommaire</h2>
<ul class="toc">
<li><a href="#toc-g%C3%A9n%C3%A9ralit%C3%A9s-sur-signal">Généralités sur Signal</a></li>
<li><a href="#toc-expos%C3%A9-de-la-situation">Exposé de la situation</a></li>
<li><a href="#toc-ouvrir-la-base-de-donn%C3%A9es">Ouvrir la base de données</a></li>
<li>
<a href="#toc-effectuer-la-fusion">Effectuer la fusion</a><ul>
<li><a href="#toc-cr%C3%A9er-la-copie-de-travail">Créer la copie de travail</a></li>
<li><a href="#toc-suppression-des-messages-communs">Suppression des messages « communs »</a></li>
<li><a href="#toc-insertion-des-messages">Insertion des messages</a></li>
<li><a href="#toc-import-des-conversations">Import des conversations</a></li>
<li><a href="#toc-les-sessions--je-ne-touche-pas">Les sessions : je ne touche pas</a></li>
<li><a href="#toc-modification-du-profil-signal">Modification du profil Signal</a></li>
<li><a href="#toc-bonus--pourquoi-une-base-de-donn%C3%A9es-chiffr%C3%A9e">Bonus : pourquoi une base de données chiffrée ?</a></li>
</ul>
</li>
<li><a href="#toc-conclusion">Conclusion</a></li>
</ul>
<p>Me revoilà pour un journal un peu technique. On part à l’aventure et on va notamment parler de Signal, de SQL, SQLite, de son extension fts5 (vite fait) et de SQLCipher (variante de SQLite capable de chiffrer les bases de données, découverte à l’occasion), et de fusion de deux bases de données Signal pour reconstruire un historique complet et « réparer » un profil Signal un peu moisi.</p>
<h2 id="toc-généralités-sur-signal">Généralités sur Signal</h2>
<p><a href="https://signal.org/">Signal</a> est une application de messagerie instantanée chiffrée de bout en bout utilisant (pour le moment) le numéro de téléphone comme identifiant. Très peu d’état est stocké sur le serveur. En particulier, le carnet de contact et les groupes dont on fait partie n’y sont pas stockés. Il y a un appareil principal et éventuellement des appareils secondaires. L’appareil principal est en général un téléphone sur Android ou iOS, mais on peut s’en sortir autrement avec <a href="https://github.com/AsamK/signal-cli">signal-cli</a> sur n’importe quel ordinateur ou <a href="https://axolotl.chat/">Axolotl</a> sur un appareil GNU/Linux doté d’une carte SIM (il doit pouvoir tourner sans, mais je n’ai pas essayé) – en général un téléphone. Les appareils secondaires sont en général des ordinateurs classiques faisant tourner <a href="https://github.com/signalapp/Signal-Desktop/">Signal-Desktop</a>, mais ça pourrait être signal-cli ou tout autre client alternatif, comme <a href="https://github.com/derricw/siggo">siggo</a>, <a href="https://github.com/isamert/scli">scli</a> ou <a href="https://github.com/jwoglom/signal-curses">signal-curses</a>.</p>
<p>Chaque appareil a sa propre session Signal. Je ne pense pas que vos contacts puissent voir depuis quel appareil vous avez envoyé un message, mais vous pouvez consulter la liste des sessions enregistrées depuis l’application mobile. Sur Signal-Desktop, vous pouvez voir le nom de l’appareil dans les paramètres mais pas le modifier.</p>
<p>Signal-Desktop est une application Electron écrite en React stockant ses données dans une base de données SQLite chiffrée, et dans des fichiers classiques (comme les pièces jointes / les images dans les messages).</p>
<p>Sous GNU/Linux, Signal-Desktop stocke toutes ses données dans le dossier <code>.config/Signal</code> de votre répertoire personnel. Copier ce dossier sur un autre ordinateur qui a une version identique ou plus récente de Signal-Desktop va marcher, mais ne vous avisez pas à continuer à utiliser l’original parce que ça va mal se passer : les deux Signal-Desktop vont sans arrêt se déconnecter mutuellement. Ne me demandez pas comment je sais. Je suppose que les messages pourraient n’arriver qu’à l’un ou qu’à l’autre, de façon aléatoire. Signal fait la supposition que vous ne faites pas ça.</p>
<p>Les anciens messages ne sont pas renvoyés à un appareil nouvellement appairé, ou à une nouvelle session principale (réinstallation de l’application mobile / changement de téléphone portable sans restaurer les données). Ça permet d’empêcher quelqu’un qui réussirait à détourner (ou qui a hérité de) votre numéro de téléphone d’avoir accès à vos anciens messages. C’est aussi un des deux états de fait qui ont impliqué cette investigation. De même, si vous rejoignez un groupe, vous n’avez pas accès aux messages anciens. Pratique et pas pratique à la fois, selon ce que vous voulez faire (si vous organisez un évènement avec un groupe Signal éphémère, vous devez sans cesse répéter les infos essentielles dès que de nouvelles personnes rejoignent le groupe. Si vous intégrez quelqu’un dans un groupe de longue durée, vous ne voulez probablement pas que cette personne ait accès aux anciens échanges). Ces non-réexpéditions sont des choix fonctionnels qui facilitent également probablement l’implémentation.</p>
<h2 id="toc-exposé-de-la-situation">Exposé de la situation</h2>
<p>J’utilise Signal-Desktop sur deux ordinateurs. Appelons-les ordi A et ordi B. J’ai appairé ordi B un poil plus tard que ordi A, donc il n’a pas les premiers messages sur ordi B. J’ai toujours souhaité avoir tous mes messages sur les deux ordis, mais pas assez pour me pencher sur le problème : en pratique, je n’ai pas réellement besoin de mes messages de l’année dernière sur ordi B, si besoin je peux utiliser ordi A pour les consulter.</p>
<p>Cependant, récemment, pour une raison inconnue, ma session Signal sur ordi A a s’est retrouvée dans un état incohérent. J’ai perdu des messages récents dessus, et j’ai observé des dysfonctionnements variés. Ce weekend, je me suis alors penché sur la question…</p>
<p><strong>Attention :</strong> je décris des étapes pas garanties. Je ne suis pas familier avec le fonctionnement précis de Signal-Desktop. Je vous invite pas à reproduire ces étapes. Elles pourraient détruire vos messages, ou vous donner l’impression qu’elles marchent mais en fait causer des problèmes plus ou moins subtils plus tard, y compris des problèmes de réceptions de messages. L’intérêt de ce journal est pour moi de garder trace de mes étapes, et pour vous de lire une session d’investigations et de peut-être découvrir certaines techniques. Si vous tentez de reproduire ces choses, vous le faites à vos risques et périls en toute connaissance de cause.</p>
<p>Malgré les risques, je me suis quand même lancé dans l’aventure. Je pars du principe que dans le pire des cas :</p>
<ul>
<li>je devrais toujours pouvoir recevoir mes messages à l’aide de l’application mobile (<a href="https://github.com/nanu-c/axolotl">Axolotl</a>), non touchée par ces bidouilles</li>
<li>je garde une copie des fichiers de travail (le dossier de configuration de Signal-Desktop des deux ordinateurs), que je peux toujours consulter avec Signal-Desktop <strong>hors connexion</strong> en les copiant là où il s’attend à les trouver</li>
<li>au pire, on désinstalle tout et on recommence de zéro</li>
<li>dans tous les cas, ma session sur l’ordi A est un peu niquée alors je n’ai pas grand-chose à perdre au final…</li>
</ul>
<h2 id="toc-ouvrir-la-base-de-données">Ouvrir la base de données</h2>
<p>Bref, assez parlé, mettons les mains dans le cambouis. En inspectant le dossier <code>.config/Signal</code>, on tombe rapidement sur le fichier <code>sql/db.sqlite</code>, on se doute vite que c’est une base de données SQLite. il y a aussi les dossiers <code>attachments.noindex</code> et <code>avatars.noindex</code> qui contiennent des données binaires liées aux messages ou au profil tel que les images.</p>
<p>Premier réflexe : ouverture du fichier avec <a href="https://sqlitebrowser.org/">SQLite Browser</a>. Et là on se rend compte tout de suite que ça demande une clé de déchiffrement.</p>
<p><img src="//img.linuxfr.org/img/68747470733a2f2f732e6a616b73652e66722f323032322d30342d31382d73716c69746562726f777365726369706865722e706e67/2022-04-18-sqlitebrowsercipher.png" alt="SQLite Browser demande une clé de déchiffrement" title="Source : https://s.jakse.fr/2022-04-18-sqlitebrowsercipher.png"></p>
<p>Un petit tour sur internet nous informe que la clé à utiliser est stockée dans le fichier <code>.config/Signal/config.json</code> :</p>
<pre><code class="json"><span class="p">{</span>
<span class="nt">"key"</span><span class="p">:</span> <span class="s2">"abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890"</span><span class="p">,</span>
<span class="nt">"mediaPermissions"</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
<span class="nt">"mediaCameraPermissions"</span><span class="p">:</span> <span class="kc">true</span>
<span class="p">}</span></code></pre>
<p>Et on finit par comprendre qu’il faut utiliser l’option « Clé de chiffrement » et la préfixer par <code>0x</code> pour ouvrir la base. En ouvrant la base, on se rend compte que les données messages ne s’affichent pas. L’interface les affiche en rouge et prétend que les valeurs sont en cours de chargement, mais on se rend compte en regardant les logs que l’interface essaie d’utiliser la fonction <code>json_extract</code> et n’y arrive pas. Je n’ai pas creusé, j’ai continué mes investigations avec <a href="https://sqliteman.dev/">SqliteMan</a> qui y arrivait sur une version non chiffrée de la base.</p>
<p><img src="//img.linuxfr.org/img/68747470733a2f2f732e6a616b73652e66722f323032322d30342d31382d73716c69746562726f777365726a736f6e657874726163746572726f722e706e67/2022-04-18-sqlitebrowserjsonextracterror.png" alt="SQLite Browser ne trouve pas la fonction `json_extract`" title="Source : https://s.jakse.fr/2022-04-18-sqlitebrowserjsonextracterror.png"></p>
<p><code>sqlite3</code> n’est pas capable d’ouvrir une base chiffrée, il faudra pour cela utiliser <a href="https://www.zetetic.net/sqlcipher/">SQLCipher</a>, que je découvre pour l’occasion. Après de longues recherches, je comprends qu’il faut préfixer la clé par <code>x</code> pour ouvrir la base de données avec SQLCipher. On peut utiliser SQLCipher pour manipuler les bases de données chiffrées ou leur retirer le chiffrement.</p>
<pre><code>SQLite version 3.37.2 2022-01-06 13:25:41 (SQLCipher 4.5.1 community)
Enter ".help" for usage hints.
$ cp ~/.config/Signal/sql/db.sqlite ordiA.sqlite
$ sqlcipher ordiB.sqlite
sqlite> pragma key = "x'abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890'";
ok
sqlite> select count(*) from messages;
11000
</code></pre>
<p>Si la clé n’est pas bonne, vous obtiendrez le message suivant au moment de faire des requêtes SQL :</p>
<pre><code>Error: in prepare, file is not a database (26)
</code></pre>
<p>Vous pouvez également vous attacher à une base chiffrée de cette manière :</p>
<pre><code>sqlite> attach database 'ordiB.sqlite' as ordiB key = "x'9876543210fedcba0987654321fedcba0987654321fedcba0987654321fedcba'";
sqlite> select count(*) from ordiB.messages;
</code></pre>
<p>Pour ouvrir ou vous attacher à des bases de données non chiffrées, simplement laisser <code>key</code> vide :</p>
<pre><code>pragma key = "";
…
sqlite> attach database 'ordiB.sqlite' as ordiB key = "";
</code></pre>
<p>On peut changer la clé de chiffrement de la base de données ouverte en utilisant <code>pragma rekey = "XXX";</code> après <code>pragma key</code>, mais on en a pas besoin. On peut sauvegarder une version non chiffrée de cette manière :</p>
<pre><code>$ cp ~/.config/Signal/sql/db.sqlite ordiA.sqlite
SQLite version 3.37.2 2022-01-06 13:25:41 (SQLCipher 4.5.1 community)
Enter ".help" for usage hints.
sqlite> pragma key = "x'abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890'";
ok
sqlite> attach database 'ordiA.nonchiffre.sqlite' as plaintext key = '';
sqlite> select sqlcipher_export('plaintext');
</code></pre>
<p>(ou en utilisant <code>pragma rekey = ''</code> mais je ne me souviens plus d’avoir essayé). Voilà, vous savez utiliser SQLCipher aussi bien que moi).</p>
<p>La base de donnée de Signal a pas mal de tables. Après inspection, celles qui nous intéressent plus particulièrement sont :</p>
<ul>
<li>
<code>messages</code> : elle contient tous les messages reçus (et envoyés ?), au sens large (changements de statut, mises à jour de groupes, accusés de réceptions / de lecture, etc). Dans le protocole Signal, tout changement d’état passe par des messages et tout semble stocké dans cette table, vous pouvez probablement rejouer précisément toute l’histoire avec cette table (à vérifier, c’est juste une intuition). Je pars du principe que le serveur Signal fait une diffusion basique des messages à chaque session enregistrée et que toutes ces sessions reçoivent exactement les mêmes messages, ou en tout cas des messages équivalents. Avec possiblement quelques messages spécifiques pour chaque session. Je n’ai pas vérifié cette hypothèse, et d’ailleurs j’ai pu observer des petites différences d’ordre dans les messages entre la session de l’ordi A et de l’ordi B, alors elle n’est pas forcément très vraie mais partons là-dessus quand même, c’est probablement une approximation acceptable. Mais du coup, pour la fusion, il ne suffit possiblement pas de se contenter de récupérer les messages d’ordi A pas présent dans ordi B et de les insérer au bon endroit (peut-être que ça marche, mais je n’ai pas essayé).</li>
<li>
<code>conversations</code> : elle contient vos conversations privées ou de groupe.</li>
<li>
<code>reactions</code> : depuis la <a href="https://github.com/signalapp/Signal-Desktop/blob/b066d04817a0f2a8cbbac8714aa574a380451047/ts/sql/migrations/index.ts#L1483">révision 29</a> de la base de données, elle contient les réactions aux messages, et la <a href="https://github.com/signalapp/Signal-Desktop/blob/b066d04817a0f2a8cbbac8714aa574a380451047/ts/sql/migrations/index.ts#L1781">révision 37</a> ajoute la référence au message de cette réaction (les réactions, sur Signal, sont des messages comme les autres, comme on peut d’ailleurs le constater dans Axolotl, qui ne gère pas encore les réactions).</li>
<li>
<code>sessions</code> contient les données de sessions avec les correspondants, par conversation. Je n’ai pas trop creusé son fonctionnement.</li>
</ul>
<h2 id="toc-effectuer-la-fusion">Effectuer la fusion</h2>
<p>Avant tout, fermez Signal sur ordi A et sur ordi B. Vous ne voulez pas recevoir de messages pendant la manipulation pour éviter d’interférer ou créer des incohérences.</p>
<p>Pour fusionner les deux profils pour avoir un profil complet sur l’ordi A, on s’assure d’avoir ouvert les deux bases de données (ordi A et ordi B) avec la même dernière version de signal-desktop. Signal-desktop fait des migrations et si les bases de données ne sont pas à la même version, ça va être la merde. Ensuite, voici l’idée générale :</p>
<h3 id="toc-créer-la-copie-de-travail">Créer la copie de travail</h3>
<p>On fait une copie de la base de données d’ordi A, vers <code>fusion.sqlite</code> par exemple, et on ouvre cette copie avec <code>sqlcipher</code>.</p>
<h3 id="toc-suppression-des-messages-communs">Suppression des messages « communs »</h3>
<p>On repère l’identifiant du premier message reçu par ordi B dans ordi A avec SqliteMan et on le supprime ainsi que tous les suivants:</p>
<pre><code class="sql"><span class="k">delete</span> <span class="k">from</span> <span class="n">messages</span> <span class="k">where</span> <span class="n">rowid</span> <span class="o">></span> <span class="mi">263</span><span class="p">;</span></code></pre>
<p>(même chose pour la table <code>reactions</code>, l’étape 2 aussi)</p>
<h3 id="toc-insertion-des-messages">Insertion des messages</h3>
<p>On insère les messages de l’autre base :</p>
<pre><code class="sql"><span class="k">insert</span> <span class="k">into</span> <span class="n">messages</span> <span class="k">select</span> <span class="o">*</span> <span class="k">from</span> <span class="n">ordiB</span><span class="p">.</span><span class="n">messages</span><span class="p">;</span></code></pre>
<p>En réalité, ça ne marche pas. Il a fallu indiquer les champs /explicitement. Pour avoir la liste des champs :</p>
<pre><code class="sql"><span class="n">pragma</span> <span class="n">table_info</span><span class="p">(</span><span class="n">messages</span><span class="p">)</span></code></pre>
<pre><code>0|rowid|INTEGER|0||1
1|id|STRING|0||0
2|json|TEXT|0||0
3|readStatus|INTEGER|0||0
4|expires_at|INTEGER|0||0
5|sent_at|INTEGER|0||0
6|schemaVersion|INTEGER|0||0
7|conversationId|STRING|0||0
8|received_at|INTEGER|0||0
9|source|STRING|0||0
10|deprecatedSourceDevice|STRING|0||0
11|hasAttachments|INTEGER|0||0
12|hasFileAttachments|INTEGER|0||0
13|hasVisualMediaAttachments|INTEGER|0||0
14|expireTimer|INTEGER|0||0
15|expirationStartTimestamp|INTEGER|0||0
16|type|STRING|0||0
17|body|TEXT|0||0
18|messageTimer|INTEGER|0||0
19|messageTimerStart|INTEGER|0||0
20|messageTimerExpiresAt|INTEGER|0||0
21|isErased|INTEGER|0||0
22|isViewOnce|INTEGER|0||0
23|sourceUuid|TEXT|0||0
24|serverGuid|STRING|0||0
25|sourceDevice|INTEGER|0||0
26|storyId|STRING|0||0
27|isChangeCreatedByUs|INTEGER|1|0|0
</code></pre>
<p>En collant dans Kate, sélection multiple sur les 10 premières lignes, espace pour aligner tous les numéros à droite, sélection multiple sur la première colonne pour la supprimer, remplacement expression régulière <code>\|.+</code> par <code>,</code> CTRL+A pour tout sélectionner, CTRL+J pour joindre toutes les lignes, on obtient une liste de champs séparés par des virgules. Requête SQL :</p>
<pre><code class="sql"><span class="k">insert</span> <span class="k">into</span> <span class="n">messages</span><span class="p">(</span><span class="n">id</span><span class="p">,</span> <span class="n">json</span><span class="p">,</span> <span class="n">readStatus</span><span class="p">,</span> <span class="n">expires_at</span><span class="p">,</span> <span class="n">sent_at</span><span class="p">,</span> <span class="n">schemaVersion</span><span class="p">,</span> <span class="n">conversationId</span><span class="p">,</span> <span class="n">received_at</span><span class="p">,</span> <span class="k">source</span><span class="p">,</span> <span class="n">deprecatedSourceDevice</span><span class="p">,</span> <span class="n">hasAttachments</span><span class="p">,</span> <span class="n">hasFileAttachments</span><span class="p">,</span> <span class="n">hasVisualMediaAttachments</span><span class="p">,</span> <span class="n">expireTimer</span><span class="p">,</span> <span class="n">expirationStartTimestamp</span><span class="p">,</span> <span class="k">type</span><span class="p">,</span> <span class="n">body</span><span class="p">,</span> <span class="n">messageTimer</span><span class="p">,</span> <span class="n">messageTimerStart</span><span class="p">,</span> <span class="n">messageTimerExpiresAt</span><span class="p">,</span> <span class="n">isErased</span><span class="p">,</span> <span class="n">isViewOnce</span><span class="p">,</span> <span class="n">sourceUuid</span><span class="p">,</span> <span class="n">serverGuid</span><span class="p">,</span> <span class="n">sourceDevice</span><span class="p">,</span> <span class="n">storyId</span><span class="p">,</span> <span class="n">isChangeCreatedByUs</span><span class="p">)</span> <span class="k">select</span> <span class="n">id</span><span class="p">,</span> <span class="n">json</span><span class="p">,</span> <span class="n">readStatus</span><span class="p">,</span> <span class="n">expires_at</span><span class="p">,</span> <span class="n">sent_at</span><span class="p">,</span> <span class="n">schemaVersion</span><span class="p">,</span> <span class="n">conversationId</span><span class="p">,</span> <span class="n">received_at</span><span class="p">,</span> <span class="k">source</span><span class="p">,</span> <span class="n">deprecatedSourceDevice</span><span class="p">,</span> <span class="n">hasAttachments</span><span class="p">,</span> <span class="n">hasFileAttachments</span><span class="p">,</span> <span class="n">hasVisualMediaAttachments</span><span class="p">,</span> <span class="n">expireTimer</span><span class="p">,</span> <span class="n">expirationStartTimestamp</span><span class="p">,</span> <span class="k">type</span><span class="p">,</span> <span class="n">body</span><span class="p">,</span> <span class="n">messageTimer</span><span class="p">,</span> <span class="n">messageTimerStart</span><span class="p">,</span> <span class="n">messageTimerExpiresAt</span><span class="p">,</span> <span class="n">isErased</span><span class="p">,</span> <span class="n">isViewOnce</span><span class="p">,</span> <span class="n">sourceUuid</span><span class="p">,</span> <span class="n">serverGuid</span><span class="p">,</span> <span class="n">sourceDevice</span><span class="p">,</span> <span class="n">storyId</span><span class="p">,</span> <span class="n">isChangeCreatedByUs</span> <span class="k">from</span> <span class="n">ordiB</span><span class="p">.</span><span class="n">messages</span><span class="p">;</span></code></pre>
<p>Mais avant ça, il faut traiter la table <code>messages_fts</code> et ses copines <code>messages_fts_config</code>, <code>messages_fts_content</code>, <code>messages_fts_data</code>, <code>messages_fts_docsize</code>, <code>messages_fts_idx</code>. Ces tables contiennent le contenu des messages, et il faut sûrement faire attention à maintenir la cohérence. Bon, spoiler (divulgâcheur) : il s’agit d’une table virtuelle <code>fts5</code> créée à l’aide de <a href="https://www.sqlite.org/fts5.html">l’extension SQLite <code>fts5</code></a>, qui fournit de la recherche complète de texte (<em>full text search</em>).</p>
<p>Ces tables <a href="https://github.com/signalapp/Signal-Desktop/blob/b066d04817a0f2a8cbbac8714aa574a380451047/ts/sql/migrations/index.ts#L333">sont créées</a> avec :</p>
<pre><code class="sql"><span class="k">create</span> <span class="n">virtual</span> <span class="k">table</span> <span class="n">messages_fts</span> <span class="k">using</span> <span class="n">fts5</span><span class="p">(</span><span class="n">id</span> <span class="n">unindexed</span><span class="p">,</span> <span class="n">body</span><span class="p">);</span></code></pre>
<p>Avant d’insérer les nouveaux messages, on supprime cette table virtuelle :</p>
<pre><code class="sql"><span class="k">drop</span> <span class="k">table</span> <span class="n">messages_fts</span><span class="p">;</span></code></pre>
<p>Et après <a href="https://github.com/signalapp/Signal-Desktop/blob/b066d04817a0f2a8cbbac8714aa574a380451047/ts/sql/migrations/index.ts#L341">l’insertion</a> :</p>
<pre><code class="sql"><span class="k">insert</span> <span class="k">into</span> <span class="n">messages_fts</span><span class="p">(</span><span class="n">id</span><span class="p">,</span> <span class="n">body</span><span class="p">)</span> <span class="k">select</span> <span class="n">id</span><span class="p">,</span> <span class="n">body</span> <span class="k">from</span> <span class="n">messages_fts</span><span class="p">;</span></code></pre>
<p>Petite digression : malheureusement, la version de SQLCipher dans les dépôts d’openSUSE n’est pas compilée avec l’extension fts5. J’ai donc été amené à compiler SQLCipher moi-même. Ça se fait bien, la bibliothèque n’a pas de dépendances qui ne seraient pas déjà présentes dans un système GNU/Linux standard :</p>
<pre><code class="sh">./configure --enable-fts5 --enable-tempstore<span class="o">=</span>yes <span class="nv">CFLAGS</span><span class="o">=</span><span class="s2">"-DSQLITE_HAS_CODEC"</span> <span class="nv">LDFLAGS</span><span class="o">=</span><span class="s2">"-lcrypto"</span>
make
sudo make install
rehash <span class="c1"># pour les utilisateurs de zsh</span></code></pre>
<h3 id="toc-import-des-conversations">Import des conversations</h3>
<p>Il faut importer les <code>conversations</code> qui sont dans ordiB et pas dans ordiA. Chez moi, c’est juste une conversation. Mais plus gênant, les identifiants de conversations (dans le champ <code>id</code>), qui sont des UUID, ne sont pas les mêmes dans les deux bases de données. On peut créer une table des correspondances. Pour les conversations privées, le numéro de la personne est dans le champ <a href="https://en.wikipedia.org/wiki/E.164"><code>e164</code></a>, qui est nul dans le cas des groupes. Pour les groupes, l’identifiant du groupe est dans le champ <code>groupId</code>, qui est nul dans le cas des discussions privées. On pourra faire la correspondance de cette manière :</p>
<pre><code class="sql"><span class="k">select</span>
<span class="n">cA</span><span class="p">.</span><span class="n">id</span> <span class="k">as</span> <span class="n">conversationIdA</span><span class="p">,</span>
<span class="n">cB</span><span class="p">.</span><span class="n">id</span> <span class="k">as</span> <span class="n">conversationIdB</span>
<span class="k">from</span> <span class="n">conversations</span> <span class="n">cA</span> <span class="k">inner</span> <span class="k">join</span> <span class="n">ordiB</span><span class="p">.</span><span class="n">conversations</span> <span class="n">cB</span> <span class="k">on</span> <span class="p">(</span>
<span class="n">cA</span><span class="p">.</span><span class="n">e164</span> <span class="o">=</span> <span class="n">cB</span><span class="p">.</span><span class="n">e164</span> <span class="k">or</span>
<span class="n">cA</span><span class="p">.</span><span class="n">groupId</span> <span class="o">=</span> <span class="n">cB</span><span class="p">.</span><span class="n">groupId</span>
<span class="p">);</span></code></pre>
<p>Ça se passe bien avec les valeurs nulles, qui sont ignorés par les tests d’égalité dans la condition de jointure.</p>
<p>Avec un peu d’édition de texte, pour chaque <code>conversationIdA</code>, on exécute la ligne suivante :</p>
<pre><code class="sql"><span class="k">delete</span> <span class="k">from</span> <span class="n">conversations</span> <span class="k">where</span> <span class="n">id</span> <span class="o">=</span> <span class="s1">'conversationIdA'</span><span class="p">;</span></code></pre>
<p>Ensuite, on peut importer toutes les conversations :</p>
<pre><code class="sql"><span class="k">insert</span> <span class="k">into</span> <span class="n">conversations</span> <span class="k">select</span> <span class="o">*</span> <span class="k">from</span> <span class="n">ordiB</span><span class="p">.</span><span class="n">conversations</span><span class="p">;</span></code></pre>
<p>Ensuite, il faut remplacer les identifiants de conversations des tables <code>messages</code>, <code>reactions</code> et <code>sessions</code>. À l’aide de la requête de correspondance, à coup d’édition multi ligne dans Kate, on peut produire des requêtes du type :</p>
<pre><code class="sql"><span class="k">update</span> <span class="n">messages</span> <span class="k">set</span> <span class="n">conversationId</span><span class="o">=</span><span class="s1">'conversationIdB'</span> <span class="k">where</span> <span class="n">conversationId</span><span class="o">=</span><span class="s1">'conversationIdA'</span>
<span class="k">update</span> <span class="n">reactions</span> <span class="k">set</span> <span class="n">conversationId</span><span class="o">=</span><span class="s1">'conversationIdB'</span> <span class="k">where</span> <span class="n">conversationId</span><span class="o">=</span><span class="s1">'conversationIdA'</span>
<span class="k">update</span> <span class="n">sessions</span> <span class="k">set</span> <span class="n">conversationId</span><span class="o">=</span><span class="s1">'conversationIdB'</span> <span class="k">where</span> <span class="n">conversationId</span><span class="o">=</span><span class="s1">'conversationIdA'</span></code></pre>
<h3 id="toc-les-sessions--je-ne-touche-pas">Les sessions : je ne touche pas</h3>
<p>Pour la table <code>sessions</code>, je n’ai pas trop cherché ce qu’il faudrait faire. Il faut peut-être éventuellement importer les sessions des conversations qui existaient dans ordiB et pas dans ordiA mais plutôt laisser les autres telles quelles.</p>
<h3 id="toc-modification-du-profil-signal">Modification du profil Signal</h3>
<p>On n’a plus qu’à faire une sauvegarde du profil Signal, à remplacer la base de données et à importer les attachements :</p>
<pre><code class="sh">cp -r ~/.config/Signal<span class="o">{</span>,.bak<span class="o">}</span>
cp fusion.sqlite ~/.config/Signal/sql/db.sqlite
rsync -avp configOrdiB/Signal/attachements.noindex/ ~/.config/Signal/attachements.noindex/
rsync -avp configOrdiB/Signal/avatars.noindex/ ~/.config/Signal/avatars.noindex/</code></pre>
<p>On teste les modifications en coupant votre connexion internet et en lançant signal-desktop, hors ligne donc. Comme ça, on peut voir si on obtient les résultats attendus sans risquer de tout pourrir en recevant des messages dans un état incohérent et se compliquer la vie, voire jamais pouvoir récupérer correctement ces messages.</p>
<h3 id="toc-bonus--pourquoi-une-base-de-données-chiffrée">Bonus : pourquoi une base de données chiffrée ?</h3>
<p>On peut se demander pourquoi la base de données Signal est chiffrée alors que la clé de déchiffrement est stockée en clair juste à côté.</p>
<p>Evan Hahn, un développeur de Signal, <a href="https://github.com/signalapp/Signal-Desktop/issues/5245#issuecomment-843234191">affirme</a> :</p>
<blockquote>
<p>We use SQLCipher for two reasons:</p>
<ol>
<li>To encrypt the data at rest. Some users don't have full disk encryption, and this can (theoretically) help them.</li>
<li>SQLCipher, unlike "vanilla" SQLite, deletes data from disk. If, for example, a message expires, we can ensure it's actually deleted instead of marking it deleted but not actually deleting it.</li>
</ol>
</blockquote>
<p>Je ne suis pas super convaincu par la première raison (ça aiderait les gens qui n’ont pas de chiffrage sur leur ordinateur). À la limite, ça peut peut-être aider pour contrer les outils d’indexation un peu zélés qui iraient indexer le contenu des bases de données. La deuxième raison me parait plus convaincante : SQLCipher fournirait plus de garanties lors de la suppression de données. La position actuelle de l’équipe de développement semble être d’inviter les gens inquiets à utiliser du chiffrement au niveau de la partition et d’utiliser l’écran de verrouillage. Des gens ne sont pas satisfaits par cette réponse parce que les données seraient accessibles à un processus malicieux même en cas de chiffrement de la partition quand le système est démarré. M’est d’avis qu’il faudrait probablement que Signal demande un mot de passe au démarrage pour résoudre de problème ou que les systèmes d’exploitations fournissent des systèmes d’isolation un peu poussés entre applications, et que l’équipe de développement de Signal ne veut pas se risquer à implémenter des mécanismes de sécurité un peu hasardeux et elle a de toute façon d’autres priorités.</p>
<h2 id="toc-conclusion">Conclusion</h2>
<p>Avec beaucoup de précautions, en s’y reprenant à plusieurs reprises, avec des recherches internet, un peu d’intuition et de lecture du code de Signal (en particulier le code des migrations SQL), j’ai fini par réussir la fusion. En tout cas, à première vue (seul le futur permettra de dire si effectivement ça s’est vraiment bien passé). J’ai retrouvé toutes mes conversations, y compris les premiers messages et les messages qui manquaient sur l’ordi A sur l’ordi A, et la conversation qui n’était jamais apparue sur l’ordi A pour une raison inconnue. J’ai manifestement pu envoyer et recevoir des messages. Et ce, sans changement de clé de session ou sans avoir à enregistrer un nouvel appareil avec l’application mobile.</p>
<p>Encore une fois, je ne recommande pas de se lancer dans cette bidouille : c’est un peu risqué et ça prend du temps peut-être mieux dépenser ailleurs. Malgré tout, j’ai appris des choses et je suis content de partager l’aventure, et si ça peut servir à quelqu’un aussi ou plus fou que moi avec la conservation des historiques, ou pour quelqu’un qui s’intéresserait à SQLCipher ou au fonctionnement de Signal, ou même à une manière partir en exploration dans ce genre de situation, tant mieux.</p>
<p>Certaines étapes auraient pu être « rendues propres » par exemple en générant des lignes SQL avec des outils Unix ou des scripts quelconques, mais j’ai justement pris le parti de ne pas le faire. Ce n’est pas comme ça que j’ai procédé, j’y suis plutôt allé à la bourrin avec un éditeur de texte et j’ai dû coup décidé de détailler un peu cette partie. C’est justement un endroit où je me suis demandé comment être efficace, comment faire une boucle en SQLite, ouh là là il faut faire des triggers récursifs, « allez viens en vrai on va être super efficace avec l’éditeur de texte ». C’est pour moi une partie importante de comment je suis effectivement efficace quand je suis en mode résolution de problèmes, donc autant le partager comme ça. Les scripts seraient à écrire s’il y avait à faire cette manipulation souvent, mais quand ce n’est pas le cas, ce n’est pas toujours la méthode la plus rapide et des méthodes plus artisanales / intuitives conviennent parfois mieux et c’est important de le noter et d’avoir ça dans sa boite à outils. Pour exécuter des scripts, il aurait de toute façon fallu que je colle les données dans des fichiers texte, et donc j’aurais probablement utilisé un éditeur de texte, et du coup à ce stade autant utiliser les outils fournis par celui-ci. Faire les choses manuellement une fois peut de toute façon rendre l’écriture de scripts plus rapide : on sait que le résultat fonctionne, et on peut en avoir une meilleure intuition.</p>
<div><a href="https://linuxfr.org/users/raphj/journaux/fusionner-deux-profils-signal-desktop-pour-retrouver-ses-conversations.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/127503/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/users/raphj/journaux/fusionner-deux-profils-signal-desktop-pour-retrouver-ses-conversations#comments">ouvrir dans le navigateur</a>
</p>
raphjhttps://linuxfr.org/nodes/127503/comments.atomtag:linuxfr.org,2005:News/407632021-11-28T10:14:30+01:002021-11-28T11:50:12+01:00Metabase - Business intelligence open sourceLicence CC By‑SA http://creativecommons.org/licenses/by-sa/4.0/deed.fr<div><p>Je forme de longue date des utilisateurs à Dolibarr et j’ai été amené à côtoyer toutes sortes d’entreprises, pris conscience de leurs besoins et mode de fonctionnement. Quand Philippe Scoffoni d’Open-DSI, m’a présenté Metabase, un outil d’analyse de données, j’ai tout de suite été emballé, c’était clairement l’outil idéal pour tout un tas de structures.</p>
<p>C’est aussi ce qui m’a poussé ensuite à écrire un <a href="https://www.d-booker.fr/metabase/699-business-intelligence-open-source-avec-metabase.html">livre</a> et maintenant à vous présenter l’outil. Bien sûr, il a aussi ses limites, mais sa simplicité de mise en place au regard de tout ce qu’il permet de faire mérite le détour. Et surtout ce serait dommage de ne pas en profiter.</p>
</div><ul><li>lien nᵒ 1 : <a title="https://www.metabase.com" hreflang="en" href="https://linuxfr.org/redirect/109450">Site de Metabase</a></li><li>lien nᵒ 2 : <a title="https://www.d-booker.fr/content/category/22-metabase-un-outil-leger-pour-interroger-vos-donnees" hreflang="fr" href="https://linuxfr.org/redirect/109451">Dossier sur d-booker.fr</a></li><li>lien nᵒ 3 : <a title="https://www.d-booker.fr/metabase/699-business-intelligence-open-source-avec-metabase.html" hreflang="fr" href="https://linuxfr.org/redirect/109452">Et pour la prise en main utilisateur, mon livre :-)</a></li></ul><div><h2 id="toc-metabase-de-la-bi1-open-source">Metabase, de la BI<sup id="fnref1"><a href="#fn1">1</a></sup> open-source ?</h2>
<p>Initialement, Metabase a été développé par un incubateur américain pour suivre les performances des start-ups qu’il finançait. Aujourd’hui, l’application est distribuée sous deux licences : une licence libre (AGPL) et une licence non-libre (<em>Metabase Commercial License</em>).</p>
<p>La version open-source est téléchargeable sur GitHub ou sur le site de Metabase sous différents paquets (docker ou archive jar) et le développement est assuré par l’équipe de Metabase, mais les contributions sur GitHub sont les bienvenues !</p>
<h2 id="toc-le-principe">Le principe</h2>
<p>Le fonctionnement est simple : on connecte Metabase à une base de données et on en contrôle le modèle. On peut alors interroger les données via un assistant, sans connaissance du langage SQL. On peut enfin choisir les options d’affichage (histogramme, tableau, courbes…) et enregistrer ces interrogations pour en afficher les résultats à la demande. Les interrogations de données peuvent faire l’objet d’un partage public ou privé, être téléchargées ou ajoutées à des tableaux de bord.</p>
<h2 id="toc-installation-et-bases-de-données-supportées">Installation et bases de données supportées</h2>
<p>Metabase fournit des packages .jar ou docker.</p>
<p>Les bases de données dont Metabase sait lire les données sont nombreuses :</p>
<ul>
<li>BigQuery</li>
<li>Druid</li>
<li>Google Analytics</li>
<li>H2</li>
<li>MongoDB</li>
<li>MySQL</li>
<li>Oracle</li>
<li>PostgreSQL</li>
<li>Presto</li>
<li>Redshift</li>
<li>Snowflake</li>
<li>SparkSQL</li>
<li>SQL Server</li>
<li>SQLite</li>
<li>Vertica</li>
</ul>
<h2 id="toc-préparation-des-données">Préparation des données</h2>
<p>Une fois l’installation et la connexion à une base de données réalisées, on prépare ses données en indiquant les tables et leurs colonnes qu’on souhaite pouvoir consulter et on contrôle/ajuste le modèle de données. C’est une opération à ne faire qu’une fois, mais importante : il s’agit de vérifier le type des données contenues dans les champs à interroger (date, quantité, montant, etc.), que les clés primaires sont bien définies, tout comme la cible des clés étrangères.</p>
<p><a href="//linuxfr.org/images/historique/images_perdues/metabase-business-intelligence-open-source-mode_donnees.png"><img src="https://linuxfr.org/images/historique/images_perdues/metabase-business-intelligence-open-source-mode_donnees-small.png" alt="Modèle de données"></a> (<a href="https://www.d-booker.net/img/cms/metabase_mode_donnees.png">source</a>)</p>
<h2 id="toc-interrogation-de-ses-données">Interrogation de ses données</h2>
<p>Metabase est un requêteur SQL dont l’assistant ergonomique et intuitif permet de se passer de la connaissance du langage SQL. <br>
Pour des interrogations complexes, on peut créer des vues sur la base de données et les interroger. Pour les connaisseurs du SQL, Metabase inclut un éditeur dans lequel saisir les requêtes SQL.</p>
<p><a href="//linuxfr.org/images/historique/images_perdues/metabase-business-intelligence-open-source-interrogation.png"><img src="https://linuxfr.org/images/historique/images_perdues/metabase-business-intelligence-open-source-interrogation-small.png" alt="Interrogation de données"></a> (<a href="https://www.d-booker.net/img/cms/metabase_interrogation.png">source</a>)</p>
<p>Une fois les résultats renvoyés, on leur applique la représentation graphique la plus adaptée parmi les seize disponibles.</p>
<p><a href="//linuxfr.org/images/historique/images_perdues/metabase-business-intelligence-open-source-graphiques_1.png"><img src="https://linuxfr.org/images/historique/images_perdues/metabase-business-intelligence-open-source-graphiques_1-small.png" alt="Modèle de données"></a> (<a href="https://www.d-booker.net/img/cms/metabase_graphiques_1.png">source</a>)</p>
<p>Chacune dispose de ses propres paramétrages et personnalisations d’affichage et on peut les enregistrer pour les consulter à tout moment, les résultats étant mis à jour au moment de leur affichage.</p>
<p><a href="//linuxfr.org/images/historique/images_perdues/metabase-business-intelligence-open-source-options.png"><img src="https://linuxfr.org/images/historique/images_perdues/metabase-business-intelligence-open-source-options-small.png" alt="Modèle de données"></a> (<a href="https://www.d-booker.net/img/cms/metabase_options.png">source</a>)</p>
<p>On peut regrouper les interrogations de données dans des tableaux de bord thématiques sur lesquels ajouter divers filtres et options.</p>
<p><a href="//linuxfr.org/images/historique/images_perdues/metabase-business-intelligence-open-source-tableau_de_bord.jpg"><img src="https://linuxfr.org/images/historique/images_perdues/metabase-business-intelligence-open-source-tableau_de_bord-small.jpg" alt="tableau de bord"></a> (<a href="https://www.d-booker.net/img/cms/metabase_tableau_de_bord.jpg">source</a>)</p>
<h2 id="toc-perspectives">Perspectives</h2>
<p><strong>Pour une entreprise</strong>, la mise en place de Metabase est à la portée de n’importe quel prestataire.</p>
<p><strong>Pour un prestataire informatique</strong> : il peut y voir la possibilité d’élargir simplement son offre en intégrant des outils de reporting très aussi simples d’utilisation.</p>
<div class="footnotes">
<hr>
<ol>
<li id="fn1">
<p><abbr title="Note des modérateurs">N. D. M. :</abbr> <em>BI ou business intelligence</em>, le terme anglais pour <a href="https://fr.wikipedia.org/wiki/Informatique_d%C3%A9cisionnelle">informatique décisionnelle</a> <a href="#fnref1">↩</a></p>
</li>
</ol>
</div>
</div><div><a href="https://linuxfr.org/news/metabase-business-intelligence-open-source.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/126023/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/news/metabase-business-intelligence-open-source#comments">ouvrir dans le navigateur</a>
</p>
RomainDeschampsBenoît SibaudYsabeau 🧶 🧦palm123BAudhttps://linuxfr.org/nodes/126023/comments.atom