Sunday, August 26, 2012

Donating to Reduce the Birth Rate

Now that I'm actually earning income again, I've decided to start donating much more to charities - both because it will reduce my tax burden, and because I feel like my money would be better spent elsewhere. I have no kids to support and no mortgages to pay and have no plans for such things in the near future, so I only really need enough money for SF housing (which is actually a fair bit), food, and travel.

So, I've started researching charities and causes. Recently, partially due to the influence of Isaac Asimov's essays, I've become very interested in the problem of lowering the global birth rate. We don't have enough resources to support the growing population of the world, and while of course we should work on ways to increase our resources, we should also think about decreasing the population growth rate. As I see it, there are a few ways of doing that:

  • Increasing the availability and contraception and family planning. For the women who don't want children, we need to make it easier for them to prevent it from happening.
  • Increasing the number of women in the workplace. For a long time in human history, the role of an adult woman was to bear children and feed them, and we haven't had time for other occupations. We needed that division to make sure that enough children would survive to continue on the human race. But now, we don't have that problem, and adult women should feel empowered to spend their time elsewhere. If they have an education and/or jobs skills training, then they should hopefully find a job opportunity awaits them. I'm not saying that no women should be mothers ever again - just that there may be some women that would happily spend more of their time in the workplace than in motherhood if they were given the chance - and we should make it possible for them to get that chance.

Given those thoughts, here are the charities I donated to:

  • Planned Parenthood San Francisco: They provide fantastic services and support, and I'm proud to say I'm both their patient and now a sponsor.
  • Bedsider: They are an online birth control support network, and they're going about it with a modern website and attitude.
  • Marie Stopes International: They're an international organization dedicated to family planning and reproductive healthcare. According to them, they prevented 4.5 million unwanted pregnancies in 2011.
  • Girls Inc: They're a US organization dedicated to helping girls to achieve and aspire.

The hard part about donating to charities is not knowing which of them will do the most with your dollar. There are sites like GiveWell and CharityNavigator, but they haven't reviewed all the charities out there. So, I'm distributing my donations in the hopes that I'll achieve my goal through at least one of them. Let me know if you have any suggestions for ways to maximize the impact of my donations in this area. Thanks!

Thursday, August 23, 2012

Using the Maps API Autocomplete for User Profile Location

As a frontend engineer at Coursera, one of my current projects is to create shared profiles for our users. When users first sign up, all we ask them for is their name, and so that's all we can show to other users as well — like when they see each other's posts in the forums. We want to make it so that users can click on a classmate's name and find out more about them, like where they're from and what other classes they're taking, because that should make a classroom of thousands of strangers feel more intimate. Eventually, we also want to make it easy for users to search their classmates to find people to form in-person, location-specific study groups or language-specific discussion forums, because that will give users a mini community inside the class.

By the time users sign up for Coursera, they've already signed up for many other sites and likely filled out a profile information many times, and I wanted to make their Coursera profile editing experience as quick and easy as possible. Easier said than done! It's amazing how many debates and decisions can go into designing a single form.


Location, Location

For example: location. We want to let users share where they are—not their exact address, that'd be creepy—just their city and country. In my first draft of profiles, I started off with a single line text input for location, with a placeholder for "City, State, Country". But, I wanted to be able to query our database and easily find out which users are from what country or state, so I needed a good way of turning user entered locations into structured data.

Halfway down the path of implementing it myself, I discovered that the Google Maps API now offers an Autocomplete API and Autocomplete widget, and that it was exactly what I needed. When you attach the widget to a text input, it listens to a user typing and pops up suggestions (like Google instant search), lets them pick a suggestion, and it can call back your code with structured information about the picked address.

The Code

To use it for our profile editor form, I first created HTML for the single-line text input and a set of hidden inputs for each piece of structured location data that I cared about. We actually write our HTML in bracket-less Jade templates, but here's what it comes out as:

<input name="location" type="text" placeholder="City, State, Country">
<input name="location_city" type="hidden">
<input name="location_state" type="hidden">
<input name="location_country" type="hidden">
<input name="location_lat" type="hidden">
<input name="location_lng" type="hidden">
<input name="location_id" type="hidden">

Then in the JavaScript, I attach the Autocomplete widget to the location input and add an event listener for every time the user selects a new result. In the callback, I populate the hidden inputs with the relevant structured data. The results format for the Maps API is a bit complex, because they designed it to work for any address anywhere in the world (and not every address is divided into the same city/state divisions as US addresses), so you end up having to search through all the "address components" to find the component that most matches what you're interested in. To simplify that search, I wrote a wrapper function using Underscore's find method. Here's what that looks like:

var autocomplete = new google.maps.places.Autocomplete($('#input[name="location"')[0], {types: ['geocode']});
  
google.maps.event.addListener(autocomplete, 'place_changed', function() {
    var place = autocomplete.getPlace();
    $('input[name="location_id"]').val(place.id);
    $('input[name="location_lat"]').val(place.geometry.location.lat());
    $('input[name="location_lng"]').val(place.geometry.location.lng());
    $('input[name="location_country"]').val(findComponent(place, 'country'));
    $('input[name="location_state"]').val(findComponent(place, 'administrative_area_level_1'));
    $('input[name="location_city"]').val(findComponent(place, 'administrative_area_level_3') || findComponent(place, 'locality'));
});

function findComponent(result, type) {
  var component = _.find(result.address_components, function(component) {
    return _.include(component.types, type);
  });
  return component && component.short_name;
}

All the code is in this gist.


The Result

We launched the first iteration of profiles today, so you can try it our yourself, in your settings. Or you can check out this screenshot of what it looks like to a user when they're editing their profile.

When a classmate is viewing their profile (like you can view mine here, they'll see their location plus a little flag (courtesy of this free flag icons database), and that flag is based on the hidden country field.


Caveats

There are a few things to keep in mind with this approach:

  • If the user types their location but does not select it from the autocomplete, then we will not store the structured data. I could add in a pre-submit reverse geocode step in case that happens, but I'll wait until I see what users do.
  • If the user types a location that the Google Maps API simply doesn't know, then we won't have any structured data about it. That's okay- we'll still display what they typed on their profile, we just won't be able to do any clever location-based recommendations for them.
  • The Autocomplete widget includes the Google logo, and per the terms, that cannot be removed. I am okay with admitting that it is courtesy of Google, though. I think it improves the user experience enough that it's okay to share branding with them.
  • If the Maps API ever shuts down the Autocomplete widget (Google is on a bit of an API-killing roll these days), we will have to re-write the functionality ourselves. That's okay, we'll still have the data from before. There's no harm in saving time now while we can.
  • Per the Maps API terms of use, you shouldn't be storing the Places data forever - so you should store the place ID and refresh your cache every so often. Or at least, that's how I interpret the terms. Do your own interpreting, or hire a lawyer. :-)

All in all, it's a nice touch in our profile experience that should hopefully make it faster and more fun. Thoughts?.

Tuesday, August 21, 2012

Geo-Targeted House Ads

At Coursera — like many companies in the valley these days — we're desperate to hire more people, particularly for our engineering department. But hiring is hard, and the reasons can be boiled down to these: 1) there aren't enough qualified candidates in the world, 2) the qualified candidates that do exist don't know that we exist, and 3) the qualified candidates don't want to work for us. Problem #1 isn't something we can quickly solve — though hopefully we are indirectly helping to solve it by offering so many CS courses for free. Problem #3 is also not easily solved — candidates may not like our technology stack, our location, or our culture — all of which are hard to change. So, in the short-term, we're focusing on Problem #2, making sure that candidates know we exist.

Well, I realized that we might actually have potential candidates amongst our Coursera users, and I wondered if there'd be an easy way to let them know that we're hiring. I didn't want to message all 1 million+ of our users, since it wouldn't be relevant to many of them - like those in countries with visas we don't sponsor, or those studying courses outside of CS. So, I decided to display a "We're hiring!" house ad based on the combination of geolocation and course enrollment information. It's my ghetto version of a geo-targeted house ad, and I thought I'd share the idea and code here.

Here's what it looks like — quite basic:

Here's the HTML for the jobs ad, which uses the Twitter Bootstrap alert classes:

<div id="coursera-promo-cs-job" class="alert alert-info coursera-promo">
  <button data-dismiss="alert" class="close">x</button>
  Want to help us build the best online education platform?   <a href="/about/jobs">Join our team!</a>
</div>

And here's the JavaScript. I use the jQuery cookie plugin to check if the user already dismissed the promo, then use our internal methods to check if the user is enrolled in CS classes, and then finally, if I've made it that far, I load the Google AJAX APIs and use the google.loader.ClientLocation object to figure out if the user is in a target country. If the user meets all the conditions, then I show the ad and add a click listener to the close button. I also use our internal analytics to track when users are shown the ad and when they click or dismiss it.

var promoId = 'coursera-promo-cs-job';
if (!cookie.get(promoId)) {
  if (Coursera.user.isInCSClass()) {
    $.getJSON('https://www.google.com/jsapi?callback=?', function () { 
      if (google.loader.ClientLocation) {
        var countryCode = google.loader.ClientLocation.address.country_code;
        var state = google.loader.ClientLocation.address.region;
        if (countryCode == 'US' || countryCode == 'AU') {

          var $promo = self.$('#' + promoId);
          $promo.show();
          Coursera.tracker.push({key: 'user.promo.show.' + promoId});

          $promo.find('.close').on('click', function() {
            $promo.hide();
            cookie.set(promoId, 'closed');
            Coursera.tracker.push({key: 'user.promo.close.' + promoId});
          });

          $promo.find('a').on('click', function() {
            Coursera.tracker.push({key: 'user.promo.click.' + promoId});
          });
          
        }
      }
    });
  }
}

When we first deployed this, I set the targeting quite narrow, restricting it to only users in 2 CS courses and from California. Since then, we've broadened the targeting to all of US and 5 more countries.

As we find the need for more "house ads", like promoting new features that may be more relevant to some users than others, I imagine I will turn this bit of code into a more general purpose library, and maybe we'd move the logic into a database or deploy script, to make it easier to configure ads without changing code. Do you do anything similar on your own sites? Let me know in the comments.