3 Aug 2011

Foodspotting Actionstream

Amidst the attention paid to new social networks, it’s easy to forget how only a few years ago the focus of everyone’s effort was bridging the gap between disparate stovepipes of personal information. While OpenSocial seems to have come to naught, some of the standards which it produced live on and are still usable in the tools which were popular during its halcyon days. True, the glory days of sites such as FriendFeed are behind them, but other, even older systems — such as MovableType — live on, at least in so far as they power blogs such as this one.

Back in 2007, when MovableType was still run by Six Apart (and when Six Apart still existed as an independent entity), WordPress had not yet completed its slow conquest of the personal blogging market. 6A’s programmers were at the forefront of OpenSocial, and productized that protocol as a feature of MT 4.1 called Action Streams. Action Streams was a pretty slick way of “syndicating” all a users’s activity at sites such as Twitter, LiveJournal, Flickr, etc, into a firehose of information that a blogger could incorporate into his or her traditional blog in a variety of ways. For its time, it was a terrific way of combining the casual social media just beginning to emerge, mostly isolated on their respective websites, with the established ‘personal brand’ which many bloggers had established on their own websites. Your digital pictures would still live on Flickr, and your tweets on Twitter, and your Buzz’s (remember Buzz?) on Google, but they would be aggregated and displayed on your own site, like so:

actionstreamsdemo.png

Action Streams was both the term for the underlying productification of Open Social, as well as a plug-in architecture that allowed new “stream” plugins to be written as new sites sprung up.

Sounds good. Unfortunately, a lot of the programming of these plugins seems to have tailed off in exact symmetry with the general demise of MovableType in the face of WordPress. Check out the dev forums — kind of tumbleweeds there.

But maybe Action Streams isn’t really dead, merely pining. I decided to see how easy it would be to adopt the underlying technology for a new social site which sprung up well after the 2007 heyday of the plugin system. This would be Foodspotting, a service that lets you upload pictures captured from a smartphone, tag them with the restaurant name and the kind of food, and then browse the entire database of others’ uploads in pursuit of items that look interesting. Architecturally, this service is kind of a very specialized Flickr, with built-in data types of restaurant and cuisine serving as inherent facets in the data. (This kind of specialized service works great as a smartphone app — the built-in controls and geolocation features let you do things with your food picture data that would be awkward or just impossible with a general-purpose system such as Picassa.)

As an example, here’s a shot I took in Ã…rhus, Denmark during the summer of 2009:

foodspotting-aspargus.png

So how do we integrate these “sightings”, in the parlance of Foodspotting, into the Action Stream that I’m displaying in the top right column on this site? While the original goal of many Open Social initiatives was a set of rich, interactive API’s which allowed you to follow and comment on your friends’ activity on many different sites, in reality we find ourselves falling back to the basic systems that are part of nearly all websites: syndication feeds such as Atom and RSS. Each user’s page on Foodspotting (example) exposes an equivalent RSS feed of pictures and metadata (example). Using this, we can approximate part of the vision for Open Social. Here’s how:

Let’s examine a sample entry from Foodspotting’s RSS feed. I took this picture yesterday at Han 202:

<item>
      <title>Spotted White Tuna Sashimi @ Han 202</title>
      <description>&lt;img src="http://s3.amazonaws.com/foodspotting-ec2/reviews/742376/thumb_600.jpg?1312330905" alt="White Tuna Sashimi @ Han 202" /&gt; &lt;p&gt;&lt;/p&gt;</description>
      <pubDate>Tue, 02 Aug 2011 17:21:45 -0700</pubDate>
      <link>http://www.foodspotting.com/reviews/742376</link>
      <guid>http://www.foodspotting.com/reviews/742376</guid>
      <media:content url="http://s3.amazonaws.com/foodspotting-ec2/reviews/742376/thumb_600.jpg?1312330905" type="image/jpeg" height="600" width="600"/>
      <media:title>Spotted White Tuna Sashimi @ Han 202</media:title>
      <media:description>. on Foodspotting</media:description>
      <media:thumbnail url="http://s3.amazonaws.com/foodspotting-ec2/reviews/742376/thumb_90.jpg?1312330905" height="90" width="90"/>
</item>

What we’d ideally like to extract from this RSS feed are: 1) when I took the picture, 2) the particular menu item, 3) the restaurant name, 4) a thumbnail (not the big picture itself) and 5) a link to the permalink on Foodspotting’s website, in case a user wants to click through to the item itself.

We can parse this feed with tools such as XPath. Inside the “config.yaml” file of our Action Stream plugin, we’ll specify:

foreach: //item
                get:
                    created_on: pubDate/child::text()
                    title: title/child::text()
                    url: link/child::text()
                    thumbnail_url: media:thumbnail/@url

We’ve almost got everything we want, with one exception: Foodspotting concatenates the name of the food and the restaurant into the “title” element, in the following way: foodname @ restaurant. I thought about splitting this apart with a regular expression, but since Foodspotting does not export the discrete links to the restaurant and the food type in the RSS anyway, I figured the benefit of having the two elements separately was pretty minimal. Action Streams usually show up as a pretty small part of the site, and it makes sense to just send interested visitors to the permalink on the main site anyway, where they can then dig deeper into the various kinds of data if they wish.

Elsewhere in our config.yaml we’ll tell the system how we want to use each of the four elements we’ve collected by parsing the RSS with XPath:

html_form: '[_1] <a href="[_3]">[_2]</a><br /><a href="[_3]"><img style="margin-top:5px; border: 1px solid black;height:100px;width:100px;" src="[_4]" /></a>' 

Not pretty, but each of those numbers within brackets, such as [_1], is serving as a placeholder for the data we’ve parsed from the RSS feed. Essentially we’re saying: USERNAME spotted FOODNAME @ VENUE, and then specifying an image below. (Luckily Foodspotting serves up a 90px square thumbnail, which is almost exactly the right size for the 100px thumbnail that most Action Stream plugins expect.)

Finally, we’ll need to let the user set up the plugin by inputting their userid. Luckily this is nothing as complicated as username and password — instead, it’s just the publicly-available unique user number which Foodspotting uses to keep track of everyone. In our config.yaml:

profile_services:
    foodspotting:
        name: Foodspotting
        url: http://www.foodspotting.com/{{ident}}
        ident_label: User ID
        ident_example: 123456
        ident_hint: |-
            You can find your User ID by clicking on the "Profile" button at the top of Foodspotting.com. The User ID is the series of six numbers before your name, without the dash: http://www.foodspotting.com/123456-john-smith

This will generate the following user interface within MovableType:

foodspotting-config.png

(Close observers will notice our nice help string, called “ident_hint” above, has failed to show up here. This is some sort of bug in MT5.x, which affects all Action Stream plugins I’ve seen.)

Now that our configuration is complete, a background task in MovableType checks for updates on the various social networks, including Foodspotting, and when it sees that I’ve uploaded a new picture, it outputs the following:

foodspotting-sashimi.png

The above is a screenshot, in case the actual entry scrolls off the page, but it shows how the thumbnail, text elements, and hyperlink all come together to point towards the actual page on Foodspotting.

There are some definite rough edges here — for one, getting Action Streams installed and working correctly is harder than it should be, there are mysterious changes between MT4 and MT5, the developer community seems to be shrinking absolutely (in a time of exponential relative growth in the social space, which is pretty shocking), and worst of all there’s the requirement to edit a crontab (!) to get MovableType to actually refresh the whole Action Streams system at all. It’s perversely charming that “crontab -e”, and the accompanying realization that your server’s default text editor is vi (!!) could could be part of a web workflow in 2011, but there you go.

In the meantime, enjoy my picture of White Tuna Sashimi, courtesy of the best intentions of web architects circa 2007.