Browsing your LinkedIn Connections by Industry

Posted in JSAPI, LinkedIn, Uncategorized on March 9th, 2011 by admin – 3 Comments

I presented at the Semantic Web Meetup in San Francisco about this topic, slides for that talk can be found here.

A friend of mine, who runs a rather cool website, asked me to help him understand how to create an industry-based query to see which of your connections are in a particular industry.  As this was a slightly trickier-than-usual problem, I put together an example for him to use, and I’ll go over how it works here.

But first, the obligatory working demo:

And the code. Note there are a couple of support files you’ll need to snag as well.

In order to keep this post manageable, I’ll mention that most of the pieces for this are included in my StreamIn’ tutorial. So if you’re interested in understanding how the basic framework works, take a look there. We’re basically going to break out on “Add Share Stream”.

Industries

One of the reasons that this example is somewhat tricky is that the API doesn’t currently have a way to give you a list of industries, and how they’re related, but we do have a web document (which is fairly static) with that information. So I grabbed the HTML from that page, fiddled with it in my editor, and created a JSON document with a hash of industries (and the groups they map to) and groups (and the industries they contain). You can see that file here. Loading that file as a javascript script gives the page access to hashes of this information.

Autocomplete

Hey, since we’ve made this handy hash, it’s a cinch to provide a jQuery autocomplete for the industries.

	var data = Object.keys(industry_names);
	$("input#Industry").autocomplete({
	   source: data,
	   select: function() { getConnections() }
	});

Titles is not really a bounded set of this type, so I’ll just let the user input that freestyle.

Getting the connections

The logic I wanted to use was… find all my 1st or 2nd degree connections in the industry who match the title. If there are fewer than 25 of those, do another search for all of the industries in the industry group. You can see how that stacks in the code, so I’ll just talk through the actual call for the first search.

	$("#connections").html("");
	var industrynum = industry_names[$("#Industry").val()]["code"]
	var title = $("#Title").val();
	path = "/people-search:(people:(id,first-name,last-name,public-profile-url,
                  three-current-positions:(title,company:(name,industry)),distance,picture-url))?
                  sort=distance&facet=industry," + industrynum + "&title=" + title + "&current-
                  title=true&facet=network,F,S&count=25"

So, clear the div we’re gong to use. Grab the code from the industry hash based on what’s in the Industry user input field. Pull the title from the Title input field.

In this case we’re using the People Search resource with some field selectors to request specific profile fields for the users who are returned. That’s this part:

people-search:(people:(
                     id,first-name,last-name,public-profile-url,
                     three-current-positions:(title,company:(name,industry)),
                     distance,picture-url))

Why three-current-positions? Because I can’t just ask for one current position, and this list will be smaller and faster than all the positions.

We also need some very specific filters on our search, so we’re using facets (also covered on the People Search page) to get us only the members in our network who have the right title and industry. Let’s go over those things individually:

  • sort=distance : distance from me, i.e. degree (first or second)
  • facet=industry,xx : I want only users who are working in this industry (like Entertainment or Internet)
  • title=yyy : This is a fuzzy string match. Doesn’t have to match exactly.
  • current_title=true : Don’t tell me about their past positions. I want people doing this right now
  • facet=network,F,S : First and Second degree connections only.
  • count=25 : I don’t have all day and I’m only going to show 25 total results in any case.

All right, now that that’s settled, let’s make the call. The Raw API call we’re making reaches directly through to the back end REST API.

	IN.API.Raw(path)
	  .result(function(result) {
	    var count = result.people.values.length;
	    var html = $("#connections").html();
	  	for (var index in result.people.values) {
	  		var person = result.people.values[index]
	  		html += "<div class=\"connectionitem\">";
	  		if (person["pictureUrl"]) {
		  		html += "<img src= \"" + person["pictureUrl"] +
                                              "\"  width=40 align=right/>";
			}
			var distance = (person["distance"] === 1 ? '1st' : '2nd')
	  		html += "<a href=\"" + person["publicProfileUrl"] + "\"><h3>" +
                                      person["firstName"] + " " + person["lastName"] +
                                      " (" + distance + ")</h3></a>";
	  		html += person["threeCurrentPositions"]["values"][0]["title"] +
                                    ", " + person["threeCurrentPositions"]["values"][0]["company"]["name"] +
                                   " (" + person["threeCurrentPositions"]["values"][0]["company"]["industry"] +
                                   ") </div>";
	  	}
	  	$("#connections").html(html)
	  });

Just as in the other tutorials, the JSAPI method (in this case IN.API.Raw()) is called with chained methods indicating how it should be executed. In this case, we just want to perform a GET on the resource, and process the results, so .results is the only thing we’re setting.

Just like in the StreamIn’ tutorial, the results are iterated over person by person and a collection of connectionitems is gathered together, made pretty, and plopped in the “connections” div.

Adding HTML5 Local Storage to Your Web App

Posted in Uncategorized on February 25th, 2011 by admin – 2 Comments

One of the things we’re working on in the Developer Relations group at LinkedIn is creating tools to help our developers work through issues and questions quickly and easily. The OAuth authentication piece is frequently tricky, so we put together an OAuth Test Console, based on a similar tool from JR Conlin.

It’s a handy tool, and certainly makes OAuth debugging much easier, but as a frequent user of the console myself, I found myself quickly getting tired of copying and pasting my consumer key, secret, oauth token, token secret into the fields every time I wanted to use the console. As a developer/user of the console, though, I certainly didn’t want to have that informations saved in a cookie which would go across the wire every time I used the tool, sharing my secret unencrypted keys.

HTML5 local storage to the rescue!  This, my friends, is just the kind of thing you can do with the new HTML5 local (browser) storage functionality.  Store the information in the user’s browser, and check for/use it when you find it there.  The functions are easy to use and implement, so I’ll go over the pieces I used to add this to our console.

Step 1: Don’t tease the IE6 users

All of the major browsers support this functionality in their newest release.  However, not everyone has a fancy, shiny new browser to work with, and telling them you have functionality they can’t have is just mean.  The first thing you should do is make sure that the buttons only show up if they’re going to actually do something.

So, I added a <div> with the store/clear buttons, but set the display to none by default:

<style>
   #storetokens { display: none; }
</style>
<div id="storetokens">
    <button id="store" onclick="storeTokens(); return false;">Store Token Info</button>
    <button id="store" onclick="clearTokens(); return false;">Clear Token Info</button>
</div>

Now that we’ve got that in place, let’s check to see if the browser knows how to store things:

<script>
   if (supportsHTML5Storage()) {$("#storetokens").show();}
</script>

Now we have buttons for storing and clearing the tokens from the browser storage, but only users who can use it will see it.

Step 2:  Store the Tokens

We’re calling a storeTokens() function when the button is clicked – let’s see what that should look like.

Once we ask the user to confirm the action, we just set the values in localStorage based on the values of the fields in the form.  Note that I’m using JQuery here to make it easier to grab the selectors, but this works just as well with raw Javascript.

function storeTokens() {
    var storeConfirm = confirm("Do you want to store your tokens and secrets in your browser's local storage?")
    if (!storeConfirm) { return; }

    localStorage["apiKey"] = $("#apiKey").val();
    localStorage["apiSecret"] = $("#apiSecret").val();
    localStorage["memberToken"] = $("#memberToken").val();
    localStorage["memberSecret"] = $("#memberSecret").val();
}

Step 3:  Clear the Tokens

Anytime you give the user the chance to store some sensitive information, you want to give them the ability to get it back out.  There are various reasons for this, but just trust me – it’s only polite.

So we have a clearTokens() function which is very similar to the storeTokens() function

function clearTokens() {
    var clearConfirm = confirm("Do you want to clear your tokens and secrets from your browser's local storage?")
    if (!clearConfirm) { return; }

   localStorage.removeItem("apiKey")
   localStorage.removeItem("apiSecret")
   localStorage.removeItem("memberToken")
   localStorage.removeItem("memberSecret")
}

Step 4: Retrieve the Stored Data

Great.  Now the user can store and clear the token information.  There’s something missing, though.  Until the form actually takes advantage of the stored data, we’re engaging in a purely academic exercise.  So, when the browser loads, we should populate the fields.

function populateFields() {
    if (localStorage.getItem("apiKey")) {
        $("#apiKey").val(localStorage.getItem("apiKey"))
  	$("#apiSecret").val(localStorage.getItem("apiSecret"));
	$("#memberToken").val(localStorage.getItem("memberToken"));
        $("#memberSecret").val(localStorage.getItem("memberSecret"));
    }
}

That’s It

That’s pretty much all there is to getting HTML5 storage working in modern browsers.  Storing secret information that a user never needs to send across the wire is a great reason to use this functionality.  For users who can use it, we’ve given them a way to save themselves some extra work when they want to use the tool. As you can see, the functionality is really quite easy to implement.  You can see the code in action (and take it to use for your own) - OAuth Test Console.

Creating an Application using LinkedIn Platform in 3 Easy Steps!

Posted in API, JSAPI, LinkedIn on February 17th, 2011 by admin – 2 Comments

The LinkedIn API has all sorts of juicy goodness to it, but the authentication and structure take some learning. LinkedIn has created a JS API called Connect to help you get around those things, so that you can focus on the logic and presentation of your application without spending too much time considering the back end.

I made an internal presentation covering this material at LinkedIn on 2/18, which is available in PDF format. This tutorial has also been integrated into the general JSAPI tutorials on the developer portal. That version has a lot less narration and is just about getting the code written and working.

Everyone loves activity streams, so to illustrate the application development process I’m going to walk through the steps needed to create an interactive LinkedIn application. StreamIn’ has a profile badge for the logged in member, a stream of the status updates from members in their network, and the ability to like/unlike particular items.

A note on this tutorial – there is detailed documentation for the JSAPI on the LinkedIn Developer Portal – there are links in the text appropriate, but detailed options aren’t covered here to keep the tutorial reasonably sized. It’s a lot more fun to get started in a framework if you’ve built something from start to finish, and that’s what we’ll do here.

Getting Started

To get started on our exercise, we need a shell for the application.  Here we have a basic layout, with places for the badge and stream to be inserted.

<div id="header"><h1>StreamIN'</h1></div>
  <div id="wrapper">
    <div id="profile">
	    <div id="badge">
	    Profile info goes here!
	    </div>
    </div>
    <div id="stream">
        Stream stuff goes here!
    </div>
    <br clear="both">
    <div id="footer">
    <div id="nav"> [ << ] <a href="Step1.html">[ >> ]</a></div>
</div>

We’ll start with this and build the application in stages.

Step 0: Check out the Console

The Connect framework does all of the authentication work for you, and loads the things you need in order to get things working in your application.  There are various ways to present login options for the member, and you can explore them using the new JSAPI Console.  At the very least you’ll want to explore the Login options (Login Button, Login Button with Events, Login Button Label). The console is an incredibly useful tool!  Take a few moments now to look around and see what it has to offer.  You can even edit the Code section and click “Run” to see how the Results change.

Step 1: Get the Profile

In order to show the user’s profile, we’ll need to do a few things.

  • Import the framework
  • Add a login button
  • Add an API call to get the profile data and display it

I know, that seems like a lot of sub-steps, but it’s really very easy.

Import the Framework

The first thing we need to do when using Connect is get the framework in our page.  In the <head> of the document, we’ll add a small script to grab the framework.  To do this you need to get a LinkedIn API key – if you don’t have one, go ahead and get one and then come back – it doesn’t take long and it’s much more fun to play along than just read about what I did.

Here’s your code. Use your own api_key. “authorize:true” tells the framework to authorize the member if they’ve visited your application before, rather than logging in each time.

<script type="text/javascript" src="http://platform.linkedin.com/in.js">
   api_key: API_KEY
   authorize: true
</script>

Add a Login Button

Let’s add the button at the bottom of the page.  Here’s the code.  No really, this is all there is!  The data-onAuth variable tells the framework to fire the loadData function when the member has been authorized.

<script type="IN/Login" data-onAuth="loadData"></script>

Load the User’s Profile

Here’s the meat of the step – now the framework is in place, so it’s possible to make calls on behalf of the member. When the member has been authorized the loadData() function will be called. This function calls the Profile method. We need a couple of extra fields from that call, so we use .fields to tell the call what to request.  And the .result sets a callback to perform as soon as the call returns.  You can put an anonymous function there (as I have here) or give it the name of another function to process your returned data.

The callback in this case just pulls the user’s information from the result and builds a profile badge, inserting it in the badge <div>.

function loadData() {
// we pass field selectors as a single parameter (array of strings)
IN.API.Profile("me")
   .fields(["id", "firstName", "lastName", "pictureUrl","headline"])
   .result(function(result) {
      profile = result.values[0];
      profHTML = "<p><a href=\"" + profile.publicProfileUrl + "\">";
      profHTML +=  "<img align=\"left\" src=\"" + profile.pictureUrl + "\"></a>";
      profHTML +=  "<a href=\"" + profile.publicProfileUrl + "\">";
      profHTML +=  "<h2>" + profile.firstName + " " + profile.lastName + "</a> </h2>";
      profHTML += "<span>" + profile.headline + "</span>";
$("#badge").html(profHTML);
});
}

This is how that renders – and you can look at the code for this step.

Step 2: Add Share Stream

The member is authorized and we can see their information. The next thing the application needs to do is grab all of the status updates in their network stream and insert the information into the application.

This can be done using the NetworkUpdates function. To restrict the type of update to “STAT” (Status) updates, .params is used. For simplicity we’re using another inline function, which iterates over the updates and builds individual stream items for each one, then injects the html into the stream <div>.

There also needs to be a call to getUpdateStream() in the loadData() function so it gets called after the profile is loaded.

function getUpdateStream() {  
	IN.API.NetworkUpdates()
	.params({type:"STAT"})
	.result(function(result) {
	    var streamHTML = "";
		for (var update in result.values) {
			var thisupdate = result.values[update]
		
			// Build each individual stream update item
			person = thisupdate.updateContent.person
			var thisHTML = "<div class=streamitem>";
			
			// Person's picture,  linked name, and status
			thisHTML += "<img align=\"left\" class=img_border height=\"50\" src=\"" + person.pictureUrl + "\"></a>"; 
			thisHTML += "<a href=\"" + person.publicProfileUrl + "\">";
			thisHTML += "<span class=updater>" + person.firstName + " " + person.lastName + "</span></a>";			
			thisHTML += "<p class=update>" + activateLinks(person.currentStatus) + "</p>"
			thisHTML += "</div>";
			streamHTML += thisHTML
		}
		$("#stream").html(streamHTML);
	});
}

And now we have this. Code is here.

Step 3: Adding Interaction

Reading from the API is all well and good, but it’s pretty hard to make a compelling application if the member can’t do anything with it. Status updates can always be “liked”, so let’s add some like/unlike action to StreamIn’.

This part is a little more complicated. getUpdateStream() needs to be extended to add Like/Unlike buttons, and those buttons have to perform the appropriate action when pressed.

The Connect calls for these buttons are a little trickier. Since there is no Like/Unlike convenience method in Connect, we’ll pass through a raw call to the backend API. For this call, the method needs to be “PUT”, and the body is simply “true” or “false”. There’s an “alert” here to tell the user something happened, and then the stream is reloaded and redisplayed.

Here’s getUpdateStream again, with the newly added code in bold. The first section simply adds a button to the stream item – if the user has not yet liked the item, a Like button is shown. Otherwise, there’s an Unlike button.
The other two functions are event handlers for clicks on these buttons.


function getUpdateStream() {  
	IN.API.NetworkUpdates()
	.params({type:"SHAR"})
	.result(function(result) {
	    var streamHTML = "";
		for (var update in result.values) {
			var thisupdate = result.values[update]
		
			// Build each individual stream update item
			person = thisupdate.updateContent.person
			var thisHTML = "<div class=streamitem>";
			
			// Person's picture,  linked name, and status
			thisHTML += "<div class=updateperson>" ;
			thisHTML += "<img class=img_border align=\"left\" height=\"50\" src=\"" + person.pictureUrl + "\"></a>"; 
			thisHTML += "<a href=\"" + person.publicProfileUrl + "\">";
			thisHTML += "<span class=updater>" + person.firstName + " " + person.lastName + "</a></span>";	
			thisHTML += "<p class=update>" + activateLinks(person.currentShare.comment) + "</p></div>";
						
			// Present a like button
			if (! thisupdate.isLiked) {
				thisHTML += "<div id=button><button class=\"likebutton ui-corner-all\" id=\"" + 
                                thisupdate.updateKey + "\"><img src=\"Thumbs_up.png\"> Like</button></div>"
			} else {
				thisHTML += "<div id=button><button class=\"unlikebutton ui-corner-all\" id=\"" +
                                thisupdate.updateKey + "\"><img src=\"Thumbs_down.png\"> Unlike</button></div>"
			}	
			thisHTML += "</div>";
			
			// Slap this onto the HTML we're building
			streamHTML += thisHTML;
		}
		$("#stream").html(streamHTML);
	});
	
	$( ".likebutton" ).live("click", function() {
		   likeURL = "/people/~/network/updates/key=" + $(this).attr("id") + "/is-liked"
		   IN.API.Raw(likeURL)
			.method("PUT")
			.body("true")
			.result(function(result) {
				alert ("Liked");
				getUpdateStream();
			})
	});	
	
	$( ".unlikebutton" ).live("click", function() {
		   likeURL = "/people/~/network/updates/key=" + $(this).attr("id") + "/is-liked"
		   IN.API.Raw(likeURL)
			.method("PUT")
			.body("false")
			.result(function(result) {
				alert ("Unliked");
				getUpdateStream();
			})
	});	
}

So that’s it. The code for Streamin’ code is here. Now that you’ve gotten an app up and running, check out the docs and see what you can build!

Using Faceted Search from the LinkedIn API

Posted in API, JSAPI, LinkedIn, YUI on January 31st, 2011 by admin – 4 Comments

As many of you know, I’m now happily employed as a Developer Advocate at LinkedIn. Part of my job there is to create interesting examples using our APIs, to help other developers find their footing and create new and interesting applications.  This will be the first in a long line of posts demonstrating how to use our API to create cool apps.

Note that this, as with many other applications, will be much less interesting with small networks. Grab more connections and it’ll have more cool data to play with.

Faceted Search


My passion has always been for showing interesting intersections of data.  The LinkedIn website has a faceted search feature you can see when browsing people you may know. On the left hand side of that page, you’ll see companies and schools listed there.  Selecting one or more companies restricts the people to those folks who match one of those companies.  Selecting a school or schools does the same thing.  Selecting both creates an “and”.

Try these examples on the website to get a feel for how the facet searches work. Because these facets are tuned to your network, you’ll have different companies and schools, but you can still see how the intersections work.

  • People who worked at Netflix in the past
  • People who worked at Netflix in the past *and* attended Stanford University
  • People who worked at Intel in the past and work at Yahoo! now
  • People who worked at either Cisco Systems or Microsoft in the Past

This same functionality is available via the LinkedIn API. Information on faceted search can be found in the People Search API documentation.

FacetBrowse

So how does this translate into a real application? The application above shows one possible implementation. But how does it work?  I’ll walk through the pieces of this application.  If you want to see the source application (which uses the LinkedIn JSAPI for interaction with LinkedIn, and YUI for presentation) you can look at the page directly at http://www.princesspolymath.com/facetbrowsesmall.html

On the left hand side of the application you see a list of buttons – by default you’re looking at the companies, but go ahead and switch to schools. This list is built as soon as the user has authenticated with LinkedIn, with a raw query to the API:

function onLinkedInAuth() {
    IN.API.Raw("/people-search:" +
   "(people:(first-name,last-name,positions:" +
    "(company:(ticker,name)),educations,picture-url),facets:" +
    "(code,buckets:(name,code,count)))?" +
    "facets=current-company,school&sort=distance&count=25")
    .result(displayFacetResults)
}

This returns the top 10 schools in your network, the top 10 companies in your network, and 25 of your contacts. FacetBrowse builds the buttons for the left hand side of the widget and labels them so the app knows what’s been selected when someone clicks one of the buttons. It then trims out contacts who don’t match any of the top companies or schools, leaving you with something that looks like this:

Any time one of the buttons is clicked, the function refreshConnections is called, which does the same facet search – this time specifying a company and/or school.

function onLinkedInAuth() {
    IN.API.Raw("/people-search:" +
   "(people:(first-name,last-name,positions:" +
    "(company:(ticker,name)),educations,picture-url),facets:" +
    "(code,buckets:(name,code,count)))?" +
    "facets=current-company,school&sort=distance&count=25" + 
    "facet=past-company,{company-code}&facet=school,{school-code})
    .result(displayFacetResults)
}

Note that because of the way the facet search works, you can’t create intersections within the request (this company *and* that company). Since I want to create these intersections, when I’m refreshing connections, I ask for one of the companies and one of the companies and then eliminate matches which don’t match all of the company/school choices.

For instance, when the user selects two different companies, such as they have here:

The application asks for the people-search and declares “current-company=“. When the results come back, each person is inspected to make sure that they have both Microsoft and Netflix within their positions. Matching results are displayed in the grid.

If a school is then selected (so now we have Stanford, Microsoft and Netflix):

The query requests Stanford University as a school and Microsoft as a company, and then checks each returned person to make sure they also have Netflix in their profile.

This application is just a small example of what can be done with the LinkedIn faceted search. Tell me in the comments if you’ve found other uses for it, or have questions about how it works.

Watching Craigslist Feeds using Safari or Mac Mail

Posted in Uncategorized on January 11th, 2011 by admin – 2 Comments

Around June 28 of last year, Craigslist made a change to their feed structure which caused applications using Apple’s WebKit to be unable to load them. Safari doesn’t work (neither version 4 or 5), Mac Mail doesn’t work… This problem has been discussed on the Apple Support Forms here and here. While this problem is specific to Apple applications, it seems to only be occurring with a few different feed sources, one of them Craigslist.

There are a couple of ways to get around this problem, each of which uses the same general theory. If the structure of the RSS feed is not possible to read in your RSS reader, then use a different structure. What you need to do is pipe the feed through another feed aggregator, such as Yahoo! Pipes or Google Reader – and then add this feed to your reader.

Google Reader (requires a Google Reader Account)

  • Go to http://reader.google.com
  • Add the Craigslist feed to your Google Reader list
  • Select “Browse for stuff” in the navigation bar
  • Click “Create a bundle”
  • Drag the feed from your list into the bundle, and add it to your shared items
  • Save the bundle and then copy the link from the “Add a Link” link to use in your RSS reader

Yahoo! Pipes (requires a Yahoo! Account)

  • Go to http://pipes.yahoo.com
  • Click “Create a Pipe”
  • Choose “Fetch Feed”
  • Type the Craigslist feed URL in the Fetch Feed box
  • Connect the boxes by dragging from the “dot” at the bottom of the Fetch Feed box to the Pipe Output
  • Click “Save”
  • Add the “Pipe Web Address” for your pipe in your RSS reader (or in Safari)

I’m sure there are countless other ways to achieve the same goal, but these are relatively simple and use tools available to all of us for free. Someday perhaps Apple will fix the issue in WebKit, or the feeds at Craigslist will be adjusted so that they work, but in the meantime you can stay on top of the listings you want to see without having to constantly visit the site.

Another Chance to Get it Right

Posted in Uncategorized on January 1st, 2011 by admin – Be the first to comment

“Cheers to a new year, and another chance to get it right” — Oprah

Happy new year, to all and sundry.  I’ve apparently decided to start off the new year with a head cold, but that’ll pass, and I’ll have all of glorious 2011 in front of me to learn new things, have new experiences, cherish the things I have, and try to scootch ever closer to zen mastery. Last year was very trying, and I don’t think I made any progress on the zen scale – on the other hand, with the list of things that happened to us I’m glad not to have lost much ground.  I won’t bore you by including them all here, but I assure you that on any scale last year was… interesting times.

So, this year!  Resolutions are always fun and exciting.  I generally don’t see the first of the year as any magical day, but if there’s some change I’m planning to make I frequently put it off until the beginning of the new year, since my experience says that making serious habit changes during autumn rarely stick.

So here we are, in winter (brr), with a handy spot on the calendar for introducing changes into our lives.  What do I plan to do with it?

Food. I’m going to stop eating so many inflammatory foods.  My joints are screaming more often than not now, and along with going to the doctor I’m going to stop eating the foods that make my tissues more cranky.  So, no meat, lots less gluten, no soy, way less sugar.  I may add corn and dairy, or I might not.  Also, I’m going to cook most meals at home so we save money.

Exercise. I think perhaps that all of my superwoman punching/kicking/falling martial activities aren’t the best choice I could make.  I am going to focus, for now, on yoga (my goal is 3 or more times a week) and running, and leave it at that.  I have groupons for some bootcampy things, but until my elbows, hips, shoulders and knees are no longer in pain when I stay still, I don’t think I’ll stress them.  I’m going to start a new DirectLife plan on Monday to make sure I’m burning the calories I want to be burning.

Scheduling.  I sure do get into ruts.  And rather than comforting and calming me because of the predictability it makes me a little cranky because I never have really new experiences. So every month I’m going to do something that’s not a usual thing.  This month, the Edwardian Ball.  Next month (ok, I might be cheating a little bit here), Pantheacon.  My friends have awesome parties that I don’t usually go to but I’m going to go to them too.

Money.  We have a lot of stuff.  We really don’t need more.  I have started a new system of adding things I would normally just buy to my amazon wishlist, then reviewing that after a couple of weeks to see if it still seems like a good idea.  I’ve got a bunch of amazon gift money to spend, but it’s not like it’s going to go bad or anything.

Stuff.  Speaking of stuff, I have a lot of stuff I don’t use.  Clothes, knick knacks, books.  This month will be a month of organizing my things, one type at a time (clothes first!) and giving the stuff I don’t use to people who don’t have stuff.

Mindfulness.  Have some.

So that’s my list.  It probably looks a lot like your list, if you have one.  But these are things I try to (and frequently do) change in my life.  I’m just making a fresh run at them since the calendar makes it easy.

Enhancing your JQuery Website with the Facebook Graph

Posted in Uncategorized on November 11th, 2010 by admin – Be the first to comment

Hackday is here again at Netflix, so I’ve been working on a web version of the Intersect which uses JQuery. It occurred to me that adding social to the system would be some nice icing on the cake, so I set forth to use the Facebook Like Button to add things to the users’ graphs, and then the Graph API to pull them back out.

The functionality I wanted to implement was:

  • Show a like button
  • Show thumbnails for each of my friends who like the current movie
  • When the user’s thumbnail is clicked show all the movies liked by that friend in the app
  • When one of those movies is clicked, jump to that movie

Turns out this task was more challenging than I anticipated – partly because, as a hack, I hadn’t thought it out completely, and partly because some of the pieces were more difficult to implement than I’d expected.  You can see the resulting application here, although I will remind you that as a hack it’s not as stable as it might be, and it seems to be particularly sad in Firefox.

This post will cover the process I went through from start to finish.

  1. Preparing the application for Facebook interaction
  2. Adding the like button
  3. Getting users who like the page
  4. Getting all the items for a particular user

Preparing the application for Facebook interaction

If your application uses JQuery or any other Javascript framework, it’s likely that the URL doesn’t change as you move around in the application.  However, if you want the Facebook interaction to address specific entities in your application (in my case, movies) you will need to adjust the application to accept different URLs for different items (likely using query parameters).

For instance, the URL for my website was:

http://www.princesspolymath.com/webIntersect/index.html

But I wanted to be able to let people “like” particular movies, so I added an optional parameter “movie” to jump directly to a particular movie.

http://www.princesspolymath.com/webIntersect/index.html?movie=BVQHL

Once I’d added that piece of interaction, I needed to tackle the Open Graph metatags, so these liked items would correctly show up in the user’s Facebook graph as movies with the right metadata.  I tried inserting them using Javascript, but the Facebook linter does not accept dynamic content, so the metatags needed to be in place as soon as the page was loaded in order to be retrieved by the linter. What I needed to add to the headers was partly static content (app_id, site_name), but partly based on the movie being shown (title, picture, url)

So my JQuery based “html” page switched to a .php file so that it could get pre-processed to show the metadata before the page was loaded.

http://www.princesspolymath.com/webIntersect/index.php?movie=BVQHL

Which was a great idea, except that coming into the page from that link meant I didn’t have the metadata I needed in order to create the open graph metadata tags. The information I needed for the tags had been retrieved from a web service, so getting them back again before page load (without storing them on the server) was a challenge. I decided to store the information I needed *inside* of the URL.

http://www.princesspolymath.com/webIntersect/index.php?movie=BVQHL&name=Howl%27s+Moving+Castleℑ=http%3A%2F%2Fcdn-3.nflximg.com%2Fus%2Fboxshots%2Flarge%2F70028883.jpg

My app ignored the extra parameters, but the PHP parser was able to get what it needed to make the tags:
function curPageURL() {
$pageURL = 'http';
if ($_SERVER["HTTPS"] == "on") {
$pageURL .= "s";
}
$pageURL .= "://";
if ($_SERVER["SERVER_PORT"] != "80") {
$pageURL .= $_SERVER["SERVER_NAME"].":".$_SERVER["SERVER_PORT"].$_SERVER["REQUEST_URI"];
}
else {
$pageURL .= $_SERVER["SERVER_NAME"].$_SERVER["REQUEST_URI"];
}
return $pageURL;
}
function getName() {
return ($_GET["name"]);
}
function getImage() {
return ("http://api.freebase.com/api/trans/image_thumb" . $_GET["fbId"] );
}
?>

Adding the Like Button

Once I had the right URL, which had the right parameters to feed the Open Graph metatags back to the linter for proper graph inclusion, and the movie information so I knew what movie it refered to, I set to adding the Like Button.

My first thought was to use the dynamic one created by the Javascript SDK (since I needed it for other functionality) – giving my users the opportunity to comment and giving me the ability to listen for clicks… but Safari had some security complaints about the way it was fiddling with content so I decided to fall back to the iframe version.

I set it up using the URL I built above and checked to make sure that it was adding the correct movies to the graph (by directly querying graph.api.com to see whether there were new movies being added to my likes).

Note that in this case, since I was dynamically generating the content for the dialog, I needed to tell the FB SDK to parse the page again once the elements were finished rendering:

window.setTimeout(function() {																					   
    FB.XFBML.parse();
}, 500)

Getting Friends who Like the Page

Unfortunately, there’s no direct graph query for “Give me all of my friends who like this page” – I couldn’t even get a list of the users who like the page similar to how the like button with pictures did it.  However, using the Facebook Javascript SDK it was relatively easy to get all of my friends and their movies. Note that it would have been more accurate to search for the specific ID for this movie, but I didn’t have that available – all I had was the URL I’d built and the name of the movie, so I used the name.

FB.api("/me/friends?fields=movies,name,picture", function(friends) {
for (var friend in friends.data) {
	currentFriend = friends.data[friend]

	if (!currentFriend["movies"]) {
		continue
	}
	for (var friendmovie in currentFriend.movies.data) {
		thisFriendMovie = currentFriend.movies.data[friendmovie]
		if (thisMovieTitle == thisFriendMovie.name) {
			thisfriend = { "id":currentFriend.id,
			            		"name":currentFriend.name,
						"picture":currentFriend.picture
			}
			liking_friends.push(thisfriend)												
		}
	}
}

Getting Movies for a Particular Friend

I tagged the images of the user’s friends with their Freebase ID, so when the thumbnail was clicked, I grabbed that user’s movies from the graph:


FB.api("/" + id + "/movies?fields=website,picture", function(movies) {
	faves = ""
	for (var movieIndex in movies.data) {
		currentMovie = movies.data[movieIndex]
		if (!currentMovie.website || !
                          currentMovie.website.match(/princesspolymath.com/)) {
				continue;
		}
		params = currentMovie.website.split('=');
		tinyurl = params[1].split('&')[0]
		image = unescape(params[3])

		faves += ' '
	}
	$('#faves').html(faves)
}) 


I used the “website” information from the graph to get the id for the movie (the “movie” parameter) and the correct image to show in the application. When the user clicks on one of the movies for the user, the id is used to bring up a dialog with that movie, allowing people to navigate within the application using the graph information retrieved from Facebook.

Feel free to play around with the application and “like” some movies to see how it works.

The Intersect has Launched

Posted in Uncategorized on October 14th, 2010 by admin – 1 Comment

Many of my faithful readers are aware that I’m quite passionate about giving people different ways to browse through data spaces. Being a Netflix subscriber, one of the things I find frustrating is that I cannot make intersecting queries to find movies I want to see, or to help me remember a movie I vaguely recall a few data points about.

To that end I have created an iPad application, The Intersect for Netflix, which allows users to do exactly that.  Watch the video below to get a taste for what it does for you.  This first version only has movies in it, but version 1.1 (already submitted to the App Store) will have TV shows as well.

This application was developed using the Freebase and Netflix APIs, combined together to provide a richer exploration experience for the user.

It’s not free, but what else are you going to do with a dollar?  Yo-yos are frustrating, band candy is temporary, but this will give you hours of entertainment – and it will just keep getting better!

Version 1.1 is now in the store, including TV shows and the ability to rate movies!

Tracking Fitness

Posted in Uncategorized on August 4th, 2010 by admin – 1 Comment

One of the things I’m doing right now is training for the Las Vegas Triathlon (as well as the Nautica Triathlon the week before). I like to be able to chart my progress with various different technical tools, but I’m finding myself a tiny bit frustrated trying to find something that’ll really work for me. What I want is something that tracks all my workouts with heart rate, time, intensity, and speed. In one place.

I’ll go over my various existing tools here to demonstrate the problem.

For simplicity, I’ll leave out the FitBit, Philips DirectLife and the GoWearFit, as they’re targeted to another type of activity (wandering around during the day when you’re not working out). And I’ll leave out integrated sites like DailyBurn, which try to pull together your workout information with food tracking and a vibrant community. I’ll talk about those another time.

Withings Scale

I have a Withings WiFi enabled scale. I know it sounds really silly, but I love this thing. It tracks my weight and fat percentage and makes charts and graphs, and makes it so much easier to track changes in my weight and body composition. Stand on the scale, wait a bit, and then you’re good – there’s an iPhone app and a website which both allow you to see your progress. I actually started losing weight at 230 pounds so the graph isn’t complete, but it really helps keep me on track (and reminds me that sometimes there are plateaus and that’s ok).

Polar FT60 Heart Rate Monitor


One of the most important tracking and feedback tools you can have while training is a heart rate monitor. Your resting heartrate, your heartrate during a workout, and your average heart rate are all critical pieces of knowledge to optimize the efficacy and safety of your workouts. I got a new Polar watch this year, the FT60. It’s got all the things I like about Polar HRMs, including the “Fitness test” where it has you relax for a few minutes and tells you how fit you are. It’s also got custom training plans for whatever you need – where it tells you what kind of workout you should do, and monitors your heart rate to make sure you’re staying on track. This is a great system for avoiding overtraining, and their website seems like it’s probably a decent place to track activity… although I don’t know how well it integrates with my social stuff (Twitter, etc.) since I haven’t yet purchased the “optional” Polar Flowlink transmitter which is needed to get information from the watch up to their site. The technology for these heart rate monitors has apparently improved to the point where swimming data is reasonably accurate, which makes me happy. If only Polar were more open so I could use the data in other applications.


RunKeeper

For tracking my runs, I like RunKeeper. It’s GPS based, and it will track my runs, my bike rides, my skating, my walking… pretty much everything. The mapping feature is great, and it’s nice to use. However, it’s only good for outside workouts, and my iPhone isn’t fond of the pool, so I can’t track my swims that way. It integrates with several different Polar watches… just not the one I have. It does integrate with my Withings Scale to determine calories, which is great. It might someday integrate with something like DailyBurn or the FitBit, which would be extra cool. I’m hoping that once I figure out how to upload the FT60 stuff to the polar website, I’ll figure out a way to integrate that into RunKeeper – but that may be a ways out.

Nike+ iPod

I’m also fond of Nike+. I’ve used it since I got my old-tyme (Gen 2?) Nano, and use it now on my iPhone. Polar recently came out with a new HRM strap which is supposed to work with Nike+ enabled devices, and I bought one. Turns out it works specifically with the Nike+ armband and the Generation 5 iPod Nano, but nothing else. So I can’t directly integrate it with my iPhone Nike+ runs, which kind of sucks. I’m considering getting a Gen5 Nano just so that I can use it for runs (the iPhone is a little large for using with runs, anyhow), but I don’t know that I want to put out the money. It does have an awful lot of my running history, which is compelling. I also wish it would track other kinds of workouts for me.

Summary

What I want doesn’t seem so outrageous. But here I find myself, with lots of new and old data at Nike+, some upcoming data to be available on the Polar site, an abiding fondness for RunKeeper along with some interest in using something more social like dailyburn.com… and no good way to integrate them all together. Integration is coming along for many of these things, albeit slowly.

I’m probably just going to buy a WearLink so that I can try the Polar site, and track my runs both with Nike+ and RunKeeper for a while. If and when I end up being sad at the lack of sharing of the Polar data, I’ll suck it up and get a refurbished Nano so that I can see how Nike+ integrates the heart rate information. The best possible case would probably be for RunKeeper to consume the polar heart rate data and integrate it with my workouts.

Creating Netflix Widgets from Freebase Queries

Posted in Geek Stuff on July 16th, 2010 by admin – Be the first to comment

Freebase has just finished creating a process to import Netflix film data on a daily basis. Freebase now includes in their movie metadata the key for the Netflix API. For Netflix API developers, this makes it possible to leverage Freebase’s powerful query engine and create amazing application interfaces. But even for those people who have never used the Netflix API, this functionality allows developers to leverage Netflix’ widgets to create interfaces within their existing applications, without the hassle of OAuth. I’ll walk through the process of creating a Netflix widget based on a freebase query here.

Getting the Movie

Mel Gibson is a popular guy, who’s done a bunch of movies. But what has he done for us lately? The following freebase query asks what movies he’s done since January 1, 2009. I’ll go ahead and ask for the Netflix “Tiny URL” as that’s what we’ll need to create the Netflix widget.

[{
  "name" : "Mel Gibson",
  "film": [{
    "film":  [{"initial_release_date>":"2009-01-01", "name":null,
               "key":[{"namespace":"/authority/netflix/tiny", "value":null}]}],
    "id":    null
  }],
  "type":          "/film/actor"
}]​

Turns out, he’s been taking it easy recently. There’s just a couple of movies released on DVD since the beginning of last year.

{
  "code":          "/api/status/ok",
  "result": [{
    "film": [
      {
        "film": [{
          "key": [{
            "namespace": "/authority/netflix/tiny",
            "value":     "BVmIj"
          }],
          "name": "Edge of Darkness"
        }],
        "id": "/m/04lr63t"
      },
      {
        "film": [{
          "key": [{
            "namespace": "/authority/netflix/tiny",
            "value":     "BVssN"
          }],
          "name": "The Beaver"
        }],
        "id": "/m/09vnjl6"
      }
    ],
    "name": "Mel Gibson",
    "type": "/film/actor"
  }],
  "status":        "200 OK",
  "transaction_id": "cache;cache01.p01.sjc1:8101;2010-07-16T21:13:40Z;0034"
}

Creating the Widget

Now that we have the tiny URL, we can create a widget. The Netflix widget builder can be used to create a template for the widget you want to create. When you’re done configuring your widget, you’ll end up with some Javascript code that looks like this:

<script src=”http://jsapi.netflix.com/us/api/w/s/sp100.js” settings=”id=http://movi.es/BVdT5″></script>

Once you’ve got that, it’s a simple task to create new widgets. The Tiny URL you grabbed above is used to update the ID, so in the case of “Edge of Darkness” you’d put “BVmIj” in place of BVdT5 and end up with this:

<script src=”http://jsapi.netflix.com/us/api/w/s/sp100.js” settings=”id=http://movi.es/BVmIj”></script>

Which will render on your page like so:

Once you’ve gotten this all working to your satisfaction, you can even set up the widgets to include your developer key and earn money from Netflix via the developer Affilliate Program.