PIM Example

Volume 8, Issue 163; 16 Dec 2005; last modified 08 Oct 2010

An example of how I use the vCard ontology and all this XML+RDF stuff.

I find that a great part of the information I have was acquired by looking up something and finding something else on the way.

Franklin P. Adams

A couple of people expressed interested in my XML+RDFpersonal information manager” setup. Dave Pawson wants to see some examples and someone else wants to know why I bother with all the RDF. I presented these ideas at Extreme Markup Languages 2002, but some of the details have changed, so I think it's probably worth running through it again.

The main points are: a lot of the actual entries in my PIM are related to each other (hotels are related to conferences are related to trip itineraries; individuals are related to each other; meetings on my calendar take place at facilities in my address book; teleconferences use phone numbers in my address book; etc.). A PIM represents a model of the real world and human beings are really good at making inferences about the real world. Well, RDF can easily express the relationships in my PIM, RDF makes aggregation easy, and RDF can be used to make inferences in the model. Win, win, win, from my point of view.

To make the explanation interesting, let's work through a concrete example. Let's say I have two friends, Dick and Jane; they're married, they have a child. We can draw lots of inferences from this information, but let's see if we can teach the machine to do it for us. Along the way, we'll avoid the evils of duplicated data.

Our example begins with the XML data that comes straight off my Sidekick. Here's a fragment of the entry for Dick:

<contact id='_1109'>
  <category>Friends</category>
  <first_name>Richard</first_name>
  <last_name>Smith</last_name>
  <addresses>
    <item>
      <city>Anytown</city>
      <zip>01010</zip>
      <label>Work</label>
      <state>MA</state>
      <street>123 Corporate Plaza</street>
    </item>
  </addresses>
  <nick_name>Dick</nick_name>
  <notes>rdf:
p:livesWith #jane-jones
p:child #robbie-jones
a g:Male</notes>
  <id>1109</id>
  <phone_numbers>
    <item>
      <number>+1-413-555-1234</number>
      <label>Work</label>
    </item>
  </phone_numbers>
  ...

We can see that Dick has the information you'd expect in a PIM: a work address (but importantly, not a home address), a phone number, etc. There's also a notes field where I've encoded a little bit of RDF in a subset of N3. That's how I add arbitrary additional metadata to each entry. That'll be important in just a little bit.

The first step in our journey is to transform this data into something more palatable. The little bit of Python that does this extracts the RDF encoded in the notes and makes certain other adjustments (for example, for appointments that are classified as flights, it extracts departure and arrival airports, carrier, and flight number data from the title field). The fragment above comes out like this:

<contact id="_1109">
  <category>Friends</category>
  <firstname>Richard</firstname>
  <surname>Smith</surname>
  <nickname>Dick</nickname>
  <phones>
    <phone label="Work">+1-413-555-1234</phone>
  </phones>
  <addresses>
    <address label="Work">
      <street>123 Corporate Plaza</street>
      <city>Anytown</city>
      <state>MA</state>
      <postcode>01010</postcode>
    </address>
  </addresses>
  <notes>rdf:
p:livesWith #jane-jones
p:child #robbie-jones
a g:Male</notes>
  <p:livesWith rdf:resource="http://norman.walsh.name/knows/who#jane-jones"/>
  <p:child rdf:resource="http://norman.walsh.name/knows/who#robbie-jones"/>
  <rdf:type rdf:resource="http://nwalsh.com/rdf/genealogy#Male"/>
</contact>

From here, it's an easy step to RDF. This is where the vCard markup comes into play. In fact, I could (maybe should) do this transformation in two steps: going from my address book to vCard and then from vCard to more “colloquial RDF”. If I was really starting with vCards that's what I'd do. But since I'm starting with my own markup, I combine the two steps, producing colloquial RDF directly. The advantage of colloquial RDF is that it uses shared vocabularies and is therefore more amenable to reuse and aggregation. Here's a fragment of Dick's entry in RDF:

<foaf:Person rdf:about="http://norman.walsh.name/knows/who#dick-smith">
   <rdf:type rdf:resource="http://nwalsh.com/rdf/contacts#Contact"/>
   <c:category>Friends</c:category>
   <foaf:firstName>Richard</foaf:firstName>
   <foaf:surname>Smith</foaf:surname>
   <foaf:name>Richard Smith</foaf:name>
   <foaf:nick>Dick</foaf:nick>
   <c:workPhone rdf:resource="tel:+1-413-555-1234"/>
   <v:workAdr rdf:parseType="Resource">
      <rdf:type rdf:resource="http://nwalsh.com/rdf/vCard#Address"/>
      <v:street-address>123 Corporate Plaza</v:street-address>
      <v:locality>Anytown</v:locality>
      <v:region>MA</v:region>
      <v:postal-code>01010</v:postal-code>
      <v:country-name>US</v:country-name>
   </v:workAdr>
   <p:livesWith rdf:resource="http://norman.walsh.name/knows/who#jane-jones"/>
   <p:child rdf:resource="http://norman.walsh.name/knows/who#robbie-jones"/>
   <rdf:type rdf:resource="http://nwalsh.com/rdf/genealogy#Male"/>
   ...</foaf:Person>

Now we've got data we can really use. Before we get started, let's take a look at the extra bits of RDF associated with Jane:

<foaf:Person rdf:about="http://norman.walsh.name/knows/who#jane-jones">
   ...
   <c:notes>rdf:
p:spouse #dick-smith
p:child #robbie-jones
p:anniversary "1990-09-19"
a g:Female</c:notes>
   <p:spouse rdf:resource="http://norman.walsh.name/knows/who#dick-smith"/>
   <p:child rdf:resource="http://norman.walsh.name/knows/who#robbie-jones"/>
   <bio:event>
      <bio:Marriage>
         <bio:date rdf:datatype="http://www.w3.org/2001/XMLSchema#date">1990-09-19</bio:date>
      </bio:Marriage>
   </bio:event>
   <rdf:type rdf:resource="http://nwalsh.com/rdf/genealogy#Female"/>
</foaf:Person>

Note that I've expressed the “p:anniversary” relationship (which is easy to type in a note) using a more standard vocabulary.

As we can see, Dick lives with Jane; Dick has a child, Robbie; and Dick's a guy; Jane is married to Dick; Robbie is Jane's biological child too; Jane's anniversary is 19 September 1990; and Jane's a girl.

Now, what are some inferences that we could make? (I'm going to use cwm so I'm using N3 notation for the inference rules.)

Well, for one thing, we know that “lives with” is a reflexive relationship:

{ ?p p:livesWith ?q } => { ?q p:livesWith ?p } .

The way you read that is: considering all individuals, if there's some individual “?p” that has the relationship “p:livesWith” some other individual “?q”, that implies that “?q” has the relationship “p:livesWith” “?p”. (There's no relationship between the “p:” namespace prefix and the variable name “?p”, that's just an unfortunate coincidence.)

In fact, “lives with” is more than reflexive, it's transitive as well:

{ ?p p:livesWith ?q .
  ?r p:livesWith ?q .
  ?p log:notEqualTo ?r } => { ?r p:livesWith ?p } .

(Because “lives with” is both reflexive and transitive, the last line of that rule avoids “living with” ourselves.)

Spouse is a kind of “significant other” relationship:

{ ?p p:spouse ?q } => { ?p p:sigother ?q } .

And “significant other” is also a reflexive relationship:

{ ?p p:sigother ?q } => { ?q p:sigother ?p } .

Marriage anniversaries are reflexive too.

{ ?p p:spouse ?q .
  ?p bio:event ?b .
  ?b a bio:Marriage . } => { ?q bio:event ?b } .

The inference that one draws from “living together” is that the parties involved share a home address and (ignoring for the moment teenagers with their own phones) a home phone number:

{ ?p p:livesWith ?q .
  ?q v:homeAdr ?r } => { ?p v:homeAdr ?r } .

{ ?p p:livesWith ?q .
  ?q c:homePhone ?r } => { ?p c:homePhone ?r } .

I also store genealogical data in my address book. We can make some inferences in that space too about mothers and fathers and siblings:

{ ?p p:mother ?q } => { ?q p:child ?p } .

{ ?p p:father ?q } => { ?q p:child ?p } .

{ ?p p:father ?q .
  ?r p:father ?q .
  ?p log:notEqualTo ?r } => { ?p p:sibling ?r } .

{ ?p p:mother ?q .
  ?r p:mother ?q .
  ?p log:notEqualTo ?r } => { ?p p:sibling ?r } .

{ ?p p:child ?q .
  ?p a g:Female } => { ?q p:mother ?p } .

{ ?p p:child ?q .
  ?p a g:Male } => { ?q p:father ?p } .

One piece that's missing so far is the address book entry for Robbie. Now, in fact, Robbie is only 10 and I don't really need an entry in my address book for him. I might someday, but not now. So I have an extra little file where I store such entries:

kwho:robbie-jones a foaf:Person; a c:Contact;
  c:category "Friends";
  foaf:firstName "Robert";
  foaf:surname "Jones";
  foaf:name "Robert Jones";
  foaf:nick "Robbie";
  bio:event [
    a bio:Birth;
    bio:date "1995-05-05"^^xs:date ];
  a g:Male;
  p:livesWith kwho:dick-smith .

All that's left now is to shake well, stir, and serve hot!

It's from this data that I actually generate the HTML PIM that I use when I'm working on my computer. Here's the entry for Dick:

Richard's address book entry in HTML.

Note, in particular, that Dick has a home address and a home phone number and I didn't have to duplicate that data in my actual address book. Dick is also related to Jane:

Jane's address book entry in HTML.

and Robbie (who are, in turn, related to each other and Dick):

Robbie's address book entry in HTML.

I use this aggregated data in other ways too. For example, whenever I start a terminal session, I get a reminder about today's schedule. On July 5, I'll see something like this:

Schedule for 05 Jul 2006

              Coming up:
............. Richard Smith's Birthday (06 Jul 2006; tomorrow)

11:00a-12:00p XML Core
02:00p-03:00p XML CG

That data is generated from my PIM. The fact that Dick's entry in my address book has a birthday associated with it automatically generates an event in my calendar.

And that's the sort of added value that makes XML+RDF worth it for me.

Comments

Some of your rules follow a standardized idiom. For example:
{ ?p p:livesWith ?q } => { ?q p:livesWith ?p } .
You can just say...
p:livesWith a owl:SymmetricProperty.
... provided you tell cwm about owl:SymmetricProperty with this rule...
{ ?prop a owl:SymmetricProperty.
 ?p ?prop ?q } => { ?q ?prop ?p } .
A nice thing about that is that while N3 rules aren't (yet ;-) standardized, OWL is, so now all the OWL tools grok more about p:livesWith, and the p:livesWith a owl:SymmetricProperty. statement only uses the turtle part of N3, which is much more widely supported. e.g. pellet groks turtle and OWL. owl:TransitiveProperty is also standardized. Can't remember if reflexive is standardized... nope; I don't see it in the OWL reference. Likewise, this rule follows an RDFS idiom:
{ ?p p:spouse ?q } => { ?p p:sigother ?q } .
You can just say...
p:spouse rdfs:subPropertyOf p:sigother.
and tell cwm what that means thusly:
{ ?X [ rdfs:subPropertyOf ?PROP] ?Y } => { ?X ?PROP ?Y }.
The semantics of RDFS are completely expressible in N3 rules. swap/util/rdfs-rules is a careful transcription of the RDFS spec into N3. You can't completely capture OWL in N3 rules, but you can express quite a useful bit of it. Jos developed owl-rules.n3, though if you feel all those rules to cwm, it'll probably head into the weeds chasing infinite deduction chains more often than not, so you sorta have to pick and choose. Jos's Euler does better, since it's a backward-chaining reasoner.
—Posted by Dan Connolly on 17 Dec 2005 @ 12:43 UTC #

Thanks Norm. This usage does far more (for me) than all the stuff on RDF I've read which, like Uche, has simply had me shrugging.

I'm getting an impression of little piles of classes, namespace lists (extra.n3 etc) and local semantics, some re-used (foaf, dc g), others locally generated (contacts, genealogy). Do they make up what you seem to be calling 'colloquial RDF'? How far is it from what you have to SW RDF? Just a transform?

Shelly's reference to http://www.w3.org/2005/Talks/1214-Trento-IH has made a difference for me. Merging (I guess thats one of your 'wins') is a key item I'm intrigued by.

I'm curious what you are editing with? Emacs I guess, but any major mode for your ownl and N3?

Again. Much appreciated.

DaveP

—Posted by Dave Pawson on 17 Dec 2005 @ 01:37 UTC #

Maybe it's offtopic, but i just wanted to say, that it's really interesting to read everything this with comments... You discuss here a lot of interesting things on useful themes. Thanks for that.

—Posted by Susan on 17 Dec 2005 @ 11:12 UTC #

Hello Norm, I took your inspiration onto the Mac and reworked some stuff. It's not that complete but it will take your address book on the web.

—Posted by Christoph Görn on 20 Dec 2005 @ 08:15 UTC #

This is perfect--it's exactly what I have in mind for managing my growing contact list. I notice that you use 'colloquial' RDF; aren't there already URIs for address, contact, etc?

—Posted by Steve on 07 Dec 2006 @ 07:20 UTC #