Cartographie des PAIR

Attention : Ce PAD présente certaines technologies qui fonctionnent dans l'état, mais qui ne seront pas maintenues par l'assemblée virtuelle (AV). L'AV migre peu à peu vers les nouveaux protocoles du W3C comme SOLID (intégrant LDP).

Intro

 
Qui ?
Ce PAD est à destination de tous les développeurs gravitant autour de l'Assemblée Virtuelle
 
Quoi ?
C'est un tutoriel permettant d'installer un environnement de développement sur le projet Carto PAIR de l'Assemblée Virtuelle à partir des sous-projets suivants : 
  • l'ontologie de l'AV ou d'autres ontologies du web
  • Une base de donnée sémantique (serveur de triplets) au choix (Semantic Forms ou Rwwplay!)
  • L'interface FluigraphClient, contenant FluidGraph et LDP Framework
 
Comment ?
Nous allons détailler tous les sous-projets, expliquer comment installer un environnement de développement contenant tous les sous-projets, et "jouer" à enregistrer des triplets et à les récupérer, ce sera déjà pas mal ! :)
 
 
Voyons les détails de chaque modules/sous-projets
 

Explications des sous-projets

Ontologie de l'AV et autres ontologies du web
Nous nous baserons sur des ontologies spécifiques pour définir les données que l'on va utiliser. L'AV a déjà travaillé sur des ontologies spécifiques à la méthodo PAIR.
Nous utilisons par ailleurs webprotege, un outil de génération d'ontologies créé par Standford :
 
FluidgraphClient
Client HTML5/JS permettant aux utilisateurs de créer ou d'afficher un graph. Pour cela, il s'appuie sur la librairie FluidGraph.
 
FluidGraph
Librairie Javascript contenant un objet composé d'une quarantaine de fonctions permettant à une librairie cliente (fluidgraphclient) de créer ou d'afficher un graph, et de le sauvegarder soit en local soit sur un serveur LDP distant.
 
LDP Framework
Librairie Javascript permettant à un développeur JS d'enregistrer ou de requêter des triplets sur un serveur LDP. Elle est utilisée par la librairie FluidGraph.
 
Rwwplay!
Serveur LDP développé par Henry Story en scala, et stockant les triplets sous forme de fichiers.
Voir aussi +Installation du serveur LDP AV 
 
Semantic Forms
Serveur SPARQL scala + interface LDP + interface HTML formulaire
 

Références pour aller plus loin : 

Autres implémentations de LDP :
 
Discussion correspondante dans basecamp : 
 
Spécifications LDP :
 
Introduction au web sémantique (Linked Data)
 
Tutoriel sur le Web distribué avec LDP
 

Monter un environnement de test

 
 
Prérequis : 
 - Avoir installé Git (http://doc.ubuntu-fr.org/git)
 - Avoir un environnement Apache fonctionnel
 - Avoir un SDK JAVA 1.8 min
 
 
GO !
Dans un répertoire de votre choix, lancer :
  • cd fluidgraphclient
  • git submodule foreach git pull origin master
  • Pour installer le serveur LDP Rwwplay, suivez le tuto :
 
Attention : vous devez choisir de lancer soit Rwwplay!, soit Semantic Forms (ils sont basés tous les deux sur play! et donc utilisent le même port 9000).
 
Lancer (en local) le serveur Semantic Forms :
cd ~/projets/semantic_forms/scala/forms_play
~/opt/activator-dist-1.3.5/activator
... l'activator télécharge tout ce qu'il faut pour ensuite pouvoir faire :
> run
 
Pour l'arrêter, CTRL+D ou CTRL+C
 
Lancer (en local) le serveur Rwwplay!
cd ~/projets/rwweb-0.7.2-SNAPSHOT
bin/rwweb -Dhttps.port=8443 -Dhttps.trustStore=noCA -Drww.root.container.path=/home/yannick/projets/ldpwww
 
 
Pour l'arrêter : CTRL+C
 
Vérification de bon fonctionnement
Pour vérifier que ça fonctionne bien, aller dans votre navigateur à l'url : 
 
(possibilité d'y aller en http://localhost:9000)
 
La première fois, vous devez autoriser la connexion https, en spécifiant l'exception.
 
Paramètre avancés > Continuer vers le site.
 
Pour obtenir une page sur laquelle est écrit "stample".
 
 
Bravo ! vous avez un serveur LDP qui fonctionne !
 
Pour lancer FluidGraph :
Une fois cloné, allez dans le répertoire : fluidgraphclient
  • Soit vous lancez index.html
  • Soit vous configurez votre Apache pour qu'il pointe vers ce répertoire.
  • Si vous souhaitez comprendre comment fonctionne l'application, allez dans le menu en haut à droite et lisez l'aide.
 
 
 

POST d'un triplet dans Rwwplay!

 
Nous allons d'abord créer un container "cartopair"
  • Si le serveur LDP tourne, l'arrêter.
  • En attendant d'arriver à utiliser la fonction "createContainer" du LDP framework
  • Aller dans votre répertoire ldpwww
  • Il doit y avoir un répertoire todos, faites une copie de ce répertoire en "cartopair"
cp -r todos cartopair
  • Lancer Rwwplay... (voir commandes plus haut...)
 
Nous allons travailler avec un triplet tout simple, voir : +Exercices de smantisation de linformation
 
Dans le code ou en ligne de commande de la console JS du navigateur, lancer : 
 
var myStore = new MyStore({ container : "https://localhost:8443/2013/cartopair/",
                       context : "http://owl.openinitiative.com/oicontext.jsonld",
                       template : "",
                       partials : ""})
 
  var jsonLd = {
    "@context":{
    },
    "@type" : "av:Organization"
  }
  myStore.save(jsonLd);
 
Cela créer bien un fichier dans le container "cartopair" (appelé "fluidlog" dans l'image ci-dessous...).
 
 
Le fichier contient bien le triplet en utilisant la syntaxe N-Triple. Cool !
 

GET d'un triplet dans rwwplay!

Lancer dans la console, lancer la ligne suivante : 
 
store.get("https://localhost:8443/2013/cartopair/2ef17ab70e").then(console.log.bind(console))
 
Ca marche ! Je récupère bien mon triplet ! :)
 
 
... le triplet attendu étant (pour ceux qui ont du mal à suivre) : @type : "http://...#Organization"
 

A présent, débarrassons-nous du contexte de Sylvain ! :)

 
Hé oui, il fallait que ça arrive, depuis le début, nous utilisions le contexte de Sylvain stocké sur le site d'Open Initiative, mais soyons indépendant, chacun son contexte et dieu pour tous ! :)
 
Profitons-en pour structurer un peu le code JS : 
 
  var serverUri = "https://localhost:8443/2013/cartopair/";
  var contextmap = {
    "@context":{
    }}
 
  var store = new MyStore({ container : serverUri,
                            context : contextmap,
                            template : "",
                            partials : ""})
 
  var jsonLd = {"@type" : "av:Organization"}
 
  store.save(jsonLd);
 
En effet, dans l'objet "jsonLd", le contexte est facultatif.
Ce qui donne un nouveau fichier dont la structure est la même, en revanche, voici ce qu'on reçoit lors du GET : 
 
store.get("https://localhost:8443/2013/cartopair/23e45cbe1d").then(console.log.bind(console))
 
 
On voit que nous n'avons n'utilisons plus l'ancien contexte "http://owl.openinitiative.com/oicontext.jsonld", et qu'il y a à présent @type : "av:Organization" (le préfixe est utilisé).
 

Données de contexte représentant une Carto PAIR

 
Données JSOND3 (c'est à dire celles qu'on donne à D3js) de base : 
{
"nodes":
    [
        {"index":0,"label":"A","type":"project","x":100,"y":100,"identifier":"http://fluidlog.com/node/0"},                
        {"index":1,"label":"B","type":"idea","x":200,"y":200,"identifier":"http://fluidlog.com/node/1"},
        {"index":2,"label":"C","type":"project","x":300,"y":300,"identifier":"http://fluidlog.com/node/2"}
    ],
"edges":
    [
        {"index" : 0, "source":0, "target":1, "type":"loglink:linkedto", "identifier": "http://fluidlog.com/edge/2"}
    ]
}
 
Création du JSON-LD
(Ce fichier sera modifié au fur et à mesure, pour conserver une cohérence avec ce qui suit)
Pour cela, nous avons utilisé le playground du site json-ld.org, qui permet de valider un JSON-LD : 
 
{
  "@context":{
  },
  "nodes":[
    {"@id":"http://fluidlog.com/node/0","@type":"av:project","index":"0","label":"A","x":"101","y":"102"},
  {"@id":"http://fluidlog.com/node/1","@type":"av:idea","index":"1","label":"B","x":"203","y":"204"},
  {"@id":"http://fluidlog.com/node/2","@type":"av:project","index":"2","label":"C","x":"305","y":"306"}
  ],
    "edges" : [
      { "@id" : "http://fluidlog.com/edge/0",
        "@type":"loglink:linkedto",
        "index": "0",
        "source" : "http://fluidlog.com/node/0",
        "target" : "http://fluidlog.com/node/1"}
    ]
}
 
Remarque :  Il est très important de mettre un "@id" pour les nœuds et les liens,  sinon, le serveur de triplet, Rwwplay! créer des noeuds blancs.
 

POST dans Semantic forms en ligne de commande

Afin de tester un GET (via le ldp-framework), il faut avoir des données dans Semantic forms.
 
  1. Sauvegardez le json-ld ci-dessus dans un fichier "carto1.json-ld".
  1. Ensuite, lancez la commande suivante, qui va envoyer ce fichier vers le serveur Semantic Forms en utilisant la méthode http POST.
 
wget  --post-file=carto1.json-ld --header='Content-Type: text/json-ld' --header='Slug: carto5' http://localhost:9000/ldp/fluidlog/
 
 
Au niveau du serveur Semantic Forms, des choses se passent, vous pouvez le voir en regardant la console.
...
 

GET dans Semantic Forms en ligne de commande

wget --header='Accept:text/json-ld' http://localhost:9000/ldp/fluidlog/carto5
 
 
Côté serveur, on voit le graph "sortir" de la base de semantic Forms... (fabuleux !) :)
 
 
Bon, à présent, il va donc falloir faire ceci, mais depuis l'application FluidGraph, et récupérer la substantifique moelle de ces données sémantisées.
 
Pour cela, nous allons tout simplement (théoriquement) utiliser la librairie de Sylvain : LDP Framework, dont le rôle est justement d'assurer les GET et les POST sur un serveur LDP.
 
 

Tests à lancer dans la console de FluidGraph

 
L'application FluidGraph inclue déjà le fichier mygraph.js qui est le cœur du LDP-Framework.
 
Lorsque nous lançons FluidGraph dans notre navigateur, le contexte Javascript est prêt pour accepter les méthodes de mygraph.js (save, get, etc...).
 
Nous allons donc directement les lancer dans la console JS du navigateur.
Personnellement, je suis sur Firefox, mais ça marche aussi avec Chrome.
 
Attention : si vous utilisez Rwwplay! comme serveur LDP, il faut que votre navigateur accepte les requêtes sur le serveur local (https://localhost:8443).
 
Ouvrez donc la console de votre navigateur 
Si vous ne souhaitez pas voir toutes les erreurs, vous pouvez jouer avec les onglets (retirer Journal, ajouter Réseau...).
 
et tapez :
store = new MyStore({container : "http://localhost:9000/ldp/fluidlog/",context : "http://owl.openinitiative.com/oicontext.jsonld", template:"", partials:""})
 
On va ensuite tenter de récupérer le fichier carto5
store.get("http://localhost:9000/ldp/fluidlog/carto5 ").then(console.log.bind(console))
 
Comme ceci :
 
 
Voilà, nous en somme là, le get ne renvoit rien, alors que dans la sortie de la console de Semantic Forms, on voit bien le graph ressortir en destination de la commande get, comme dans l'exemple en ligne de commande...
 
Dernière info : d'après la discussion dans Basecamp, cela avait fonctionné (yannick4/test5), j'ai relancé mon autre PC pour voir la différence, bien fait attention à @id, mais rien n'y fait, je n'arrive plus à le faire fonctionner...
 
Erreur JS dans la console : 
uncaught exception: parser error: Error: Expected subject but got literal at line 2. <inconnu>
 
 
Après recherche, cette erreur est celle rencontrée le 2 octobre et causée par le "content-type : "application/ld+json; charset=utf-8" non accepté dans LDP Framework 
Suite à un échange avec Sylvain, la correction n'est pas encore effective car le problème vient d'un problème dans la librairie rdf-ext...
 
Correction "rustine", demander à Semantic Forms de ne pas envoyer le charset UTF-8:
dans Application.scala: ligne 212 :
       val contentType = accepts.mimeType + "; charset=utf-8" (mettre en commentaire la fin)
 
Une fois qu'on a mis en place cette rustine, on reçoit bien un objet dans le GET !
 
 

Code JS appelant le LDP-Framework

Voici un exemple de code JS qui appelle la fonction save du ldp-framework pour sauvegarder des triplets dans Semantic Forms.
 
  var myStore = new MyStore({
  container : "http://localhost:9000/ldp/fluidlog/",
  template : "",
  partials : ""});
  
  myStore.save(jsonLd);
 
Exemple d'objet JS contenant le JSON-LD 
...obtenue en faisant   console.log("jsonLd " + JSON.stringify(jsonLd)); et en faisant un copier/coller de la sortie ici.
 
var jsonLd = {
"nodes":
[
{"@id":"http://fluidlog.com/0","@type":"av:project","index":"0","label":"A","x":"201","y":"202"},
{"@id":"http://fluidlog.com/1","@type":"av:idea","index":"1","label":"B","x":"303","y":"304"},
{"@id":"http://fluidlog.com/2","@type":"av:project","index":"2","label":"C","x":"405","y":"406"}
],
"edges":
[
{"source":"http://fluidlog.com/0","target":"http://fluidlog.com/1"}
],
"@id":""
}
 
Suite à un échange avec Sylvain, quand on fait un "save()", si ton objet a un @id, ça appelle update, sinon ça appelle add. Donc c'est normal (pour l'instant), de ne pas avoir d'@id dans notre objet JSON.
 
Le navigateur refuse d'envoyer la requête à partir d'un fichier HTML local:
 myStore.save(jsonLd);
undefined
mystore.js:31456 XMLHttpRequest cannot load file:///home/jmv/src/SemanticNodes/lesfaitsdesmots/loglink4.6/index.html. Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, https, chrome-extension-resource.utils.defaultRequest @ mystore.js:31456(anonymous function) @ mystore.js:29562serialize @ mystore.js:30059self.add @ mystore.js:29556(anonymous function) @ mystore.js:30113(anonymous function) @ mystore.js:30091(anonymous function) @ mystore.js:30082(anonymous function) @ mystore.js:34576
index.html:1 Uncaught (in promise) status code error: 0
 
J'ai donc écrire un petit script qui met FluidLog 4.6 dans Semantic_forms , c à d. de façon que le serveur Play! renvoie les pages FluidLog 4.6 .
FAIT!
Maintenant je peux voir FluidLog avec cet URL:
NON, j'ai raccourci l'URL, je ne mets que 4.6 dans  Semantic_forms .
 
Hélas, même après avoir implémenté le PUT comme  le POST , ça plante comme ça:
 
utils.defaultRequest @ mystore.js:31456
(anonymous function) @ mystore.js:29562
rdf.serialize @ mystore.js:30059
self.add @ mystore.js:29556
(anonymous function) @ mystore.js:30113
funcTemplate @ mystore.js:30091
funcTemplate @ mystore.js:30082
(anonymous function) @ mystore.js:34576
 
localhost:9000/assets/SemanticNodes/index.html:1 Uncaught (in promise) status code error: 404
 

Sauvegarde depuis FluidGraph

 
Lorsqu'on sauvegarde directement via le bouton du menu, cela lance un save() sur Semantic Forms, et cela fonctionne !!!!! :-)
Jean-Marc en a profité pour implémenter le POST.
 

GET (console) une fois sauvegardé avec FluiGraph dans SemForms

 
Taper dans la console :
store = new MyStore({container : "http://localhost:9000/ldp/fluidlog/",context : "http://owl.openinitiative.com/oicontext.jsonld", template:"", partials:""})
 
On va ensuite tenter de récupérer le fichier carto5
store.get("http://localhost:9000/ldp/fluidlog/unnamed ").then(console.log.bind(console))
 
On reçoit bien un objet, mais celui-ci est vide :-(
 
Pourtant, dans la console de Semantic forms, on voit bien le graph sortir :
 
LDP GET: request GET /ldp/fluidlog/unnamed
GET:
 
CONSTRUCT { ?s ?p ?o } WHERE {
  graph <lpd:fluidlog/unnamed> {
    ?s ?p ?o .
  }
}
sparqlConstructQuery: before parseConstruct
sparqlConstructQuery: before executeConstruct
LDP: GET: result {
  "@graph" : [ {
    "@id" : "_:b0",
    "source" : "http://fluidlog.com/0",
    "target" : "http://fluidlog.com/1"
  }, {
    "@id" : "http://fluidlog.com/0",
    "@type" : "av:project",
    "index" : "0",
    "label" : "A",
    "x" : "201",
    "y" : "202"
  }, {
    "@id" : "http://fluidlog.com/1",
    "@type" : "av:idea",
    "index" : "1",
    "label" : "B",
    "x" : "303",
    "y" : "304"
  }, {
    "@id" : "http://fluidlog.com/2",
    "@type" : "av:project",
    "index" : "2",
    "label" : "C",
    "x" : "405",
    "y" : "406"
  }, {
    "@id" : "lpd:fluidlog/",
    "edges" : "_:b0",
  } ],
  "@context" : {
    "index" : "http://schema.org/index",
    "label" : "http://schema.org/label",
    "x" : "http://schema.org/x",
    "y" : "http://schema.org/y",
    "source" : "http://schema.org/source",
    "target" : "http://schema.org/target",
    "edges" : {
      "@id" : "http://schema.org/edges",
      "@type" : "@id"
    },
    "nodes" : {
      "@id" : "http://schema.org/nodes",
      "@type" : "@id"
    }
  }
}
 
contentType application/ld+json
 
Nous en resterons là pour aujourd'hui, mais nous continuons demain !
 
Afin de se familiariser avec la sémantisation (et partir de cas simple avant de faire complexe), je créer un autre PAD.
 

Exercices de sémantisation de l'information

 
 

Nouveau test avec une information simple

 
Suite à cet exercice de sémantisation, nous arrivons à un JSON-LD beaucoup plus simple ne contenant qu'un triplet, ce qui va simplifier nos tests :)
 
{
  "@context":{
  },
  "@type" : "av:Organization"
}
 

Test sur Semantic Forms

 
  var store = new MyStore({
  container : "http://localhost:9000/ldp/fluidlog/",
  template : "",
  partials : ""});
 
var jsonLd = {
  "@context":{
  },
  "@type" : "av:Organization"
}
 
  store.save(jsonLd);
 
Qui semble fonctionner correctement dans Semantic Forms :
 
 
Lorsqu'on fait un GET de cette ressource en ligne de commande : 
store.get("http://localhost:9000/ldp/fluidlog/unnamed ").then(console.log.bind(console))
 
Bug
 
Nous obtenons toujours un objet vide, donc le problème ne vient pas de notre contenu, mais certainement du contexte, qui n'est pas en ligne...
 
 
 
Encore une fois, on voir des choses intéressantes sortir de Semantic Forms...
 
 
Dans l'onglet réseau, on a le droit à un beau 200, tout va bien :)
 
 
 

Nouveau test sur Semantic Forms avec le contexte simplifié

 
Pour cela, il suffit d'arrêter rwwplay! et de relancer SF, et de modifier dasn le code : 
var serverUri = "http://localhost:9000/ldp/fluidlog/";
 
Puis toujours le GET : 
store.get("http://localhost:9000/ldp/fluidlog/unnamed ").then(console.log.bind(console))
 
Même résulat... :-(
 
Nous envoyons un mail à Sylvain pour comprendre cette histoire...
 

The "John Lennon" test ...

 
  var store = new MyStore({ container : "http://dbpedia.org/resource/",
                            context : {},
                            template : "",
                            partials : ""})
 
store.get("http://dbpedia.org/resource/John_Lennon").then(console.log.bind(console))
 
Ne fonctionne pas non plus... Bon, fallait pas rêver non plus... :)
 

Comment publier un contexte JSON-LD en ligne ?

 
Bon,  a priori, le problème ne vient pas du contexte, mais il sera  intéressant plus tard de savoir publier son propre contexte. J'ai essayé  sur fluidlog, cela n'a pas fonctionné, certainement à cause (encore et  toujours) de CORS, qu'il faut que j'active sur mon serveur, mais cela  dépend peut-être de mon hébergement !
 
@JM, peux-tu mettre le lien vers la documentation que tu m'avais envoyé et que je ne retrouve plus ? Merci.
 

UPDATE d'un JSON-LD existant

 
Erreur 422 - precondition failed
La requête a l'air propre ?
 
 
Nouveau test plus simple : 
 
Pour l'instant, dans la base, il y a cela :
 
var myStore = new MyStore({ container : "https://localhost:8443/2013/cartopair/",
                       context : "http://owl.openinitiative.com/oicontext.jsonld",
                       template : "",
                       partials : ""})
 
  var jsonLd = {
    "@context":{
    },
    "@type" : "av:Organization"
  }
  myStore.save(jsonLd);
 
Cela nous a créé un fichier c4c5d906a1.ttl
 
Nous souhaitons le mettre à jour : 
 
var jsonLd = {
  "@context":{
  },
}
 
myStore.save(jsonLd)
 
Mais rien ne se passe dans l'onglet réseau... :(
 
Sur le serveur, par contre, ça bouge
 
Sylvain pense que c'est à cause d'un bug avec l'ETag.
Il faut faire un get, et ensuite un save(), mais cela ne fonctionne pas mieux...
 
    store.get(thisGraph.externalStore.uri + thisGraph.graphName)
    store.save(jsonLd);
 
Toujours Erreur 422 - precondition failed
 
Idem en ligne de commande
 
myStore.get("https://localhost:8443/2013/cartopair/e913efcf2f").then(console.log.bind(console))
 

Synthèse des problèmes 

Voir l’état des lieux des problèmes avec LDP ici :