RDFLib e RDF-Schema

Saturday, November 29, 2003

Pubblico una breve sessione con RDFLib per l'interrogazione di un paio di RDF-Schema (RDFS) sicuramente noti ai blogger più incalliti.

Per comodità ho copiato precedentemente sul mio PC entrambi gli schemi utilizzati nell'esempio, ma indicando un URL come parametro del metodo load lo schema può essere prelevato da un host remoto utilizzando il protocollo HTTP.

Partiamo con il creare un TripleStore, cioè un oggetto che andrà a contenere le triple definite dai due schemi. Fatto questo carichiamo il file RDFS di RSS 1.0, uno dei più noti formati di syndacation.

from rdflib.TripleStore import TripleStore as Store
# vocabulary for RDF e RDFS
import rdflib.constants as const 

store = Store()
store.load('schemas/rss/1.0/schema.rdfs') 

# utility function
def pr(l):
  for _ in l: print _
	

Interroghiamo l'oggetto TripleStore richiedendo tutte le classi definite da RSS.

s = store.subjects(const.TYPE, const.RDFS_CLASS)
pr(s)
http://purl.org/rss/1.0/textinput
http://purl.org/rss/1.0/item
http://purl.org/rss/1.0/image
http://purl.org/rss/1.0/channel
	

Le classi sono poche e per nulla interessanti. Carichiamo quindi uno schema più corposo, la versione 0.1 di Friend of a Friend (FOAF).

store.load('schemas/foaf/0.1/schema.rdfs') # merge triples
s = store.subjects(const.TYPE, const.RDFS_CLASS)	
pr(s)
http://purl.org/rss/1.0/item
http://purl.org/rss/1.0/textinput
http://xmlns.com/foaf/0.1/Person
http://xmlns.com/foaf/0.1/OnlineChatAccount
http://purl.org/rss/1.0/channel
http://xmlns.com/foaf/0.1/Agent
http://xmlns.com/foaf/0.1/OnlineEcommerceAccount
http://xmlns.com/foaf/0.1/Organization
(...)
	

Da notare come RDFLib abbia automaticamente effettuato la fusione delle triple presenti nei due schemi e l'uso di URI univoci eviti spiacevoli collisioni tra i due vocabolari. Continuiamo chiedendo ad RDFLib di elencare le possibili proprietà della classe FOAF chiamata Person.

from rdflib.Namespace import Namespace	
foaf = Namespace('http://xmlns.com/foaf/0.1/')

props = store.possible_properties(foaf['Person'])
pr(props)	
http://xmlns.com/foaf/0.1/firstName
http://xmlns.com/foaf/0.1/knows
http://xmlns.com/foaf/0.1/homepage
http://xmlns.com/foaf/0.1/mbox_sha1sum
http://xmlns.com/foaf/0.1/mbox
(...)
	

Il metodo possible_properties non si limita di fare un semplice lookup delle propietà definite esplicitamente in Person, ma include le propietà ereditate dalla classe. Visto che Person è una sottoclasse di Agent RDFLib include le proprietà di quest'ultima tra quelle di Person.

Infine RDFS permette di associare una etichetta ed un commento ad ogni classe o propietà definita nello schema, i metodi comment e label ci permettono di recuperare tali valori.

print store.comment(foaf['Agent'])	
An agent (eg. person, group, software or 
physical artifact).

print store.label(foaf['mbox'])
personal mailbox
	

Il Web Ontology Language (OWL) definisce in modo ancora più preciso cardinalità e relazioni tra classi, sottoclassi e proprietà. Attualmente RDFLib non fornisce tali funzionalià in modo nativo ma, visto che OWL segue lo stesso modello "a triple" definito da RDF, è possibile utilizzare l'API di RDFLib per eseguire query che comprendano le informazioni specificate da OWL.


Python, Webware & Cheetah

Wednesday, November 19, 2003

Cheetah è uno dei motori di templating più potenti e flessibili disponibili per Python. Un template Cheetah può essere molto complesso ed esistono svariati modi di utilizzarlo per generare pagine HTML accoppiandolo ad uno dei tanti web framework del pitone.

L'esempio che riporto di seguito è un utilizzo minimale e piuttosto atipico di Cheetah, ma che credo si adatti molto bene allo sviluppo di pagine web altamente dinamiche, dove solitamente la logica di presentazione dei dati ricopre un ruolo importante. Vista l'ottima integrazione di Cheetah con Webware, impiegherò quest'ultimo per sviluppare gli esempi mostrati in questo post.

Cominciamo col vedere il sorgente di SitePage, una servlet Webware che verrà poi estesa dalle altre pagine del nostro ipotetico sito.

from Cheetah.Template import Template
from WebKit.Page import Page

class SitePage(Page):
  """ Base site servlet """

  _template = None

  def title(self): 
    """ Title of the page on top 
	  of the browser window """
    return 'My Cool Website'

  def htTitle(self):
    """ Title of the page, 
      possibly with markup """
    return 'My <strong>Cool</strong> Website'

  def content(self):
    """ Page content, sub-classes 
      should override """	
    return '[content placeholder]'

  def footer(self):
    """ Page footer """	
    return 'Copyright © 2003 XYZ Corp.'

  def writeHTML(self):
      
    request = self.request()
    # load the template once
    if not SitePage._template:
      SitePage._template = 
        Template(file=request.serverSidePath('tmpl.html'))

    # pass caller servlet as namespace
    SitePage._template._ = self
    # let template to output the HTML page
    SitePage._template.respond(self.transaction()) 
	

La classe SitePage definisce una serie di metodi dal nome familiare: content, footer, title. Questi metodi restituiranno il codice HTML necessario per popolare il template Cheetah definito esternamente e creato con un qualsiasi editor di pagine web.

Tipicamente solo l'implementazione di content verrà poi ridefinita dalle sottoclassi di SitePage. Il file tmpl.html contiene il layout che accomuna le pagine dell'intero sito. Tale file una volta in memoria verrà compilato da Cheetah e ad ogni richiesta verranno invocati i metodi della servlet chiamante, i quali andranno a sostituire con del markup i placeholder presenti all'interno del file di template.

I placeholder sono identificati dal nome dei metodi e vengono risolti da Cheetah in modo automatico. La servlet che richiama il template passa se stessa a quest'ultimo tramite un binding al simbolo _ (underscore). Il pattern per identificare un metodo della servlet diventa quindi $_.metodo.

Di seguito viene presentato il codice di una sottoclasse di SitePage chiamata index che avrà come unico metodo content, che costituisce il contenuto principale della pagina.

from SitePage import SitePage
from datetime import date

class index(SitePage):
  """ Home page for my site """

  def content(self):
    return "<p>Hi there, it's %s</p>" % date.today()
	

Come si evince dell'esempio è molto semplice "iniettare" codice HTML all'interno del template. Alcuni moduli come htmlgen o SimpleHTMLGen, rendono ancora più "pythonic" generare markup HTML. In particolare utilizzando SimpleHTMLGen l'esempio sopra può essere riscritto in:

from SitePage import SitePage
from datetime import date
from SimpleHTMLGen import html

class index(SitePage):
  """ Home page for my site """

  def content(self):
    return html.p("Hi there, it's %s" % date.today())
	

SimpleHTMLGen si occupa di chiudere correttamente tutti i tag (secondo la sintassi XHTML, non HTML) e di effettuare l'escape dei caratteri speciali. Un esempio che dimostri in maniera più convincente l'utilità di questo modulo è la generazione di una lista (UL) di link:

from SitePage import SitePage
from SimpleHTMLGen import html

class cool(SitePage):
  """ Show a list of cool links """

  def content(self):
    # link's text and URL
    data = [('Spam', 'http://www.foo.com/'), 
	  ('Eggs', 'http://www.bar.com/')]
	  
    return html.ul(c=[html.li(html.a(text, href=url)) for text, url in data])	
	

È facile immaginare una serie di estensioni a questo meccanismo. Una potrebbe essere quella di parametrizzare il nome del template in modo che sia ridefinibile dalle sottoclassi di SitePage senza essere costretti a riscrivere il metodo writeHTML. Ma questa sarà materia per un altro post.

Per concludere, ritengo che utilizzare Python per la generazione di codice HTML anziché affidarsi ai mini-linguaggi presenti in molti sistemi di templating sia l'approccio da preferire, visto che la logica associata alla presentazione dei dati tende a diventare più complessa con il crescere del sito. Avendo cura di generare solo il codice HTML strettamente necessario e delegando i dettagli estetici ai fogli di stile (CSS) si riesce ad avere codice più leggibile e manutenibile.

Aggiornamento: il file pwc-src.zip contiene i sorgenti dell'ultimo esempio proposto.


C# 2.0

Monday, November 3, 2003

Il Microsoft Professional Developer Conference di Los Angeles è da poco terminato e le prime informazioni sulle novità del linguaggio cominciano a circolare sul web.

Inizialmente pensavo di scrivere un lungo post dilungandomi sui Generics, su yield return, sui Partial Types ed i metodi anonimi, facendo notare come queste funzionalità fossero eleganti rattoppi o idee rubate ad altri linguaggi senza sponsor miliardari... ma poi ho deciso di non addentrarmi in questioni tecniche. Il perché di questo ripensamento è presto detto: