Sunteți pe pagina 1din 28

Executarea interogrilor Sesame din Python

Urmtoarele exerciii se vor executa n linia de comand Python i vor presupune c avei pe calculator o
instalare corect a acestui limbaj 1.

Accesarea unui fiier on-line din Python prin HTTP

Importai biblioteca pentru declanarea de cereri HTTP 2:


>>> import urllib2
Trimitei o cerere HTTP ctre pagina Google:
>>> temp=urllib2.urlopen("http://www.google.com")
Preluai rspunsul HTTP ntr-un string:
>>> content=temp.read()
Afiai rspunsul:
>>> content
Ar trebui s putei vedea codul surs HTML i Javascript pentru pagina Google. Aceasta nseamn c
putei s preluai coninut de pe Internet i s l procesai n Python.

ncrcare de grafuri RDF din Python n Sesame prin protocolul HTTP Graph Store

Coninutul RDF poate fi ncrcat folosind cereri HTTP trimise ctre serviciul web Sesame, la o adres de
forma urmtoare (va trebui s concatenm identificatorul grafului la captul ei):
http://localhost:8080/openrdf-sesame/repositories/numele bazei de cunostinte/rdf-graphs/service?graph=identificatorul grafului

S se creeze fiierul statements1.ttl n directorul principal Python (C:\Python27):


@prefix : <http://expl.at#>.
@prefix foaf: <http://xmlns.com/foaf/0.1/>.
:Anna foaf:knows :Andrew, :Alin, :Roxanne, :Maria.
:Andrew foaf:knows :Maria, :Jim.
:Jim foaf:knows :Ana, :Roxanne, :George.
:George foaf:knows :Jane.
Observai c am folosit unii termeni din terminologia FOAF (pentru relaii sociale).

S se creeze n Sesame o nou baz de cunotine numit PythonRepo. S se adauge urmtoarele


cunotine scris cu sintaxa TriG:
@prefix : <http://expl.at#>.
:friends {:Ana :cunoastePe :Andrei}

Revenii la linia de comand Python. n continuare vom folosi Python pentru a nlocui coninutul grafului
:friends cu coninutul fiierului .ttl de pe disc. Pentru aceasta, se vor parcurge urmtorii pai:
1. Construirea adresei URL la care putem accesa graful, din 3 componente:
a. serverul Sesame,
b. comanda trimis serverului,
c. identificatorul grafului pe care l accesm,
2. Configurarea unei cereri HTTP la acea adres, cu componentele:

1
Exemplele sunt testate pentru Python 2.7
2
Atenie, dac nchidei Python i continuai ulterior cu exerciiile, importurile n linia de comand Python trebuie
executate din nou. n acest document importul n linia de comand se face o singur dat, prima dat cnd e
nevoie de o librrie i considerm c linia de comand nu se nchide pn la finalizarea exerciiilor.
a. coninutul care se trimite (citit din fiierul de pe disc),
b. sintaxa n care se trimite (Turtle, N-triples, etc.) n cazul nostru fiierul e scris n
Turtle,
c. metoda prin care se trimite (GET, POST, PUT etc., fiecare avnd un alt efect asupra
grafului accesat) n cazul nostru vom folosi PUT, care are ca efect nlocuirea unui
graf, dac acesta exist deja, sau crearea sa, dac nu exist. Noi ne aflm n prima
situaie.
3. Executarea cererii HTTP

Definii serverul ce urmeaz a fi accesat:


>>> server="http://localhost:8080/openrdf-sesame/"
Aceasta este adresa URL la care Sesame ofer un serviciu de interogare prin HTTP. De fapt e vorba de un
serviciul web de tip REST, configurat s rspund la adrese URL cu o anumit structur! De obicei adresa
URL va include serverul, o comand care trebuie executat i parametrii comenzii.

Definii comanda ce urmeaz a fi trimis serviciului:


>>> command="repositories/PythonRepo/rdf-graphs/service?"
Aceasta este structura pentru adresa URL ateptat de protocolul Graph Store 3 pentru comenzile
executate prin HTTP.

n continuare definii ca parametru al comenzii identificatorul grafului. Aici intervine o problem: faptul
c toate aceste elemente vor alctui o adres URL impune anumite restricii - URL-urile nu permit orice
caractere. n consecin e necesar o codare URL a identificatorului de graf, cu ajutorul unei funcii
(urlencode) disponibil n librria urllib (care se import separat de urllib2!):
>>> import urllib
>>> params=urllib.urlencode({"graph":"http://expl.at#friends"})

Concatenai toate aceste componente pentru a obine adresa URL complet a grafului accesat:
>>> target=server+command+params
>>> target
'http://localhost:8080/openrdf-sesame/repositories/PythonRepo/rdf-graphs/service?graph=http%3A%2F%2Fexpl.at%23friends'

Deschidei n Python fiierul RDF de pe disc i punei coninutul acestuia ntr-un string:
>>> fileobject=open("statements1.ttl")
>>> content=fileobject.read()
>>> content
'@prefix : <http://expl.at#>.\n@prefix foaf: <http://xmlns.com/foaf/0.1/>.\n:Anna foaf:knows :Andrew, :Alin, :Roxanne, :Maria.\n:Andrew
foaf:knows :Maria, :Jim.\n:Jim foaf:knows :Ana, :Roxanne, :George.\n:George foaf:knows :Jane.\n'

Creai o cerere HTTP, folosind clasa Request, al crei constructor trebuie s primeasc adresa int i
coninutul care se va trimite:
>>> request=urllib2.Request(target, content)

Folosind cmptul de antet HTTP numit Content-Type, definii tipul MIME pentru sintaxa ce urmeaz a fi
trimis 4:
>>> request.add_header("Content-Type","application/x-turtle")

3
Detalii la adresa: http://rdf4j.org/sesame/2.7/docs/system.docbook?view#The_Sesame_REST_HTTP_Protocol
4
Consultai lista tipurilor MIME pentru fiecare sintax la adresa
http://rdf4j.org/sesame/2.7/docs/system.docbook?view#Content_types (probabil la adresa respectiv vei gsi un
alt cod MIME pentru sintaxa Turtle pot exista mai multe astfel de coduri pentru o sintax)
Definii metoda HTTP ce urmeaz a fi folosit:
>>>request.get_method=lambda:"PUT"
Metoda PUT este folosit pentru a trimite coninut prin HTTP n scopul de a nlocui coninutul prezent al
grafului destinaie. Implicit, metoda folosit este POST (care adaug coninut la cel existent). lambda
este o construcie special Python o funcie anonim ce se creeaz ad-hoc pentru a schimba valoarea
returnat prin funcia get_method(). Nu putei scrie n mod direct request.get_method=PUT deoarece
nu putem atribui o valoare unei funcii/metode (unei funcii trebuie s i se atribuie o alt funcie, n
acest caz, una care nu are argumente i doar returneaz valoarea PUT).

Executai cererea HTTP cu urlopen:


>>>urllib2.urlopen(request)

Verificai n Sesame n baza de cunotine PythonRepo i ar trebui s gsii graful :friends cu afirmaiile
preluate din fiier.

Protocolul Graph Store este folositor pentru a lucra la nivel de graf (crearea unui graf, nlocuirea unui
graf, distrugerea unui graf) dar nu i la nivelul tripleilor sau a interogrilor. Din fericire Sesame ofer n
cadrul aceluiai serviciu Web i faciliti ale protocolului SPARQL, care permite un acces mai granular.

ncrcarea coninutului RDF din Python prin protocolul SPARQL HTTP

Operaia se realizeaz n mod asemntor, dar acest protocol folosete o adres puin diferit 5:
http://localhost:8080/openrdf-sesame/repositories/numele bazei de cunostinte/statements?context=<identificatorul de graf>

Definii serverul ce va fi accesat:


>>> server="http://localhost:8080/openrdf-sesame/"

Creai fiierul statements2.ttl i salvai-l n directorul principal Python (C:\Python27):


@prefix : <http://expl.at#>.
@prefix dc: <http://purl.org/dc/elements/1.1/>.
:Terminator dc:creator :JamesCameron; dc:title "Terminator".
:Hamlet dc:creator :Shakespeare; dc:title "Hamlet"; dc:language "en".
Observai c am folosit unii termeni din terminologia Dublin Core.

Creai comanda ce va fi folosit de protocolul SPARQL:


>>> command="repositories/PythonRepo/statements?"
Creai identificatorul de graf dup cum este solicitat de protocolul SPARQL (prin variabila context):
>>> params=urllib.urlencode({"context":"<http://expl.at#mediaworks>"})
Remarcai c spre deosebire de cazul anterior, trebuie s includem paranteze unghiulare n
identificatorul de graf.

Creai adresa destinaie prin concatenare (serverul rmne acelai):


>>> target=server+command+params

Punei coninutul fiierului de pe disc ntr-un string:

5
Serviciul Sesame poate fi contactat la mai multe astfel de adrese, fiecare avnd un alt efect, n combinaie cu
metoda HTTP i parametrul ataat cererii HTTP. Lista complet i combinaiile permise (inclusiv interpretarea pe
care Sesame o d fiecrei cereri HTTP) pot fi consultate la adresa
http://rdf4j.org/sesame/2.7/docs/system.docbook?view#Protocol_summary
>>> fileobject=open("statements2.ttl")
>>> content=fileobject.read()
>>> content
'@prefix : <http://expl.at#>.\n@prefix dc: <http://purl.org/dc/elements/1.1/>.\n:Terminator dc:creator :JamesCameron; dc:title
"Terminator".\n:Hamlet dc:creator :Shakespeare; dc:title "Hamlet"; dc:language "en".\n'

Creai o nou cerere, de data asta folosind metoda POST:


>>> request=urllib2.Request(target, content)
>>> request.add_header("Content-Type","application/x-turtle")
>>> request.get_method=lambda:"POST"

Executai cererea:
>>> urllib2.urlopen(request)

Verificai baza de cunotine pentru a vedea graful nou numit :mediaworks.

ncrcai coninut RDF din Python prin protocolul SPARQL folosind interogri de scriere

Interogrile SPARQL pot fi concatenate la adresa:


http://localhost:8080/openrdf-sesame/repositories/numele bazei de cunostinte/statements?update=interogarea de scriere

Definii serverul ce urmeaz a fi accesat:


>>> server="http://localhost:8080/openrdf-sesame/"
Creai comanda dup cum este ateptat de protocolul SPARQL:
>>> command="repositories/PythonRepo/statements?"

Va trebui s codm interogarea i s o atribuim parametrului update:


>>> params=urllib.urlencode({"update":"prefix : <http://expl.at#> insert data {graph :other {:Ana :livesIn :Vienna}}"})
>>> params
'update=prefix+%3A+%3Chttp%3A%2F%2Fexpl.at%23%3E+insert+data+%7Bgraph+%3Aother+%7B%3AAna+%3AlivesIn+%3AVienna%7D
%7D'
Observai c e vorba de o interogare INSERT ce include i afirmaiile ce trebuie adugate, deci ele nu se
vor mai citi dintr-un fiier de pe disc.

Vom construi apoi o nou adres destinaie, incluznd i interogarea (care include afirmaiile):
>>> target=server+command+params

Putem acum s executm cererea.


>>> request=urllib2.Request(target,None)
>>> request.get_method=lambda:"POST"
>>> urllib2.urlopen(request)
Remarcai c la constructorul Request am folosit cuvntul None n loc s precizm coninutul ce trebuie
trimis! Asta deoarece coninutul este deja precizat n corpul interogrii. n consecin, nici sintaxa
coninutului (content type) nu mai trebuie declarat.

Verificai baza de cunotine pentru a observa un graf numit :other cu o singur afirmaie, creat cu
ajutorul interogrii.

Ultimul exemplu mai putea fi realizat prin specificarea interogrii ca i coninut HTTP (separat de adresa
int) dar n acest caz ar trebui s declarm un tip special de coninut (tipul MIME pentru coninut
preluat din formulare, ca n HTML/Javascript):
>>> request=urllib2.Request(server+"repositories/PythonRepo/statements",params)
>>> request.add_header("Content-Type","application/x-www-form-urlencoded")
>>> request.get_method=lambda:"POST"
>>> urllib2.urlopen(request)

Citirea coninutului dintr-un graf

Definii serverul ce urmeaz a fi accesat:


>>> server="http://localhost:8080/openrdf-sesame/"

Definii comanda ce urmeaz a fi executat:


>>> command="repositories/PythonRepo/rdf-graphs/service?"

Definii ca parametru graful ce trebuie interogat:


>>> params=urllib.urlencode({"graph":"http://expl.at#friends"})

Concatenai toate aceste componente pentru a obine adresa int.


>>> target=server+command+params

Creai o nou cerere:


>>> request=urllib2.Request(target)
Deoarece nu se trimite coninut, constructorul Request nu mai are al doilea argument.
Deoarece se va folosi metoda GET, implicit, nu vom mai specifica metoda.
n schimb trebuie specificat sintaxa n care dorim s primim rspunsul. Atenie, sintaxa rspunsului nu
se mai declar cu Content-Type, ci cu Accept. Indicm faptul c dorim s obinem sintaxa RDF/XML:
>>> request.add_header("Accept","application/rdf+xml")

Executai cererea i afiai rspunsul pe ecran:


>>> temp=urllib2.urlopen(request)
>>> print temp.read()

Ar trebui s putei vedea coninutul grafului.

Citirea coninutului dintr-un graf filtrat dup subiect

Definii serverul ce urmeaz a fi accesat:


>>> server="http://localhost:8080/openrdf-sesame/"

Definii comanda ce urmeaz a fi executat:


>>> command="repositories/PythonRepo/statements?"

Creai parametrii pe de o parte graful de accesat (context), pe de alt parte parametrul subj pentru a
filtra afirmaiile dup un anumit subiect (se pot folosi i pred i obj pentru filtrri dup predicat sau
obiect):
>>> params=urllib.urlencode({"context":"<http://expl.at#friends>","subj":"<http://expl.at#Anna>"})
>>> target=server+command+params
Creai o nou cerere:
>>> request=urllib2.Request(target)

Preluai rspunsul n sintaxa N-triples (tipul MIME este text/plain):


>>> request.add_header("Accept","text/plain")
>>> temp=urllib2.urlopen(request)
>>> print temp.read()
Citirea coninutului unui graf prin interogare SPARQL

Interogrile de citire trebuie trimise la o adres de forma:


http://localhost:8080/openrdf-sesame/repositories/numele bazei de cunostinte?query=interogarea de citire

Definii serverul ce urmeaz a fi accesat:


>>> server="http://localhost:8080/openrdf-sesame/"

Ajustai comanda pentru a permite ataarea interogrilor:


>>> command="repositories/PythonRepo?"

Interogrile se pot trimite codat n cadrul parametrului query:


>>> params=urllib.urlencode({"query":"prefix : <http://expl.at#> select * where {?x ?y ?z}"})
>>> target=server+command+params

Creai cererea i executai-o. Declarai ca sintax dorit formatul JSON i stocai rspunsul ntr-un string:
>>> request=urllib2.Request(target)
>>> request.add_header("Accept","application/sparql-results+json")
>>> temp=urllib2.urlopen(request)
>>> results=temp.read()
>>> print results

Ar trebui s vedei toate afirmaiile din toate grafurile n format JSON. Atenie, interogrile SELECT nu
rspund niciodat cu grafuri RDF, deci nu putem solicita sintaxe precum Turtle, N-triples etc.! Asta
deoarece interogarea SELECT nu returneaz n mod garantat un graf bine format cel mai adesea va
returna tabele cu mai multe coloane, liste de valori, chiar i valori unice (amintii-v de media
bugetelor). n consecin pentru rezultatele SELECT rspunsul se primete de obicei ca structur JSON
sau XML ce trebuie procesat de client pentru a extrage datele dorite.

Procesarea rezultatelor JSON i extragerea datelor din ele

Importai parserul JSON:


>>> import json

Convertii rspunsul JSON ntr-un dicionar Python:


>>> dictionary=json.loads(results)

Studiai puin structura JSON afiat anterior, pentru a nelege de unde se pot extrage valorile.
Observai c structura JSON este una arborescent, ce include diverse array-uri (liste sau dicionare
Python): results, bindings, vars.

Extragei numele variabilelor din rspuns:


>>> dictionary['head']['vars']
[u'x', u'y', u'z']
Atenie, literele u sunt doar nite indicatori ai faptului c e vorba de stringuri UNICODE. Observai c
rezultatele sunt stocate n variabilele x, y, z, aa cum a impus interogarea.

Extragei prima nregistrare din rspuns, preluat din arrayul bindings, care e inclus n results:
>>> dictionary['results']['bindings'][0]
{u'y': {u'type': u'uri', u'value': u'http://xmlns.com/foaf/0.1/knows'}, u'x': {u'type': u'uri', u'value': u'http://expl.at#Anna'}, u'z': {u'type': u'uri', u'value':
u'http://expl.at#Andrew'}}

Extragei toate subiectele ntr-o list, parcurgnd acelai array:


>>> subjects=[res['x']['value'] for res in dictionary['results']['bindings']]
Aceasta este o construcie special Python, numit list generat. De fapt e o prescurtare a unui ciclu
FOR care parcurge dicionarul i pentru fiecare nregistrare din bindings extrage valoarea lui x (adic
subiectele afirmaiilor). Putei verifica asta:
>>> subjects
[u'http://expl.at#Anna', u'http://expl.at#Anna', u'http://expl.at#Anna', u'http://expl.at#Anna', u'http://expl.at#Andrew', u'http://expl.at#Andrew',
u'http://expl.at#Jim', u'http://expl.at#Jim', u'http://expl.at#Jim', u'http://expl.at#Ana', u'http://expl.at#George', u'http://expl.at#Terminator',
u'http://expl.at#Terminator', u'http://expl.at#Hamlet', u'http://expl.at#Hamlet', u'http://expl.at#Hamlet']

Afiai coninutului listei fr repetiii:


>>> set(subjects)
set([u'http://expl.at#Jim', u'http://expl.at#Ana', u'http://expl.at#Terminator', u'http://expl.at#Hamlet', u'http://expl.at#George',
u'http://expl.at#Andrew', u'http://expl.at#Anna'])

tergei un graf prin protocolul Graph Store

Definii adresa destinaie:


>>> server="http://localhost:8080/openrdf-sesame/"
>>> command="repositories/PythonRepo/rdf-graphs/service?"
>>> params=urllib.urlencode({"graph":"http://expl.at#other"})
>>> target=server+command+params

Definii cererea:
>>> request=urllib2.Request(target)
>>> request.get_method=lambda:"DELETE"
>>> urllib2.urlopen(request)

Verificai baza de cunotine dac graful :other a fost distrus. Observai c am folosit metoda HTTP
DELETE n acest scop, i am folosit adresa structurat conform protocolului Graph Store, care permite
operaii la nivel de graf (aici, tergerea unui graf ntreg).

tergerea unui graf prin interogri SPARQL

Definii adresa destinaie:


>>> server="http://localhost:8080/openrdf-sesame/"
>>> command="repositories/PythonRepo/statements?"
>>> params=urllib.urlencode({"update":"drop graph <http://expl.at#mediaworks>"})
>>> target=server+command+params
Definii cererea:
>>> request=urllib2.Request(target,None)
>>> request.get_method=lambda:"POST"
>>> urllib2.urlopen(request)
Observai c spre deosebire de exemplul anterior, nu am indicat operaia printr-o metod HTTP
(DELETE) ci ca interogare SPARQL (DROP). Metoda HTTP folosit aici rmne POST care n protocolul
SPARQL este obligatorie pentru toate interogrile de scriere (fie c sunt de inserare, tergere etc.),
mpreun cu folosirea parametrului update.

Verificai baza de cunotine dac graful :mediaworks a fost distrus.


Procesarea grafurilor cu ajutorul librriei RDFlib
n caz c nu l-ai instalat deja de la alte cursuri, downloadai managerul de librrii externe pentru
Python, numit easy_install:
https://bootstrap.pypa.io/ez_setup.py
Executai fiierul. Dac nu ruleaz, nseamn c folderul ce conine executabilul python.exe (probabil
C:\Python27) trebuie adugat la variabila de mediu PATH din Windows.

n urma instalrii, managerul easy_install trebuie la rndul su inclus n variabila de mediu PATH (calea la
care s-a instalat ar trebui s fie C:\Python27\Scripts) pentru a putea fi executat din orice folder.

Dup ce ai fcut i asta, deschidei linia de comand i testai instalarea sa:


easy_install h
Ar trebui s vedei help-ul pentru easy_install. Acest program asigur instalarea de librrii externe, care
nu vin incluse n kit-ul de instalare Python.

Dac totul funcioneaz, folosii acest program pentru a instala biblioteca RDFLib, prin tastarea n linia
de comand Windows (nu cea din Python!):
easy_install rdflib
Documentaia RDFLib este disponibil la adresa:
https://rdflib.readthedocs.org/en/stable/

ncrcai urmtorul exemplu n Sesame, ntr-o baz de cunotine numit PythonRepo2 (exemplul se
gsete n fiierul rdflibsample1.ttl):
@prefix : <http://expl.at#>.
@prefix foaf: <http://xmlns.com/foaf/0.1/>.
:friends
{
:Anna foaf:knows :Andrew, :Alin, :Roxanne, :Maria.
:Andrew foaf:knows :Maria, :Jim.
:Jim foaf:knows :Anna, :Roxanne, :George.
:George foaf:knows :Jane.
}
:otherinfo
{
:Anna :livesIn :Vienna.
}

Creai un al doilea fiier numit rdflibsample2.ttl dar n loc s l ncrcai n Sesame, facei-l disponibil
pentru acces HTTP direct prin localhost, salvndu-l n rdcina Tomcat (webapps\ROOT):
@prefix : <http://expl.at#>.
@prefix dc: <http://purl.org/dc/elements/1.1/>.
:Terminator dc:creator :JamesCameron; dc:title "Terminator".
:Hamlet dc:creator :Shakespeare; dc:title "Hamlet"; dc:language "en".

Importai grafuri RDF din diverse surse ntr-un set de date RDFLib

Am vzut n exerciiile precedente cum grafurile RDF interogate prin HTTP sosesc n Python sub form de
stringuri, care apoi necesit procesri suplimentare pentru a extrage datele ce prezint interes:
fie procesare ca structuri de date JSON, dac e vorba de rezultate ale interogrilor SELECT (e
posibil i ca XML, cu o structur similar);
fie procesare brut ca stringuri, dac e vorba de coninutul integral al unui fiier RDF sau al unui
graf extras din Sesame.
n cel de-al doilea caz, e mult mai convenabil s folosim metode obiectuale dedicate pentru manipularea
grafurilor (aa cum funciile DOM permit manipularea documentelor XML). RDFLib este un exemplu de
librrie care permite acest lucru (o alt librrie este RDFAlchemy, tot n Python; pentru proiectul vostru
va trebui s identificai librriile existente n limbajul de programare ales).

n continuare vom folosi 3 strategii pentru a accesa grafuri RDF din Python:
1. trimiterea unei cereri HTTP configurate cu urllib2 spre serviciul Sesame, prin protocoalele
discutate deja;
2. trimiterea unei cereri HTTP simple, direct spre un fiier RDF accesibil on-line (n Tomcat), prin
funcii RDFLib
3. trimiterea unei cereri HTTP simple (neconfigurate), spre serviciul Sesame, prin funcii RDFLib.

Strategia 1. Cerere HTTP configurat:


Pornii linia de comand Python IDLE i extragei toate afirmaiile din PythonRepo2:
>>> import urllib2
>>> server="http://localhost:8080/openrdf-sesame/"
>>> command="repositories/PythonRepo2/statements"
>>> request=urllib2.Request(server+command)
>>> request.add_header("Accept","application/x-trig")
>>> temp=urllib2.urlopen(request)
>>> results=temp.read()
>>> print results
Observai c s-a folosit metoda implicit GET pentru a extrage ntregul coninut al bazei de cunotine n
format TriG.

Creai un set de date de tip in-memory n Python:


>>> import rdflib
>>> d1=rdflib.Dataset()
Preluai n setul de date rezultatele din cererea HTTP:
>>> d1.parse(data=results, format="trig")
Observai utilizarea funciei parse(), ce convertete grafuri RDF dintr-un format textual (TriG n acest caz)
ntr-un obiect Python ce va fi ulterior procesate prin metode obiectuale specializate.

Seturile de date in-memory sunt create n memoria RAM i vor fi procesate n regim run-time (deci se
distrug la oprirea Python). Sunt posibile i variantele:
Stocarea lor persistent ntr-o baz de date (inclusiv n MySQL, n urma unei convertiri a
grafurilor n tabele!). Pentru aceasta RDFLib ofer clasa Store(). Acest tip de stocare necesit
unele drivere pentru conectarea ntre Python i sistemul pentru baze de date;
Stocarea lor persistent la un serviciu de interogare SPARQL. Pentru aceasta RDFLib ofer clasa
SPARQLStore();
Salvarea lor pe disc ca fiiere RDF, pentru a fi ulterior preluate direct de pe disc de o alt
aplicaie. Pentru aceasta RDFLib ofer funcii de serializare (conversia grafurilor n text).
n cazul nostru stocarea persistent este deja asigurat de Sesame i nu vom crea un al doilea sistem de
stocare, ci vom folosi RDFLib doar pentru a prelucra grafuri extrase din Sesame n regim run-time.

n practic e, totui, posibil s se lucreze cu mai multe sisteme de stocare persistent de exemplu o
aplicaie Python care interogheaz o baz de cunotine on-line (de ex. DBPedia) i i stocheaz
rezultatele pentru reutilizare ulterioar ntr-un Sesame local, n fiiere RDF pe discul local sau chiar ntr-o
baz de date MySQL local. Cei care realizeaz lucrri de licen pe aceast tem, s se gndeasc la
aceast posibilitate.

Strategia 2. Cerere HTTP neconfigurat spre un fiier on-line accesibil direct (pe Tomcat):

Funcia parse() nu asigur doar conversia de text n seturi de date RDFLib, ci poate chiar s acceseze
coninut RDF prin HTTP. Exemplificm acest lucru prin crearea unui nou set de date i ncrcarea cu
coninutul fiierului rdflibsample2.ttl (care ar trebui s fie accesibil n mod direct la adresa
localhost:8080, dac a fost salvat corect n rdcina Tomcat):
>>> d2=rdflib.Dataset()
>>> d2.parse(location="http://localhost:8080/rdflibsample2.ttl",format="turtle")
Dezavantaje fa de cererea HTTP configurat:
funcia parse() nu permite o personalizare avansat a cererii HTTP (de exemplu nu permite
comutarea ntre metodele GET, POST etc.);
argumentul format poate induce n eroare: nu e acelai lucru cu configurarea antetelor HTTP
(Accept, Content-Type), adic nu comunic serverului ce sintax SE DORETE, ci comunic lui
RDFLib n ce sintax va sosi coninutul accesat; deci nu se asigur nici flexibilitatea sintactic de
mai sus;
nu se pot trimite interogri, cci adresa int e un simplu fiier, nu un serviciu care s accepte
interogri;
Avantaje:
Este posibil totui ataarea de parametrii prin simpla concatenare (cu codare URL) la adresa
URL;
E o metod mai rapid de a aduce grafuri RDF n Python, atunci cnd acestea pot fi accesate
printr-o cerere HTTP standard, fr configurri, cum e acest caz al unui fiier RDF direct accesibil
pe un server.

Strategia 3. Cerere HTTP neconfigurat spre un serviciu Sesame:

Deoarece parse() poate accesa orice adres URL, nseamn c poate accesa i adresa serviciului Sesame
prin protocoalele studiate mai sus (dar fr posibilitatea de a manipula antetul HTTP, metoda HTTP etc.).
>>> server="http://localhost:8080/openrdf-sesame/"
>>> command="repositories/PythonRepo2/statements"
>>> d3=rdflib.Dataset()
>>> d3.parse(location=server+command)
Am accesat direct coninutul bazei de cunotine din Sesame, profitnd de faptul c acesta e oferit la
adresa format prin concatenarea din exemplu. Rspunsul se preia n sintaxa oferit implicit de Sesame,
adic RDF/XML (reamintim c parse nu poate comunica aceast preferin). Aceast sintax mai
nseamn c se pierde distincia dintre grafurile identificate (nesuportate n RDF/XML).
Spre deosebire de cazul precedent, e posibil totui s se trimit interogri, prin codarea i concatenarea
lor corect la adresa serviciului Sesame, conform protocoalelor discutate.

S se vizualizeze grafurile care au fost obinute n fiecare caz:


>>> graphlist1=[x for x in d1.contexts()]
>>> graphlist2=[x for x in d2.contexts()]
>>> graphlist3=[x for x in d3.contexts()]
>>> graphlist1
Remarcai c n primul caz att graful :friends ct i :otherinfo sunt clar difereniat (mpreun cu un graf
implicit generat de Python pentru toate afirmaiile)
>>> graphlist2
n acest caz, observai c n Python a ajuns un singur graf, al crui identificator este chiar adresa URL a
fiierului accesat.
>>> graphlist3
Pentru acest caz, iar avem un graf, al crui identificator este adresa URL a serviciului Sesame (distincia
dintre grafuri aa cum era n Sesame s-a pierdut).

n concluzie, prima abordare, chiar dac este mai complex, ofer informaii mai complete i permite
configurri flexibile ce pot fi necesare.

Extragerea datelor dintr-un set de date RDFLib prin metode programabile

Creai un obiect graf RDFLib cu ntregul coninut al grafului :friends:


>>> g=d1.graph(rdflib.URIRef("http://expl.at#friends"))
>>> friendstriples=[x for x in g.triples((None,None,None))]
>>> friendstriples
A se observa c metoda triples() extrage afirmaii dup un anumit ablon. ablonul (None, None, None)
reprezint toate afirmaiile (n mod similar cu ablonul {?x ?y ?z} din SPARQL).

Extragei prima nregistrare (este posibil s obinei una diferit, deoarece ordinea este arbitrar):
>>> friendstriples[0]
(rdflib.term.URIRef(u'http://expl.at#Anna'), rdflib.term.URIRef(u'http://xmlns.com/foaf/0.1/knows'), rdflib.term.URIRef(u'http://expl.at#Maria'))

Extragei subiectul primei nregistrri:


>>> friendstriples[0][0]
rdflib.term.URIRef(u'http://expl.at#Anna')

Extragei predicatul primei nregistrri:


>>> friendstriples[0][1]
rdflib.term.URIRef(u'http://xmlns.com/foaf/0.1/knows')

Extragei obiectul primei nregistrri:


>>> friendstriples[0][2]
rdflib.term.URIRef(u'http://expl.at#Maria')

Deoarece rezultatul este afiat ca i obiect URIRef, la afiare programatorul trebuie s realizeze o
conversie ctre string:
>>> str(friendstriples[0][0])
'http://expl.at#Anna'

Coninutul grafului :friends poate fi de asemenea obinut direct din setul de date, dac solicitm
cvadruplei n locul tripleilor din graful selectat:
>>> friendsquads=[x for x in d1.quads((None,None,None,"http://expl.at#friends"))]
>>> friendsquads[0]
(rdflib.term.URIRef(u'http://expl.at#Anna'),rdflib.term.URIRef(u'http://xmlns.com/foaf/0.1/knows'), rdflib.term.URIRef(u'http://expl.at#Roxanne'),
rdflib.term.URIRef(u'http://expl.at#friends'))
Principalele diferene sunt: am accesat setul de date n mod direct n loc s extragem n primul rnd
graful acestuia; graful este indicat n ablonul cvadrupleilor (quad); rezultatul este o list cu cvadruplei
( ce includ ntotdeauna un identificator de graf).

Acelai rezultat poate fi obinut prin reprezentarea tripleilor din setul de date restricionai de un
argument suplimentar pentru context:
>>> friends=[x for x in d1.triples((None,None,None),context="http://expl.at#friends")]
>>> friends

Afiai o list a subiectelor:


>>> subjects=[str(x[0]) for x in friends]
>>> subjects
['http://expl.at#Anna', 'http://expl.at#Jim', 'http://expl.at#Anna', 'http://expl.at#Anna', 'http://expl.at#Jim', 'http://expl.at#Andrew',
'http://expl.at#Jim', 'http://expl.at#Andrew', 'http://expl.at#George', 'http://expl.at#Anna']

Pentru a evita rezultatele duplicat, lista se poate converti ntr-un set:


>>> set(subjects)
set(['http://expl.at#Andrew', 'http://expl.at#Anna', 'http://expl.at#Jim', 'http://expl.at#George'])

Putem s obinem subiectele din obiectul graf extras anterior(g):


>>> subjects=[x for x in g.subjects(None,None)]
>>> subjects
n
Putem obine perechile subiect-obiect pentru acele afirmaii care folosesc proprietatea foaf:knows:
>>> subob=[x for x in g.subject_objects(rdflib.URIRef("http://xmlns.com/foaf/0.1/knows"))]
>>> subob
Practic acestea sunt metode obiectuale ce nlocuiesc necesitatea de a folosi interogri SPARQL pentru a
extrage rezultate corespunztoare unor abloane de afirmaii. Mai sunt disponibile i alte astfel de
metode: predicates(), objects(), predicate_objects(), subject_predicates() 6.
Reinei totui c aceste metode se aplic obiectelor graf (aici g) i nu ntregului set de date (aici d1). Pe
setul de date se folosesc triples() sau quads(), apoi profitm de faptul c rezultatele acestora sunt liste
sau tuple Python, ca n exemplul de mai jos:

S se gseasc lista persoanelor cunoscute de Anna:


>>> Annastatements=[x for x in
d1.triples((rdflib.URIRef("http://expl.at#Anna"),rdflib.URIRef("http://xmlns.com/foaf/0.1/knows"),None),context="http://expl.at#friends")]

Observai c n ablon trebuie s folosim obiecte URIRef i nu stringuri simple!


>>> Annafriends=[x[2] for x in Annastatements]
>>> Annafriends
[rdflib.term.URIRef(u'http://expl.at#Andrew'), rdflib.term.URIRef(u'http://expl.at#Roxanne'), rdflib.term.URIRef(u'http://expl.at#Maria'),
rdflib.term.URIRef(u'http://expl.at#Alin')]

O alt modalitate de a obine prietenii Anei, de aceast dat direct din obiectul graf:
>>> Annafriends=[x for x in g.objects(rdflib.URIRef("http://expl.at#Anna"),rdflib.URIRef("http://xmlns.com/foaf/0.1/knows"))]
>>> Annafriends

Adugai afirmaii noi ntr-un set de date RDFLib prin metode obiectuale

Creai o nou afirmaie (Anna is 30) i creai un graf noi pentru aceasta:
>>> mypref=rdflib.namespace.Namespace("http://expl.at#")
>>> mysubject=mypref["Anna"]
>>> mysubject
rdflib.term.URIRef(u'http://expl.at#Anna')
S-a creat subiectul.
>>> myproperty=mypref["hasAge"]
>>> myproperty
rdflib.term.URIRef(u'http://expl.at#hasAge')

6
Putei gsi mai multe detalii n modului Graph al bibliotecii RDFLib:
http://rdflib.readthedocs.org/en/latest/apidocs/rdflib.html?highlight=dataset#module-rdflib.graph
S-a creat proprietatea.
>>> myobject=rdflib.Literal(30)
>>> myobject
rdflib.term.Literal(u'30', datatype=rdflib.term.URIRef(u'http://www.w3.org/2001/XMLSchema#integer'))
S-a creat obiectul.
>>> mygraph=mypref["newgraph"]
>>> mygraph
rdflib.term.URIRef(u'http://expl.at#newgraph')
S-a creat un graf nou.
>>> myquad=[mysubject,myproperty,myobject,mygraph]
>>> myquad
[rdflib.term.URIRef(u'http://expl.at#Anna'), rdflib.term.URIRef(u'http://expl.at#hasAge'), rdflib.term.Literal(u'30',
datatype=rdflib.term.URIRef(u'http://www.w3.org/2001/XMLSchema#integer')), rdflib.term.URIRef(u'http://expl.at#newgraph')
S-a creat afirmaia, ca o list Python format din cele 4 componente.

Adugai afirmaia la primul set de date din exerciiul anterior.


>>> d1.add(myquad)
O putem vizualiza dac interogm noul graf dup subiectul Anna.
>>> Annastatements=[x for x in d1.quads((mypref["Anna"],None,None,mypref["newgraph"]))]
>>> Annastatements
[(rdflib.term.URIRef(u'http://expl.at#Anna'), rdflib.term.URIRef(u'http://expl.at#hasAge'), rdflib.term.Literal(u'30',
datatype=rdflib.term.URIRef(u'http://www.w3.org/2001/XMLSchema#integer')), rdflib.term.URIRef(u'http://expl.at#newgraph')]

Executai interogri SPARQL pe seturi de date RDFLib

RDFLib permite interogri SPARQL asupra seturilor de date i grafurilor stocate n obiecte Python:

S se obin toi prietenii Anei prin rularea unei interogri SPARQL pe graful friends, extras la un
exerciiu anterior din setul de date d1 (vezi ultima atribuire a variabilei g):
>>> myquery="select * where {x:Anna foaf:knows ?c}"
>>> urifoaf=rdflib.namespace.Namespace("http://xmlns.com/foaf/0.1/")
>>> myprefixes={"x":mypref,"foaf":urifoaf}
>>> temp=g.query(myquery,initNs=myprefixes)
Observai cum s-au construit interogarea i prefixele necesare interogrii. n continuare prelum
rspunsul interogrii ntr-o list Python:
>>> results=[x for x in temp.result]
>>> results
[(rdflib.term.URIRef(u'http://expl.at#Maria'),), (rdflib.term.URIRef(u'http://expl.at#Roxanne'),), (rdflib.term.URIRef(u'http://expl.at#Alin'),),
(rdflib.term.URIRef(u'http://expl.at#Andrew'),)]

S se interogheze toate afirmaiile despre Anna din tot setul de date d1. Afiai doar obiectele i graful n
care au fost gsite:
>>> myquery="select ?g ?c where {graph ?g {x:Anna ?b ?c}}"
>>> myprefixes={"x":mypref}
>>> temp=d1.query(myquery,initNs=myprefixes)
>>> results=[x for x in temp.result]
>>> results
Observai c interogrile pot fi executate att pe obiecte-graf (aici g) ct i pe seturi ntregi de date ce
pot conine mai multe grafuri (aici d1). Interogrile de scriere pot fi executate n mod asemntor, dar n
loc de funcia query() se folosete funcia update().

Creai un fiier text dintr-un set de date RDFLib


Seturile de date RDFLib pot fi stocate persistent printr-o serie de tehnici sugerate deja. Vom demonstra
aici doar stocarea lor pe discul local, n fiiere RDF:
>>> rdfstring=d1.serialize(format="nquads")
>>> print rdfstring
Aici ntreg setul de date d1 a fost convertit ntr-un string folosind sintaxa N-quads i afiat pe ecran.
>>> fileobj=open("rdffile.xml","w")
>>> d1.serialize(format="trix",destination=fileobj)
>>> fileobj.close()
Aici ntreg setul de date a fost convertit ntr-un fiier scris n sintaxa TriX i salvat pe disc. Verificai n
directorul de instalare Python apariia fiierului rdffile.xml.

Extindei cod HTML cu afirmaii i extragei-le n Python

Vom crea n continuare o pagin HTML cu afirmaii ncorporate n codul HTML folosind RDFa (un set de
atribute ce extind limbajul HTML, permind s atam afirmaii oricrui element din pagin). RDFa nu
este singura metod de a extinde cod HTML cu afirmaii. n slide-urile de la curs (exemplul cu pagina
IMDB a filmului Maleficent) am vzut o alt metod introdus de HTML 5, numit microdate care
funcioneaz pe un principiu similar, dar folosete alte atribute (itemprop, itemtype etc.). n ambele
cazuri, afirmaiile se vor extrage din codul HTML cu ajutorul unor parsere speciale numite distilatoare
(distillers).

S se scrie urmtorul cod ntr-o pagin numit page.html pe care o vei salva n directorul rdcin
Tomcat (Tomcat\webapps\Root).
<html>

<body prefix="x: http://expl.at#">


<table border="1">

<tr>
<td style="color:red">Name</td></tr>
<tr>
<td about="x:Andrew">
<span property="foaf:name">Andrew Smith</span>
<span rel="foaf:knows">
<span resource="x:Mary"></span>
<span resource="x:Anna"></span>
<span resource="x:George"></span>
</span>
</td>
</tr>

<tr>
<td about="x:Mary">
<span property="foaf:name">Mary Smith</span>
<span rel="foaf:knows">
<span resource="x:Andrew"></span>
<span resource="x:Anna"></span>
</span>
</td>
</tr>

<tr>
<td about="x:Anna">
<span property="foaf:name">Anna Smith</span>
<span rel="foaf:knows">
<span resource="x:Andrew"></span>
<span resource="x:Mary"></span>
</span>
</td>
</tr>

<tr>
<td about="x:George">
<span property="foaf:name">George Smith</span>
<span rel="foaf:knows">
<span resource="x:Andrew"></span>
</span>
</td>
</tr>

</table>
</body>
</html>

Observai c am definit doar prefixul x:, dei n cod se folosete i prefixul foaf: (pentru foaf:name,
foaf:knows dou proprieti din terminologia FOAF friend of a friend folosit la descrieri de
persoane i relaii sociale). Omiterea definirii prefixului foaf: e posibil deoarece W3C a stabilit o list cu
anumite prefixe ce nu mai trebuie declarate (cel puin n RDFa). Lista este disponibil la adresa:
http://www.w3.org/2011/rdfa-context/rdfa-1.1
Toate distilatoarele RDFa trebuie s recunoasc automat aceste prefixe i adresele de domeniu asociate
lor.

Verificai n browser dac putei deschide fiierul prin acces HTTP direct:
http://localhost:8080/page.html.

Un utilizator uman care deschide pagina n browser ar trebui s vad un tabel HTML cu o coloan de
nume:

n schimb un software (motor de cutare, plug-in de browser, aplicaie ce viziteaz pagina prin cereri
HTTP) nu va vedea doar o list de stringuri, ci o reea social care exprim faptul c unii din aceti indivizi
se cunosc ntre ei. Acest lucru este posibil doar dac aplicai software dispune de un mecanism de
distilare (extragere de afirmaii din codul HTML+RDFa)

W3C ofer un astfel de serviciu public de distilare la adresa de mai jos:


http://www.w3.org/2012/pyRdfa/#distill_by_upload+with_options

ncrcai fiierul HTML la acest serviciu, alegei Turtle ca sintax de ieire i apsai Go! Ar trebui s
primii un fiier Turtle cu afirmaiile extrase (observai cum s-a adugat prefixul foaf: chiar dac nu a fost
declarat n fiierul original):
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
@prefix x: <http://expl.at#> .

x:George foaf:knows x:Andrew; foaf:name "George Smith" .


x:Anna foaf:knows x:Andrew, x:Mary; foaf:name "Anna Smith" .
x:Mary foaf:knows x:Andrew, x:Anna; foaf:name "Mary Smith" .
x:Andrew foaf:knows x:Anna, x:George, x:Mary; foaf:name "Andrew Smith" .

Acelai distilator este disponibil i n librria RDFLib! n continuare l vom folosi pentru a obine aceleai
afirmaii n Python:
>>> distgraph=rdflib.Graph()
>>> distgraph.parse(location="http://localhost:8080/page.html", format="rdfa")
De aceast dat nu am creat un obiect de tip Dataset ci un obiect de tip Graph (nu avem mai multe
grafuri identificate).
Observaie: dac pagina HTML a folosit microdate (itemprop) n loc de atribute RDFa, la extragere
funcia parse() va folosi format=microdata

S se afieze afirmaiile extrase:


>>> contents=[x for x in distgraph.triples((None,None,None))]
>>> contents

S se afieze doar declaraiile de nume:


>>> contents=[x for x in distgraph.triples((None,rdflib.URIRef("http://xmlns.com/foaf/0.1/name"),None))]
>>> contents

S se afieze doar numele:


>>> names=[str(x[2]) for x in contents]
>>> names

Creai un site Web simplu n Python

Site-urile Web pot fi realizate n Python folosind o diversitate de framework-uri (unul dintre cele mai
puternice este Django). Vom folosi unul simplu, pentru a demonstra doar cum grafurile RDF pot fi
publicate n Web n diverse forme fie n form serializat (Turtle, N-triples etc.), fie ncorporate n cod
HTML pe care l pot vizita i utilizatori umani.

Mai nti ns trebuie s ne acomodm cu modul de creare a unui site n Python, folosind frameworkul
CherryPy, care include propriul server pentru acces prin localhost. n primul rnd vom instala librriile
CherryPy executnd urmtoarea comand n consola Windows:
easy_install cherrypy

Creai un director C:\PythonSite pentru a salva toate fiierele necesare. n acest director, vom face un
fiier de configurare cu numele config.txt cu urmtorul coninut:
[global]
server.socket_host: "localhost"
server.socket_port: 9999
[/]
tools.staticdir.root = "C:\PythonSite"
Acest fiier definete adresa i portul unde serverul CherryPy poate fi accesat. Am definit portul 9999
pentru a evita eventualele conflicte ce apar datorit serverului Tomcat instalat deja. Am declarat i
directorul principal n care vom stoca site-ul pe disc, cu staticdir.root.

n directorul C:\PythonSite creai un prim site web de test. Pentru aceasta, introducei urmtorul cod
ntr-un fiier numit test.py:
import cherrypy
class MySite:
@cherrypy.expose
def index(self):
return "This is the default page of my test site"
@cherrypy.expose
def otherpage(self,data):
return "This is another page. During access it received the value "+data
cherrypy.quickstart(MySite(),config="config.txt")

A se observa:
Acest site web este creat ca o clas Python;
Fiecare pagin a site-ului este creat ca i metod a clasei, ce preia ca argument obligatoriu
cuvntul self plus parametrii GET pe care vrem s i poat accepta pagina atunci cnd o
accesm prin metoda GET. Astfel de metode obiectuale ce au rolul de a genera la cerere pagini
Web poart nume de controllere (n filozofia MVC: model-view-controller). Aici am definit dou
pagini:
o cea implicit (index, ns numele ei nu va trebui tastat)
o otherpage (care la accesare ateapt o valoare prin GET, cu numele data);
Fiecare pagin e precedat de decoratorul expose, care asigur vizibilitatea paginii (e posibil s
avem i pagini interne inaccesibile);
Ultima linie va porni serverul CherryPy i n acelai timp va face accesibil site-ului, folosind
detaliile din fiierul de configurare.

Deschidei linia de comand Windows i navigai n directorul site-ului C:\PythonSite. Executai fiierul
test:
cd C:\PythonSite
C:\PythonSite> test.py

Atta timp ct acest fiier este n execuie, se pot trimite cereri HTTP din browser la serverul CherryPy:

La accesarea paginii otherpage trebuie s specificm i parametrul GET (data) deoarece este ateptat de
pagin. Tot ce face pagina este s concateneze acel parametru la un text:
Oprii serverul CherryPy prin Ctrl+C n linia de comand.

Creai un site Python pe baz de abloane Mako

n exemplul anterior, codul HTML se scrie sub form de stringuri n acele comenzi return, din cadrul
fiecrei pagini. Totui vom apela la o metod mai sofisticat, de tip MVC, n care codul HTML e proiectat
sub forma unor abloane (views, conform arhitecturii MVC) folosind librria Mako. O vei instala prin
executarea urmtoarei comenzi n linia de comand Windows:
easy_install mako

Creai un ablon (view) Mako pentru partea de front-end: alvai-l ca fiier myfrontend.txt tot n
directorul C:\PythonSite) cu urmtorul coninut:
I got the following value: <br/>
<strong> ${data} </strong>

Observai c un ablon Mako e un fiier text ce conine cod HTML i la unele poziii indic prin semnul $
c se vor insera n mod dinamic variabile oferite de controllerele CherryPy. Acest ablon va fi returnat n
browser n locul string-ului din exemplul anterior, mpreun cu datele necesare.

Creai un site Web nou (salvai-l ca i makotest.py n directorul C:\PythonSite) cu urmtorul coninut:
import cherrypy
import mako.template
class Site:
@cherrypy.expose
def index(self):
frontend=mako.template.Template(filename="myfrontend.txt")
return frontend.render(data="123456")
cherrypy.quickstart(Site(),config="config.txt")

Observai c de data aceasta cu return nu am mai construit un string, ci am apelat funcia de procesare a
ablonului Mako, indicnd i variabila pe care ablonul le ateapt (aceeai variabil data, de data asta
cu o valoare constant atribuit direct n cod).

Lansai noul site web prin tastarea n linia de comand Windows (din directorul site-ului web):
C:\PythonSite> makotest.py

Accesai pagina de pornire a site-ului i ar trebui s vedei rezultatul inserrii constantei data n codul
ablonului Mako:
Oprii serverul (Ctrl+C n linia de comand Windows)

Modificai site-ul web (creai un fiier makotest2.py) astfel nct s transmit ctre ablonul Mako mai
mult de o singur valoare, sub forma unei liste Python:
import cherrypy
import mako.template
class Site:
@cherrypy.expose
def index(self):
frontend=mako.template.Template(filename="myfrontend2.txt")
return frontend.render(data=[1,2,3,4])
cherrypy.quickstart(Site(),config="config.txt")

Modificai ablonul Mako (myfrontend.txt) astfel nct s afieze toate valorile primite ntr-o bucl i s
verifice dac acestea sunt numere pare sau impare, genernd cte un string corespunztor celor dou
situaii:
%for x in data:
the received value is ${x}, which is
%if x%2==0:
an even number
%else:
an odd number
%endif
<br/>
%endfor

Observai c abloanele Mako pot fi extinse cu elemente de programare (liniile precedate de caracterul
%) pentru a asigura o procesare a datelor nainte de inserarea lor n codul HTML.

Lansai site-ul web n linia de comand Windows (n cadrul directorului cu site-ul web):
C:\PythonSite> makotest2.py

Verificai n browserul web:


Creai un site care s afieze pagina HTML+RDFa folosit anterior. Presupunem c datele sunt deja
disponibile n dicionare Python

Presupunem c toate datele sunt disponibile n dou dicionare Python:


namedict conine perechi ntre indivizi i numele lor afiabile
relationdict conine pentru fiecare individ o list cu persoanele pe care le cunoate.

Vom crea site-ul web n C:\PythonSite cu numele staticdata.py:


import cherrypy
import mako.template

class Site:
@cherrypy.expose
def index(self):
namedict={"Andrew":"Andrew Smith", "Mary":"Mary Smith", "Anna":"Anna Smith", "George":"George Smith"}
relationdict={"Andrew":["Anna","George","Mary"], "Mary":["Andrew","Anna"], "Anna":["Andrew","Mary"], "George":["Andrew"]}
address="http://expl.at#"
frontend=mako.template.Template(filename="table.txt")
return frontend.render(names=namedict,relations=relationdict,pref=address)
cherrypy.quickstart(Site(),config="config.txt")

Observai cele dou dicionare iniializate direct n pagin mpreun cu adresa de domeniu pentru a
forma URI, apoi sunt transmise spre ablonul Mako ce va trebui s construiasc din ele cod HTML+RDFa.

Creai un ablon Mako (table.txt) care parcurge ntr-o bucl dicionarul cu relaii i genereaz pentru
fiecare individ o celul de tabel (cu numele) plus o serie de elemente SPAN cu relaiile sale sociale:
<?xml version="1.0" encoding="UTF-8"?>
<html prefix="x: ${pref}">
<body>

<table border="1">
<tr>
<td style="color:red">Name</td></tr>
%for individual in relations:
<tr>
<td about="x:${individual}">
<span property="foaf:name">${names[individual]}</span>
<span rel="foaf:knows">
%for knownperson in relations[individual]:
<span resource="x:${knownperson}"></span>
%endfor
</span>
</td>
</tr>
%endfor
</table>

Observai structura HTML prin care s-a extins fiecare celul de tabel:
cu ABOUT s-a indicat URI-ul individului din fiecare celul (deci subiectul afirmaiilor);
cu SPAN PROPERTY s-a declarat numele acestora, acesta fiind totodat textul vizibil din celul;
cu SPAN REL s-a declarat relaiile sociale (foaf:knows);
cu SPAN RESOURCE s-au declarat obiectele acelor relaii sociale (pe cine cunoate fiecare).
Observai c acest SPAN nu are deloc coninut, deci nu va avea nici un efect vizual n browser,
rolul su fiind doar de a stoca cunotine n pagin, ntr-un mod invizibil vizitatorilor umani.
Lansai noul site web n linia de comand Windows din directorul ce conine site-ul web (nchidei site-ul
anterior cu Ctrl+C):
C:\PythonSite> staticdata.py

Verificai site-ul n cadrul unui browser.

Verificai i codul surs HTML pentru a vedea dac s-au generat corect extensiile RDFa la codul HTML.

Reamintim c rolul extensiilor RDFa este s permit altor programe sau motoare de cutare s
neleag despre cine e vorba n pagin, i ce proprieti au acei indivizi (aici, doar cteva relaii
sociale).

Pentru a testa acest lucru, vom folosi tehnicile nvate pentru a trimite cereri HTTP din Python, spre
serverul CherryPy, pentru a colecta afirmaiile pe care le ofer prin aceast pagin. Tastai n linia de
comand Python IDLE:
>>> import rdflib
>>> g=rdflib.Graph()
Accesai site-ul web CheryPy folosind distilatorul RDFa:
>>> g.parse("http://localhost:9999",format="rdfa")
Serializai rezultatele n format N-triples i afiai-le pe ecran:
>>> distilled=g.serialize(None,format="nt")
>>> print distilled
Ar trebui s se vad afirmaiile despre relaiile sociale dintre indivizi, n format N-triples.

Modificai exemplul anterior pentru ca relaiile sociale s nu fie preluate din dicionare statice, ci s
fie interogate dinamic din Sesame

n primul rnd, trebuie s facem disponibile relaiile sociale n Sesame, pentru a fi interogate de orice
clieni ai bazei noastre de cunotine. Totui, nu vom ncrca dect urmtoarele afirmaii (disponibile n
fiierul socialnetwork.ttl):
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
@prefix x: <http://expl.at#> .

x:Mary foaf:name "Mary Smith" .


x:Andrew foaf:knows x:Anna, x:Mary; foaf:name "Andrew Smith" .
x:George foaf:knows x:Andrew; foaf:name "George Smith" .
x:Anna foaf:knows x:Mary; foaf:name "Anna Smith" .

Ceea ce lipsete sunt relaiile reciproce (x:Anna foaf:knows x:Andrew). Am stocat doar relaii
unidirecionale (x:Andrew foaf:knows x:Anna) i vom genera inversele acestora n mod dinamic, prin
aplicarea unor reguli. n acest mod pstrm baza de cunotine la o dimensiune minim necesar i
generm afirmaii noi doar cnd e nevoie de ele.

ncrcai acest exemplu n Sesame ntr-o baz de cunotine numit social. n continuare vom construi n
mod dinamic dicionarele folosite ca surs de date n exemplul precedent.

nti verificm funcionarea comenzilor necesare pentru a construi dicionarele namedict i relationdict
din exemplul anterior. O facem pas cu pas, n linia de comand Python IDLE:

Crearea adresei serviciului Sesame de unde urmeaz s extragem afirmaiile:


>>> import rdflib
>>> server="http://localhost:8080/openrdf-sesame/"
>>> command="repositories/social/statements"
Extragerea afirmaiilor ntr-un graf RDFLib (nu vom folosi o cerere HTTP configurat, accesarea direct a
ntregului coninut este suficient pentru acest caz):
>>> tempgraph=rdflib.Graph()
>>> tempgraph.parse(server+command)
Definirea prefixelor n Python:
>>> urifoaf=rdflib.namespace.Namespace("http://xmlns.com/foaf/0.1/")
>>> myuri=rdflib.namespace.Namespace("http://expl.at#")
Extragerea perechile (URI, nume) din cadrul fiecrei declaraii de nume:
>>> namemappings=[(x[0],x[2]) for x in tempgraph.triples((None,urifoaf["name"],None))]
>>> namemappings
[(rdflib.term.URIRef(u'http://expl.at#George'), rdflib.term.Literal(u'George Smith')), (rdflib.term.URIRef(u'http://expl.at#Anna'),
rdflib.term.Literal(u'Anna Smith')), (rdflib.term.URIRef(u'http://expl.at#Andrew'), rdflib.term.Literal(u'Andrew Smith')),
(rdflib.term.URIRef(u'http://expl.at#Mary'), rdflib.term.Literal(u'Mary Smith'))]
Observai c rezultatele sunt obiecte URIRef i obiecte Literal, iar noi avem nevoie s le obinem sub
forma unor stringuri simple pentru a le injecta n abloane Mako. De aceea vom aplica urmtoarea
conversie:
>>> namemappings2=[(str(x),str(y)) for (x,y) in namemappings]
>>> namemappings2
[('http://expl.at#George', 'George Smith'), ('http://expl.at#Anna', 'Anna Smith'), ('http://expl.at#Andrew', 'Andrew Smith'), ('http://expl.at#Mary',
'Mary Smith')]
Remarcai c n exerciiul anterior cheile din namedict nu sunt identificatori URI absolui ci doar sufixele
de dup semnul #. Vom aplica o simpl extragere de substring-uri pentru a le avea exact n acea form:
>>> namemappings3=[(x.rpartition("#")[2],y) for (x,y) in namemappings2]
>>> namemappings3
[('George', 'George Smith'), ('Anna', 'Anna Smith'), ('Andrew', 'Andrew Smith'), ('Mary', 'Mary Smith')]
A se observa c rpartition a descompus fiecare string la poziia unde aprea caracterul # i apoi a extras
ultima parte a rezultatului (cu indicele [2]). Prefixul este astfel ndeprtat (el rmne disponibil n
variabila myuri).
Acum vom converti aceast list de perechi n dicionarul Python dorit:
>>> namedict=dict(namemappings3)
>>> namedict
{'Mary': 'Mary Smith', 'George': 'George Smith', 'Anna': 'Anna Smith', 'Andrew': 'Andrew Smith'}
Aceasta este exact structura de date folosit pentru a stoca n Python numele din exerciiul anterior.

Acelai lucru trebuie realizat i pentru dicionarul cu relaii:


Acum vom genera relaii foaf:knows bidirecionale pentru a le putea insera n pagina web final. Pentru
aceasta, realizm o interogare CONSTRUCT local n RDFLib, fr a salva afirmaiile generate n Sesame:
>>> myquery="construct {?a foaf:knows ?b.?b foaf:knows ?a} where {?a foaf:knows ?b}"
>>> myprefixes={"x":myuri,"foaf":urifoaf}
>>> queryoutput=tempgraph.query(myquery,initNs=myprefixes)
Vom folosi rezultatul regulii CONSTRUCT pentru a construi n Python toate perechile de indivizi care se
cunosc ntre ei, n ambele direcii ale relaiei knows:
>>> knownpersonmappings=[(x[0],x[2]) for x in queryoutput]
>>> for i in knownpersonmappings:
print i
(rdflib.term.URIRef(u'http://expl.at#Andrew'), rdflib.term.URIRef(u'http://expl.at#Mary'))
(rdflib.term.URIRef(u'http://expl.at#Andrew'), rdflib.term.URIRef(u'http://expl.at#George'))
(rdflib.term.URIRef(u'http://expl.at#Anna'), rdflib.term.URIRef(u'http://expl.at#Andrew'))
(rdflib.term.URIRef(u'http://expl.at#Mary'), rdflib.term.URIRef(u'http://expl.at#Anna'))
(rdflib.term.URIRef(u'http://expl.at#Mary'), rdflib.term.URIRef(u'http://expl.at#Andrew'))
(rdflib.term.URIRef(u'http://expl.at#Anna'), rdflib.term.URIRef(u'http://expl.at#Mary'))
(rdflib.term.URIRef(u'http://expl.at#George'), rdflib.term.URIRef(u'http://expl.at#Andrew'))
(rdflib.term.URIRef(u'http://expl.at#Andrew'), rdflib.term.URIRef(u'http://expl.at#Anna'))
A se observa c acum avem relaii n ambele direcii (Mary->Anna, Anna->Mary) obinute cu
CONSTRUCT, un tip de interogare care genereaz afirmaii fr a le salva n baza de cunotine.
Rezultatele sale sunt mai departe manipulate prin programare.

Vom aplica aceleai transformri ca i n cazul numelor pentru a obine dicionarul de date final.

Convertim nti toate valorile n string:


>>> knownpersonmappings2=[(str(x),str(y)) for (x,y) in knownpersonmappings]
>>> knownpersonmappings2
[('http://expl.at#Andrew', 'http://expl.at#Mary'), ('http://expl.at#Andrew', 'http://expl.at#George'), ('http://expl.at#Anna', 'http://expl.at#Andrew'),
('http://expl.at#Mary', 'http://expl.at#Anna'), ('http://expl.at#Mary', 'http://expl.at#Andrew'), ('http://expl.at#Anna', 'http://expl.at#Mary'),
('http://expl.at#George', 'http://expl.at#Andrew'), ('http://expl.at#Andrew', 'http://expl.at#Anna')]

Apoi ndeprtm prefixele:


>>> knownpersonmappings3=[(x.rpartition("#")[2],y.rpartition("#")[2]) for (x,y) in knownpersonmappings2]
>>> knownpersonmappings3
[('Andrew', 'Mary'), ('Andrew', 'George'), ('Anna', 'Andrew'), ('Mary', 'Anna'), ('Mary', 'Andrew'), ('Anna', 'Mary'), ('George', 'Andrew'), ('Andrew',
'Anna')]

Mai trebuie realizat un pas suplimentar gruparea persoanelor cunoscute pentru fiecare individ:
Creai un dicionar cu liste goale pentru fiecare individ:
>>> relationdict={x:[] for (x,y) in knownpersonmappings3}
>>> relationdict
{'George': [], 'Andrew': [], 'Anna': [], 'Mary': []}
Apoi ntr-o bucl FOR completai aceste liste cu persoanele cunoscute obinute mai sus:
>>> for i in relationdict:
relationdict[i]=[obj for (subj,obj) in knownpersonmappings3 if subj==i]
>>> relationdict
{'George': ['Andrew'], 'Andrew': ['Mary', 'George', 'Anna'], 'Anna': ['Andrew', 'Mary'], 'Mary': ['Anna', 'Andrew']}

Acum avem toat procedura necesar construirii pas cu pas a celor dou dicionare, care n exemplul
anterior fuseser iniializate static. Punem laolalt toi aceti pai, ntr-o pagin Web Python (salvai n
fiierul dynamicdata.py):
import cherrypy
import mako.template
import rdflib
server="http://localhost:8080/openrdf-sesame/"
command="repositories/social/statements"
urifoaf=rdflib.namespace.Namespace("http://xmlns.com/foaf/0.1/")
myuri=rdflib.namespace.Namespace("http://expl.at#")

class Site:
@cherrypy.expose
def index(self):
tempgraph=rdflib.Graph()
tempgraph.parse(server+command)
namemappings=[(x[0],x[2]) for x in tempgraph.triples((None,urifoaf["name"],None))]
namemappings2=[(str(x),str(y)) for (x,y) in namemappings]
namemappings3=[(x.rpartition("#")[2],y) for (x,y) in namemappings2]
namedict=dict(namemappings3)
myquery="construct {?a foaf:knows ?b.?b foaf:knows ?a} where {?a foaf:knows ?b}"
myprefixes={"x":myuri,"foaf":urifoaf}
queryoutput=tempgraph.query(myquery,initNs=myprefixes)
knownpersonmappings=[(x[0],x[2]) for x in queryoutput]
knownpersonmappings2=[(str(x),str(y)) for (x,y) in knownpersonmappings]
knownpersonmappings3=[(x.rpartition("#")[2],y.rpartition("#")[2]) for (x,y) in knownpersonmappings2]
relationdict={x:[] for (x,y) in knownpersonmappings3}
for i in relationdict:
relationdict[i]=[obj for (subj,obj) in knownpersonmappings3 if subj==i]
frontend=mako.template.Template(filename="table.txt")
return frontend.render(names=namedict,relations=relationdict,pref="http://expl.at#")
cherrypy.quickstart(Site(),config="config.txt")

ablonul Mako creat anterior, table.txt, poate s rmn acelai deoarece va primi aceleai informaii,
de inserat n acelai mod n acelai tabel HTML. Lansai noul site web prin tastarea n linia de comand
Windows (dup ce ai nchis site-ul anterior cu Ctrl+C):
C:\PythonSite> dynamicdata.py

Ar trebui s avei acelai rezultat ca n exerciiul anterior (att pe ecran ct i codul surs HTML).

Diferena este c acum pagina Web va reaciona la schimbrile din Sesame. Pentru a verifica acest lucru,
executai n Sesame urmtoarea interogare de scriere:
PREFIX foaf:<http://xmlns.com/foaf/0.1/>
PREFIX x:<http://expl.at#>
insert data
{
x:Marianne foaf:name "Marianne Smith"; foaf:knows x:George, x:Anna
}

Dai un Refresh n fereastra browser-ului i numele Marianne ar trebui s apar n tabel, iar dac
verificai codul HTML, vei remarca i relaiile sale sociale cu George i Anna:
Creai un serviciu endpoint care ofer tot coninutul bazei de cunotine n sintaxa solicitat de client

n continuare construim o pagin Web care ofer tot coninutul bazei de cunotine social unui client, n
formatul solicitat de acesta, dintre urmtoarele: RDF/XML, N-triples, Turtle. Pentru orice alt format, se
va returna un mesaj de eroare.

Creai urmtorul site (salvai-l n directorul C:\Python\Site cu numele endpoint.py):


import cherrypy
import rdflib
server="http://localhost:8080/openrdf-sesame/"
command="repositories/social/statements"

class Site:
@cherrypy.expose
def index(self):
tempgraph=rdflib.Graph()
tempgraph.parse(server+command)
if ("Accept" in cherrypy.request.headers):
if (cherrypy.request.headers["Accept"]=="application/rdf+xml"):
response=tempgraph.serialize(None,format="xml")
return response
elif (cherrypy.request.headers["Accept"]=="text/plain"):
response=tempgraph.serialize(None,format="nt")
return response
elif (cherrypy.request.headers["Accept"]=="text/turtle"):
response=tempgraph.serialize(None,format="turtle")
return response
else:
return "your desired format is not available on this endpoint"
cherrypy.quickstart(Site(),config="config.txt")

Observaii:
Site-ul folosete metoda parse() pentru a extrage coninutul bazei de cunotine social ntr-un
graf RDFLib;
Dac site-ul este accesat printr-o cerere HTTP care solicit sintaxa dorit cu antetul Accept va
construi din graful RDFLib un fiier text (cu serialize) n formatul solicitat i l va returna ca string;
acest mecanism ns se aplic DOAR dac se solicit una din sintaxele XML, N-triples sau Turtle
(vezi tipurile MIME corespunztoare lor);
Dac site-ul este accesat prin browser se va primi mesajul your desired format is not
available..., deoarece browserele seteaz antetul HTTP pe html, sintax care nu e prevzut
n lista de variante acceptate. Am putea combina acest exemplu cu exerciiul anterior pentru ca
n caz de acces prim browser s se genereze o pagin HTML+RDFa!
Nu este nevoie s folosim abloane Mako deoarece site-ul nu este menit s fie vizitat de oameni.
El funcioneaz ca un serviciu public (endpoint) care poate rspunde doar cu grafuri RDF n
diverse sintaxe. Browserul va vedea doar un mesaj de eroare! abloanele Mako sunt necesare
atunci cnd se genereaz cod HTML.

Lansai noul site web prin tastarea n linia de comand Windows (nchidei site-ul anterior cu Ctrl+C):
C:\PythonSite> endpoint.py

ncercai s vizitai pagina cu browserul, la localhost:9999. Vei vedea mesajul de eroare.

ncercai s accesai aceeai pagin cu o cerere HTTP configurat n Python n aa manier nct s
specifice antetul Accept, cu una din valorile acceptate de pagin:
>>> import urllib2
>>> myrequest=urllib2.Request("http://localhost:9999")
>>> myrequest.add_header("Accept","application/rdf+xml")
>>> temp=urllib2.urlopen(myrequest)
>>> print temp.read()

n urma afirii, ar trebui s vedei graful n Python n sintaxa RDF/XML.

Creai un serviciu endpoint care ofer, n sintaxa dorit, toate afirmaiile despre un anumit termen

Creai urmtorul site web CherryPy (salvai-l n C:\Pythonsite cu numele endpoint2.py):


import cherrypy
import rdflib
import urllib
import urllib2
server="http://localhost:8080/openrdf-sesame/"
command="repositories/social?"
namespace="http://expl.at#"

class Site:
@cherrypy.expose
def index(self, resource):
params=urllib.urlencode({"query":"describe <"+namespace+resource+">"})
target=server+command+params
req=urllib2.Request(target)
if ("Accept" in cherrypy.request.headers):
req.add_header("Accept",cherrypy.request.headers["Accept"])
temp=urllib2.urlopen(req)
results=temp.read()
return results
cherrypy.quickstart(Site(),config="config.txt")

Diferene fa de exerciiul anterior:


De aceast dat site-ul web ateapt un parametru GET numit resource. Valoarea sa va fi
interpretat ca un termen RDF, pentru care se vor extrage din Sesame toate afirmaiile ce conin
acel termen;
De aceast dat decizia n legtur cu sintaxa rspunsului nu este luat n Python, de aceea
lipsete acea structur IF din exemplul precedent. Antetul Accept este forwardat de pagin ctre
Sesame, mpreun cu interogarea (prin intermediul req.add_header). Sesame va lua decizia i va
returna graful gata serializat n format text, n sintaxa solicitat;
Oricare ar fi rezultatele primite de la Sesame, acestea sunt doar returnate ctre client fr nicio
modificare.
n acest caz pagina Python nu face dect s intermedieze comunicarea ntre clieni i Sesame. Aceasta e
o practic uzual atunci cnd nu dorim s facem cunoscut adresa serviciul de interogare Sesame (de
exemplu pentru a mpiedica operaii de scriere). Toate cererile spre Sesame vor fi gestionate de pagina
Python, care permite doar solicitri de afirmaii despre o resurs anume.

Lansai noul site web prin tastarea n linia de comand Windows (nchidei site-ul anterior cu Ctrl+C):
C:\PythonSite> endpoint2.py

Dac dorii s l accesai n fereastra browser-ului, este nevoie s se specifice parametrul resource:

Dei n browser se vede numele individului, putei verifica n codul paginii c aceasta e de fapt cod
RDF/XML, iar browserele afieaz din orice cod XML doar nodurile text.
Reamintim c browserele solicit automat fiiere HTML, iar serviciul Sesame nu poate rspunde cu cod
HTML, aa c rspunde cu formatul implicit, care e RDF/XML.

Pentru a primi afirmaiile despre Andrew n alte sintaxe, trebuie s iniiem cereri HTTP cu valori Accept
pe care Sesame s le poat satisface. Ca i n cazul precedent, exemplificm cu o cerere trimis din linia
de comand Python:
>>> import urllib2
>>> myrequest=urllib2.Request("http://localhost:9999?resource=Andrew")
>>> myrequest.add_header("Accept","text/plain")
>>> temp=urllib2.urlopen(myrequest)
>>> print temp.read()
De aceast dat ar trebui s vedem afirmaiile despre Andrew n format N-triples.

Acest exemplu este o implementare incomplet a noiunii de derefereniere URI (URI dereferencing).
O implementare complet a mecanismului de derefereniere ar trebui s permit ca resursa Andrew s
poat fi solicitat prin adrese de forma http://expl.at/Andrew sau http://expl.at#Andrew n loc de
folosirea unui parametru GET (http://localhost:9999?resource=Andrew). Lsm aceast sarcin pentru
care care doresc s dezvolte lucrri de licen n acest domeniu.

Ca sugestie, acest mecanism ar presupune urmtoarele funcionaliti suplimentare:


Definirea unui virtualhost pentru adresa http://expl.at, astfel nct serverul (CherryPy) s
rspund la acea adres n loc de localhost;
Programarea unui dispatcher CherryPy care s extrag numele resursei din adresa URL
solicitat;
Programarea unui script care s rspund la solicitri dinspre browsere cu o pagin HTML ce
prezint unui vizitator uman afirmaiile solicitate (vezi paginile DBPedia);
Programarea paginii index astfel nct, n caz de solicitare de la un browser s redirecteze spre
scriptul care ofer HTML (punctul precedent);
Opional, se poate aduga negociere de coninut (content negotiation) prin programarea
paginii index n unul din comportamentele urmtoare:
o S rspund la orice cerere cu o list de sintaxe disponibile i apoi s atepte o a doua
cerere din partea clientului, care s specifice antetul Accept prin selecie din lista
oferit;
o S atepte mai multe antete Accept cu prioriti diferite i s aleag sintaxa cea mai
potrivit, conform unor reguli de potrivire ntre prioriti i sintaxele disponibile.