<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0">
 
 <title>Pushing Squares - The Squarepush Blog</title>
 
 <link href="http://squarepush.com/ps" />
 <updated>2010-02-24T17:28:26-05:00</updated>
 <id>http://squarepush.com/ps/</id>
 <author>
   <name>Mark Percival</name>
   <email>mark@mpercival.com</email>
 </author>

 
 <atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/squarepush" /><feedburner:info uri="squarepush" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><link rel="license" type="text/html" href="http://creativecommons.org/licenses/by-nc-nd/3.0/" /><entry>
   
     <title>Released Divining Rod Library</title>
     <link href="http://feedproxy.google.com/~r/squarepush/~3/iYRw-wAgc4A/Released-a-mobile-profiling-library" />
     <updated>2010-02-24T00:00:00-05:00</updated>
     <id>http://litanyagainstfear.com/articles/2010/02/24/Released-a-mobile-profiling-library</id>
     <content type="html">&lt;p&gt;We just released another iteration of our mobile device profiling library, &lt;a href='http://github.com/markpercival/divining_rod'&gt;Divining Rod&lt;/a&gt;. Divining Rod hopes to simplify the process of determining which mobile devices are visiting your site or web application, and their capabilities.&lt;/p&gt;

&lt;p&gt;Unlike more complicated solutions, such as WURFL, we wanted to make the solution both simple to implement and easy to customize, a feat we think we&amp;#8217;ve accomplished with Divining Rod.&lt;/p&gt;

&lt;p&gt;Read more about Divining Rod &lt;a href='/articles/2010/02/23/dont-put-another-dime-in-the-jukebox'&gt;here&lt;/a&gt;, or download it from &lt;a href='http://github.com/markpercival/divining_rod'&gt;github&lt;/a&gt;.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/squarepush/~4/iYRw-wAgc4A" height="1" width="1"/&gt;</content>
   
 <feedburner:origLink>http://squarepush.com//articles/2010/02/24/Released-a-mobile-profiling-library</feedburner:origLink></entry>
 
 <entry>
   
     <title>Don't put another dime in the jukebox</title>
     <link href="http://feedproxy.google.com/~r/squarepush/~3/cJF5y9n-LPk/dont-put-another-dime-in-the-jukebox" />
     <updated>2010-02-23T00:00:00-05:00</updated>
     <id>http://litanyagainstfear.com/articles/2010/02/23/dont-put-another-dime-in-the-jukebox</id>
     <content type="html">&lt;h1 id='or_how_i_stopped_writing_regular_expresssions_to_parse_user_agents'&gt;Or how I stopped writing regular expresssions to parse user agents&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;Just want the code? Head over the Github and &lt;a href='http://github.com/markpercival/divining_rod'&gt;grab it!&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Beneath every good mobile app, lies the ability to easily and accurately determine the make and model of the user&amp;#8217;s mobile device. And up until a month ago we hard coded this into our apps. But like everything, as time passed, what seemed like the simplest solution began to morph into a complex web of regular expressions and conditional statements.&lt;/p&gt;

&lt;p&gt;And we&amp;#8217;ve seen a fair number of solutions to the problem. Some clients come with their own complex sets of mobile matching algorithms, while other rely on open source solutions like WURFL. At Squarepush we began to wonder if it was all really necessary.&lt;/p&gt;

&lt;h2 id='the_problem_for_the_majority'&gt;The problem for the majority&lt;/h2&gt;

&lt;p&gt;If you&amp;#8217;re looking to deliver Java jar files to every Nokia made since 1994 and need a reliable way to determine exactly which phone you&amp;#8217;re dealing with, this isn&amp;#8217;t the library for you. But according to &lt;a href='http://metrics.admob.com/'&gt;AdMob&lt;/a&gt;, 91% of mobile traffic in North America consist of iPhones, Android, and Blackberry, in that order. So if you&amp;#8217;re simply wanting to build the best possible web experience for the majority of your users, something easier is in order&lt;/p&gt;

&lt;h2 id='divining_rod__a_magical_stick_for_mobile_profiling'&gt;Divining Rod - A magical stick for mobile profiling&lt;/h2&gt;

&lt;p&gt;When we looked at the current solutions for profiling phones, we noticed a lot of repetition. Most of the phones out there have similar user agents. For example, both the Apple iPhone and the Nexus One have a user agent with the words &amp;#8216;Apple&amp;#8217;, &amp;#8216;Mobile&amp;#8217;, and &amp;#8216;Safari&amp;#8217;.&lt;/p&gt;

&lt;p&gt;The other problem was defining capabilities. Both Android and the iPhone are capable of showing videos on YouTube, but the geolocation javascript hooks are slightly different. What we needed was a way to tag multiple mobile devices and not be forced into an absolute hierarchy.&lt;/p&gt;

&lt;h5 id='enter_diviningrod'&gt;Enter DiviningRod&lt;/h5&gt;

&lt;p&gt;DiviningRod lets you do something like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;DiviningRod::Mappings.define do |map|
    map.ua /Apple.*Mobile.*Safari/, :format =&amp;gt; :webkit
                                  , :tags =&amp;gt; [:webkit, :safari, :youtube_capable] do |webkit|
      webkit.ua /Android/, :name =&amp;gt; &amp;#39;Android&amp;#39;, :tags =&amp;gt; [:android, :google_gears]
      # Apple iPhone OS
      webkit.with_options :tags =&amp;gt; [:apple, :iphone_os] do |iphone|
        iphone.ua /iPad/, :tags =&amp;gt; :ipad, :name =&amp;gt; &amp;#39;iPad&amp;#39;
        iphone.ua /iPod/, :tags =&amp;gt; :ipod, :name =&amp;gt; &amp;#39;iPod Touch&amp;#39;
        iphone.ua /iPhone/, :tags =&amp;gt; :iphone, :name =&amp;gt; &amp;#39;iPhone&amp;#39;
      end
    end

    map.ua /BlackBerry/, :tags =&amp;gt; :blackberry, :name =&amp;gt; &amp;#39;BlackBerry&amp;#39;
end&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;So when we see an Apple iPhone user agent like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Mozilla/5.0 (iPhone; U; CPU iPhone OS 3_1_2 like Mac OS X; en-us) AppleWebKit/528.18 (KHTML, like Gecko) Version/4.0 Mobile/7D11 Safari/528.16&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We can go ahead and tag it &lt;strong&gt;apple&lt;/strong&gt; while continuing to look for more specific matches, in this case the iPhone match.&lt;/p&gt;

&lt;p&gt;And when we profile that request with DiviningRod, we get to do this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;profile =  DiviningRod::Profile.new(request)

# Notice that the tags aggregate. This is the only hash value that doesn&amp;#39;t override.
profile.tags #=&amp;gt; [:webkit, :safari, :youtube_capable, :apple, :iphone_os, :iphone]

# Which we can ask about
profile.iphone? #=&amp;gt; true
profile.apple?  #=&amp;gt; true

# and retrieve arbitrary hash values, like :name, and :format
profile.format  #=&amp;gt; :webkit
profile.name    #=&amp;gt; &amp;#39;iPhone&amp;#39;&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id='where_were_headed'&gt;Where we&amp;#8217;re headed&lt;/h2&gt;

&lt;p&gt;Right now it&amp;#8217;s a really simple but powerful library, and while we want to keep it as extensible and intuitive as possible, we also want to make it dead simple to get it configured for most applications.&lt;/p&gt;

&lt;p&gt;The next step is building out the definitions, and including them in the gem for easier use. Ultimately, we&amp;#8217;d like to build one master set of user agent definitions that cover 95% of the mobile apps out there, while still allowing for customization. We think we&amp;#8217;re pretty close to that goal.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/squarepush/~4/cJF5y9n-LPk" height="1" width="1"/&gt;</content>
   
 <feedburner:origLink>http://squarepush.com//articles/2010/02/23/dont-put-another-dime-in-the-jukebox</feedburner:origLink></entry>
 
 <entry>
   
     <title>Released PingTags</title>
     <link href="http://feedproxy.google.com/~r/squarepush/~3/57uOG_5jCRw/pingtags" />
     <updated>2010-01-16T00:00:00-05:00</updated>
     <id>http://litanyagainstfear.com/articles/2010/01/16/pingtags</id>
     <content type="html">&lt;p&gt;PingTags.com is our latest project demonstrating what can be done current QR Code technology. We&amp;#8217;re able to capture some exciting metrics, such as the exact location of the user, from a wide variety of devices.&lt;/p&gt;

&lt;p&gt;Check out &lt;a href='http://ps.squarepush.com/post/341007083/pingtags-our-latest-project'&gt;our blog post&lt;/a&gt; about it for more information, or &lt;a href='http://pingtags.com'&gt;try it out for yourself&lt;/a&gt;.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/squarepush/~4/57uOG_5jCRw" height="1" width="1"/&gt;</content>
   
 <feedburner:origLink>http://squarepush.com//articles/2010/01/16/pingtags</feedburner:origLink></entry>
 
 <entry>
   
     <title>The economics of Mechanical Turk</title>
     <link href="http://feedproxy.google.com/~r/squarepush/~3/d_tb7S1JpwU/the-economics-of-mechanical-turk" />
     <updated>2009-08-26T00:00:00-04:00</updated>
     <id>http://litanyagainstfear.com/articles/2009/08/26/the-economics-of-mechanical-turk</id>
     <content type="html">&lt;p class="note"&gt;This is part of a series of Mechanical Turk blog posts &amp;#8211; check out more at &lt;a href="/ps/2009/08/11/mechanical-turk-in-ruby/"&gt;Mechanical Turk in Ruby&lt;/a&gt;, &lt;a href="/ps/2009/08/11/mechanical-turk-and-jquery-external-pages/"&gt;Mechanical Turk Pages in JQuery&lt;/a&gt;, and Building Mechanical Turk into Rails(coming soon).&lt;/p&gt;
&lt;p&gt;In this post I want to go over just a few tips about deciding how much to pay and how to structure your HITs on Mechanical Turk for the best and most cost competitive results.&lt;/p&gt;
&lt;h2&gt;The business behind the HITs&lt;/h2&gt;
&lt;p&gt;At the heart of it, Mechanical Turk tasks are completed by real people, so unlike the other Amazon services, the pricing isn&amp;#8217;t so simple. So when you start to place tasks on the network, it&amp;#8217;s completion is dependent upon several factors, the largest being:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;Price&lt;/li&gt;
	&lt;li&gt;Complexity&lt;/li&gt;
	&lt;li&gt;Number of tasks&lt;/li&gt;
	&lt;li&gt;Time of creation&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The first two are obvious, but the combination of all three can make for a tricky time figuring out what to offer.&lt;/p&gt;
&lt;h3&gt;Number of tasks&lt;/h3&gt;
&lt;p&gt;The more task available the less reward you can offer. This works because a worker is looking at this from two angles:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;How long will it take to learn to complete the tasks?&lt;/li&gt;
	&lt;li&gt;And how long is each task going to take to complete?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The first question is the biggest barrier to getting someone to work on your HIT&amp;#8217;s. If the first few take quite a while to learn how to do, and their only 20 available, it&amp;#8217;s not going to be very rewarding.&lt;/p&gt;
&lt;p&gt;However offer 1000 and you can probably discount your price significantly, so submitting your HITs all at once has a tendency to encourage acceptance and completion.&lt;/p&gt;
&lt;h3&gt;Time of creation&lt;/h3&gt;
&lt;p&gt;Although there are people all across the globe working on Mechanical Turk, if your task requires a decent grasp of English, or knowledge of western culture(eg. Read this newspaper article and tell me if it&amp;#8217;s positive of negative), you&amp;#8217;re workers will primarily come from the US and Canada. This of course means that you need to schedule your HIT&amp;#8217;s accordingly.&lt;/p&gt;
&lt;p&gt;A reasonable person would expect that HITs in the system get randomly cycled through listing page, however the truth is that they are listed chronologically, and this means that if you don&amp;#8217;t get them completed within the first hour, it&amp;#8217;s unlikely they&amp;#8217;ll be completed at all.&lt;/p&gt;
&lt;h3&gt;Complexity&lt;/h3&gt;
&lt;p&gt;There&amp;#8217;s two types of complexity you need to worry about, but both have the tendency to chase away perfectly good workers.&lt;/p&gt;
&lt;p&gt;The first is task complexity. Distill your task down to the most basic parts, and don&amp;#8217;t expect everyone to understand your instructions. The wording should be simple, clear, and concise. You should be able to look over it and immediately understand what it is you&amp;#8217;re asking for and how it should be done.&lt;/p&gt;
&lt;p&gt;The second is UI complexity. A worker wants to be able to load up new tasks and complete them as fast as possible. What they don&amp;#8217;t want is a website that slow, or javascript magic that doesn&amp;#8217;t work in their browser. And don&amp;#8217;t assume someone has the lastest browser when designing your external HITs. Make the design simple and easy to use. And for god sakes, don&amp;#8217;t get crazy with the JS &amp;#8211; the goal is a web application page that works well for the worker and allows them to use the key binding and events they are used to. Especially if it&amp;#8217;s data entry, make sure the tab navigation works.&lt;/p&gt;
&lt;h3&gt;Price&lt;/h3&gt;
&lt;p&gt;The easiest way to figure out what your tasks are worth is to build a baseline guide. Start with a few(less than 20) and pick a price which seems inline with similar tasks. Also ask that each task be completed 4 times to ensure that it&amp;#8217;s not just one worker doing it. Then posts the jobs, and check the response. If you don&amp;#8217;t have them all completed within a few hours, cancel them and raise the price. Likewise if they&amp;#8217;re competed quickly, it may be a sign you&amp;#8217;re paying too much. You&amp;#8217;re only going to figure this out through a little trial and error, and maybe a little gut instinct. You&amp;#8217;ll get better an estimating the appropriate reward as you get more experience&lt;/p&gt;
&lt;h3&gt;Putting it together&lt;/h3&gt;
&lt;p&gt;It always looks easier than it is, but in this case we&amp;#8217;ve got a lot of different variables to work with. The first step is to establish a price and build from that. You can also attempt to fit more than one task into a &lt;span class="caps"&gt;HIT&lt;/span&gt;; if they are simple, you may lower your cost per &lt;span class="caps"&gt;HIT&lt;/span&gt; by allowing workers to spend more time on the HITs and less on page reloads.&lt;/p&gt;
&lt;p class="note"&gt;Integrating Mechanical Turk can be a daunting task &amp;#8211; let us build a solution that&amp;#8217;s easy to administer and get Mechanical Turk working for you and not just sucking up your time. We built and maintain the only open-source &lt;a href="http://github.com/markpercival/rturk"&gt;Ruby library for Mechanical Turk&lt;/a&gt;, so why not let us do the hard part. &lt;a href="/contact.html"&gt;Give us a call!&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/squarepush/~4/d_tb7S1JpwU" height="1" width="1"/&gt;</content>
   
 <feedburner:origLink>http://squarepush.com//articles/2009/08/26/the-economics-of-mechanical-turk</feedburner:origLink></entry>
 
 <entry>
   
     <title>2D Barcode size and experiences</title>
     <link href="http://feedproxy.google.com/~r/squarepush/~3/3BogKbPJw-g/qr-code-size-and-uppercase-characters" />
     <updated>2009-08-25T00:00:00-04:00</updated>
     <id>http://litanyagainstfear.com/articles/2009/08/25/qr-code-size-and-uppercase-characters</id>
     <content type="html">&lt;p&gt;I went to see District 9 the other day, and while I was in the lobby I noticed the new poster for Tim Burtons &amp;#8216;9&amp;#8217; movie. If you haven&amp;#8217;t seen it, I&amp;#8217;ve included it below&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/articles/qr-codes/9-qr-code-poster.jpg" title="9 Movie Poster" alt="9 Movie Poster" /&gt;&lt;/p&gt;
&lt;p&gt;In the center of the poster is a type of 2D barcode called a QR code, which the consumer can scan with a cellphone camera(with software) and be sent to the &lt;span class="caps"&gt;URL&lt;/span&gt; encoded in the barcode. This article assumes you already know a little about these.&lt;/p&gt;
&lt;p&gt;The first thing I noticed of was the unusually dense barcode. Normally I&amp;#8217;d expect a barcode to look like this&lt;/p&gt;
&lt;p&gt;&lt;img src="http://chart.apis.google.com/chart?chs=250x250&amp;amp;cht=qr&amp;amp;chl=HTTP://9.MSITE.TV/QR1&amp;amp;choe=UTF-8" title="Small QR Code" alt="Small QR Code" /&gt;&lt;/p&gt;
&lt;p&gt;Which is 21&amp;#215;21 blocks of data. But the QR Code on the &amp;#8216;9&amp;#8217; poster was 29&amp;#215;29, a huge difference. Why does it matter? You see currently when you use a QR Code in todays market, a disproportionally large number of your &amp;#8216;clicks&amp;#8217; will come from iPhones. And since these are phone without an autofocus lens, you have can&amp;#8217;t get them too close to the code you&amp;#8217;re reading or the image will be blurry and you&amp;#8217;ll be unable to scan the code. In this case the poster was large enough that it could be read by an iPhone, but the more dense the barcode the harder it is to read and the longer it takes. &lt;em&gt;The phone processor has 841 blocks to deal with rather than 441, a 91% increase&lt;/em&gt;.&lt;/p&gt;
&lt;h3&gt;What&amp;#8217;s the solution&lt;/h3&gt;
&lt;p&gt;Well here&amp;#8217;s the embarrassing part for &lt;a href="http://thehyperfactory.com"&gt;Hyperfactory Ltd&lt;/a&gt;, the company that built the mobile campaign &amp;#8211; the poster&amp;#8217;s code above and the smaller example below it are the same. They resolve to the same &lt;span class="caps"&gt;URL&lt;/span&gt;, work on all QR code readers, and both meet the Denso Wave standard &amp;#8211; with one exception, I uppercased all the characters in the &lt;span class="caps"&gt;URL&lt;/span&gt; on the smaller one. &lt;em&gt;That&amp;#8217;s right, by using only uppercase &lt;span class="caps"&gt;ASCII&lt;/span&gt; characters, I reduced the complexity of the barcode by 47%.&lt;/em&gt; The reason, when using only uppercase &lt;span class="caps"&gt;ASCII&lt;/span&gt; characters, the encoding method reverts to a simpler character set which results in more compression.&lt;/p&gt;
&lt;p&gt;Now I can&amp;#8217;t fault Hyperfactory too much; it&amp;#8217;s a new technology and a market that&amp;#8217;s greatly misunderstood. But now their users have to deal with scanning a more complicated barcode, and the likelihood successful scans decreases. So it&amp;#8217;s still a costly mistake none the less.&lt;/p&gt;
&lt;h3&gt;QR Codes are actually quite simple&lt;/h3&gt;
&lt;p&gt;Actually, ridiculously simple. In fact there are only 2 rules you need to know when generating your own codes&lt;/p&gt;
&lt;ol&gt;
	&lt;li&gt;Use only uppercase characters in the &lt;span class="caps"&gt;URL&lt;/span&gt;, and avoid complicated URL&amp;#8217;s. By complicated I mean URL&amp;#8217;s with &amp;#8216;?&amp;#8217; or &amp;#8216;&amp;amp;&amp;#8217; in them, which force the barcode complexity to increase dramatically. This usually happens when you have a long &lt;span class="caps"&gt;URL&lt;/span&gt; that includes extra data, such as a Google Map&amp;#8217;s &lt;span class="caps"&gt;URL&lt;/span&gt;. Simply run these through a service like &lt;a href="http://bit.ly"&gt;bit.ly&lt;/a&gt;. &lt;em&gt;Note: You&amp;#8217;ll need to use the custom address feature and give it a name with all uppercase characters, as bit.ly is case sensitive&lt;/em&gt;&lt;/li&gt;
	&lt;li&gt;Keep it under 25 characters but don&amp;#8217;t worry about getting it as short as possible. A 14 character &lt;span class="caps"&gt;URL&lt;/span&gt; results in the same 21&amp;#215;21 barcode as a 25 character &lt;span class="caps"&gt;URL&lt;/span&gt;. So instead of making it cryptic and small(e.g. http://9.msite.tv/qr1), use the space to build a nice looking &lt;span class="caps"&gt;URL&lt;/span&gt;. For example, in this case, &lt;span class="caps"&gt;HTTP&lt;/span&gt;://M.9THEMOVIE.&lt;span class="caps"&gt;COM&lt;/span&gt;/1 &amp;#8211; where 1 is your tracking number for the poster.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;Resources&lt;/h4&gt;
&lt;p&gt;Need to quickly generate a QR code, check out these resources:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;a href="http://code.google.com/apis/chart/types.html#qrcodes"&gt;The Google Charts &lt;span class="caps"&gt;API&lt;/span&gt;&lt;/a&gt; &amp;#8211; Free and fast, but requires some basic web knowledge. It&amp;#8217;s a great solution to integrate into an existing web application, and that&amp;#8217;s why it&amp;#8217;s our personal favorite.&lt;/li&gt;
	&lt;li&gt;&lt;a href="http://invx.com/"&gt;&lt;span class="caps"&gt;INVX&lt;/span&gt; encoder&lt;/a&gt; &amp;#8211; This is a the more user friendly of the two; it allows you to quickly generate 2D barcodes on the fly. Just plug in the &lt;span class="caps"&gt;URL&lt;/span&gt; and go. Better than the Kaywa encoder in my opinion, as Kaywa tends to add unnecessary error correction to the codes.&lt;/li&gt;
&lt;/ul&gt;
&lt;p class="note"&gt;Don&amp;#8217;t get fooled by the marketing firms that make QR codes and mobile campaigns seem difficult. If you have any questions about QR codes, and how to integrate them into you web application, &lt;a href="/contact.html"&gt;give us a call&lt;/a&gt;. We can usually build a solution in a matter of days that get the results you need, without a lot of extra work. Really, it&amp;#8217;s that simple!&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/squarepush/~4/3BogKbPJw-g" height="1" width="1"/&gt;</content>
   
 <feedburner:origLink>http://squarepush.com//articles/2009/08/25/qr-code-size-and-uppercase-characters</feedburner:origLink></entry>
 
 <entry>
   
   
   
 </entry>
 
 <entry>
   
   
   
 </entry>
 
 <entry>
   
     <title>Mechanical Turk in Ruby</title>
     <link href="http://feedproxy.google.com/~r/squarepush/~3/FhtjadRcUbY/mechanical-turk-in-ruby" />
     <updated>2009-08-11T00:00:00-04:00</updated>
     <id>http://litanyagainstfear.com/articles/2009/08/11/mechanical-turk-in-ruby</id>
     <content type="html">&lt;p class="note"&gt;This is part of a series of Mechanical Turk blog posts &amp;#8211; check out more at &lt;a href="/ps/2009/08/26/the-economics-of-mechanical-turk/"&gt;Economics of Mechanical Turk&lt;/a&gt;, &lt;a href="/ps/2009/08/11/mechanical-turk-and-jquery-external-pages/"&gt;Mechanical Turk Pages in JQuery&lt;/a&gt;, and Building Mechanical Turk into Rails(coming soon).&lt;/p&gt;
&lt;p&gt;If you&amp;#8217;ve used Mechanical Turk, you already understand some of the initial challenges of getting a successful process in place. Even without getting into the development side of it, Mechanical Turk involves a great deal more than a technical problem that can be solved by a good &lt;span class="caps"&gt;API&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;There&amp;#8217;s the issue of payment, task design, and processing the results. It&amp;#8217;s actually an incredibly challenging problem, which we&amp;#8217;ve had fun solving. The variable list is huge, and eventually you start asking yourself questions like &amp;#8220;What&amp;#8217;s the best time to request a task if the ideal worker is in the Phillipines?&amp;#8221;&lt;/p&gt;
&lt;p&gt;I&amp;#8217;m going to first touch the basics, then move into code and development using our &lt;a href="http://github.com/markpercival/rturk"&gt;RTurk ruby gem&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;What is Mechanical Turk&lt;/h2&gt;
&lt;p&gt;Simply, it&amp;#8217;s a service where you can ask a human worker to perform a task. It can be anything, quite literally. You can ask them to verify a picture is not vulgar, or call and do phone number verification. So when I go through some of the details I&amp;#8217;m going to give some easy sample problems we can solve and narrow down the complexity.&lt;/p&gt;
&lt;h3&gt;How it works &amp;#8211; Worker side&lt;/h3&gt;
&lt;p&gt;When a worker logs in, he sees a list of tasks available to him, the price and a basic description of the task to be performed. He clicks on one that he prefers and looks at the actual task. He can then choose to accept it, or go back to the list and choose a different task. At this point if he accepts the &lt;acronym title="Human Intelligence Task"&gt;&lt;span class="caps"&gt;HIT&lt;/span&gt;&lt;/acronym&gt; he&amp;#8217;s perform it and submits the answer.&lt;/p&gt;
&lt;p&gt;Let&amp;#8217;s have an example:&lt;/p&gt;
&lt;p&gt;The task is to look at a picture and select if it&amp;#8217;s vulgar or inappropriate. On his end all he sees is the Mechnical Turk website with a page framed with the question. It&amp;#8217;s a simple iframe with the page inside detailing the task, showing the picture and asking for a response and submission. It&amp;#8217;s your basic &lt;span class="caps"&gt;HTML&lt;/span&gt; form and it can be hosted on Amazon or offsite. Surround the the frame is the MTurk site with information like how much you&amp;#8217;ve made and other metrics.&lt;/p&gt;
&lt;h3&gt;How it works &amp;#8211; Requester side&lt;/h3&gt;
&lt;p&gt;You can use Amazons question builder, which uses a template and a simple web &lt;span class="caps"&gt;GUI&lt;/span&gt; editor to build a question for a worker, but we&amp;#8217;re developers and doing this in Ruby, so lets ignore that since it&amp;#8217;s quite trivial.&lt;/p&gt;
&lt;p&gt;What you need to know is that there are two ways to submit a &lt;span class="caps"&gt;HIT&lt;/span&gt;. Submit an &lt;span class="caps"&gt;XML&lt;/span&gt; document with the questions, requirements and layout information, which Amazon will transform into an &lt;span class="caps"&gt;HTML&lt;/span&gt; form which they host locally. Or submit a &lt;span class="caps"&gt;URL&lt;/span&gt; which Amazon will place via iframe inside their own site.&lt;/p&gt;
&lt;p&gt;I&amp;#8217;m not even going to touch the &lt;span class="caps"&gt;XML&lt;/span&gt; route. In my opinion it&amp;#8217;s far more complicated than it needs to be and doesn&amp;#8217;t give you near the freedom to build the form how you wish. The &amp;#8216;external &lt;span class="caps"&gt;HIT&lt;/span&gt;&amp;#8217;, as Amazon refers to it, gives you an incredible amount of freedom to craft the task page and even use javascript UI tweaks.&lt;/p&gt;
&lt;p&gt;When you make a request, you&amp;#8217;ll send up the external &lt;span class="caps"&gt;HIT&lt;/span&gt; url, the requirements, and reward amount in a restful request. Your external page should include a form, obviously, that posts to Amazon&amp;#8217;s external hit submission url, and one of your fields should include the AssignmentID so Amazon knows who it&amp;#8217;s from.(It&amp;#8217;ll be passed to you in the &lt;span class="caps"&gt;URL&lt;/span&gt;)&lt;/p&gt;
&lt;h2&gt;Let get to the code&lt;/h2&gt;
&lt;p&gt;I&amp;#8217;m going to keep this simple, so the example task will be to flag a picture if it&amp;#8217;s vulgar. And I&amp;#8217;m going to use static page external &lt;span class="caps"&gt;HIT&lt;/span&gt;. In this case we are going to drop the Turker(Amazon&amp;#8217;s term for the workers) to a page hosted on s3 at http://s3.amazonaws.com/squarepush.com/turk/picturerate.html&lt;/p&gt;
&lt;h3&gt;On the request side&lt;/h3&gt;
&lt;p&gt;It&amp;#8217;s as simple and writing up the properties and creating the &lt;span class="caps"&gt;HIT&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;Some things to note&lt;/p&gt;
&lt;ol&gt;
	&lt;li&gt;We are passing the picture_url parameter along with the &lt;span class="caps"&gt;URL&lt;/span&gt;&lt;/li&gt;
	&lt;li&gt;MaxAssignments lets more that one person perform this task(ie. let 3 people rate the same picture)&lt;/li&gt;
	&lt;li&gt;You&amp;#8217;ll see we have a reward and qualifications of 90% approval rate&lt;/li&gt;
	&lt;li&gt;In this case the turker will be directed to http://s3.amazonaws.com/squarepush.com/turk/picturerate.html?picture_url=example.jpg&amp;amp;AssignmentId=abcd12345 where you&amp;#8217;ll be able to get the AssignmentID from the &lt;span class="caps"&gt;URL&lt;/span&gt; and place it in the form to be submitted.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class='prettyprint lang-ruby'&gt;
require 'rturk'

props = {:Title=&amp;gt;"Tell me if this picture is safe for general audiences", 
         :MaxAssignments=&amp;gt;1, :LifetimeInSeconds=&amp;gt;3600, 
         :Reward=&amp;gt;{:Amount=&amp;gt;0.05, :CurrencyCode=&amp;gt;"USD"}, 
         :Keywords=&amp;gt;"twitter, blogging, writing, english", 
         :Description=&amp;gt;"I need to know if this picture is vulgar or adult only",
         :RequesterAnnotation=&amp;gt;"Example1",
         :AssignmentDurationInSeconds=&amp;gt;3600, :AutoApprovalDelayInSeconds=&amp;gt;3600, 
         :QualificationRequirement=&amp;gt;[{
           # Approval rate of greater than 90%
           :QualificationTypeId=&amp;gt;"000000000000000000L0", 
           :IntegerValue=&amp;gt;90, 
           :Comparator=&amp;gt;"GreaterThan", 
           :RequiredToPreview=&amp;gt;"false"
           }]
        }

@turk = RTurk::Requester.new(AWSAccessKeyId, AWSAccessKey, :sandbox =&amp;gt; true)
page = RTurk::ExternalQuestionBuilder.build(
  "http://s3.amazonaws.com/squarepush.com/turk/picturerate.html", :picture_url =&amp;gt; 'example.jpg')

# Turkers will be directed to http://s3.amazonaws.com/squarepush.com/turk/picturerate.html?picture_url=example.jpg&amp;amp;AssignmentId=abcd12345

@turk.create_hit(props, page)

&lt;/pre&gt;
&lt;p&gt;Want to know more about the landing page for the Turker &amp;#8211; I put it in a separate article &amp;#8211; &amp;#8220;Mechanical Turk External &lt;span class="caps"&gt;HIT&lt;/span&gt; Pages with JQuery&amp;#8221;&lt;/p&gt;
&lt;h3&gt;Getting the results&lt;/h3&gt;
&lt;p&gt;After you&amp;#8217;ve created your HIT&amp;#8217;s it becomes a waiting game. Turkers will look for promising HIT&amp;#8217;s that they believe are worth their time, open them up to preview them, then accept and complete them.&lt;/p&gt;
&lt;p&gt;If you&amp;#8217;re having multiple Turkers do a single task(getting more than one opinion of the picture for example), you&amp;#8217;ll have multiple assignments attached to that &lt;span class="caps"&gt;HIT&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;Amazon has what I consider to be a pretty goofy format for the answers, which come in the form of escaped &lt;span class="caps"&gt;XML&lt;/span&gt;, inside of a wrapper &lt;span class="caps"&gt;XML&lt;/span&gt; document. Yes, you read correctly &amp;#8211; an escaped &lt;span class="caps"&gt;XML&lt;/span&gt; document inside of an &lt;span class="caps"&gt;XML&lt;/span&gt; document. I&amp;#8217;ve taken the liberty of having the RTurk library go ahead and unescape it and pull it in as a hash. I hope you don&amp;#8217;t mind.&lt;/p&gt;
&lt;p&gt;Again, lets have an example (You&amp;#8217;ll need to have saved each &lt;span class="caps"&gt;HIT&lt;/span&gt; id from the creation, which I usually put in a database, but I won&amp;#8217;t go into that now)&lt;/p&gt;
&lt;pre class='prettyprint lang-ruby'&gt;
@turk = RTurk::Requester.new(aws['AWSAccessKeyId'], aws['AWSAccessKey'], :sandbox =&amp;gt; false)

answers = []
@turk.get_assignments_for_hit("ABCDEFG1234356789").each do |assignment|
   answers &amp;lt;&amp;lt; assignment['Answer']
end
&lt;/pre&gt;
&lt;p&gt;You should get answers that look something like this:&lt;br /&gt;
&lt;pre class='prettyprint lang-ruby'&gt; [&amp;#8216;inappropriate&amp;#8217; =&amp;gt; &amp;#8216;yes&amp;#8217;, &amp;#8216;assignmentId&amp;#8217; =&amp;gt; &amp;#8216;12345abcde&amp;#8217;]&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;At this point you can decline to pay the worker if you feel the task wasn&amp;#8217;t completed correctly, or let the worker get paid automatically (set via the &amp;#8216;AutoApprovalDelayInSeconds&amp;#8217;).&lt;/p&gt;
&lt;h3&gt;Tips and Tricks&lt;/h3&gt;
&lt;ol&gt;
	&lt;li&gt;Keep it external and build your own submission page. This could be a page on a rails app, or a simple &lt;span class="caps"&gt;HTML&lt;/span&gt; page with javascript. But keeping it external gives you the ability to customize as you see fit.&lt;/li&gt;
	&lt;li&gt;Pass a unique record id with each &lt;span class="caps"&gt;HIT&lt;/span&gt;. You can&amp;#8217;t assume that every incoming Turker completes the task, in fact many return it. Pass in an identifier to each &lt;span class="caps"&gt;HIT&lt;/span&gt; created and let Amazon deal with the messy details of returned HIT&amp;#8217;s and previews.&lt;/li&gt;
	&lt;li&gt;Make sure the page is easy to read and loads quickly. This will encourage Turkers to select and complete the &lt;span class="caps"&gt;HIT&lt;/span&gt;, and at lower prices.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Other Reading&lt;/h3&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;a href="http://github.com/markpercival/rturk"&gt;RTurk gem on github&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="http://docs.amazonwebservices.com/AWSMechanicalTurkRequester/2008-08-02/"&gt;The Amazon Mechanical Turk &lt;span class="caps"&gt;API&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 class="note"&gt;Need Help?&lt;/h3&gt;
&lt;p class="note"&gt;Although we wrote and open sourced the RTurk gem with the intention of giving everyone easier access to Mechanical Turk, it&amp;#8217;s still a complex system with a steep learning curve. &lt;a href="/contact.html"&gt;Give us a call&lt;/a&gt; if you need help &amp;#8211; we&amp;#8217;re the Ruby experts when it comes to Mechanical Turk.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/squarepush/~4/FhtjadRcUbY" height="1" width="1"/&gt;</content>
   
 <feedburner:origLink>http://squarepush.com//articles/2009/08/11/mechanical-turk-in-ruby</feedburner:origLink></entry>
 
 <entry>
   
     <title>Mechanical Turk External Page with JQuery</title>
     <link href="http://feedproxy.google.com/~r/squarepush/~3/WRcXZD6Y3PI/mechanical-turk-and-jquery-external-pages" />
     <updated>2009-08-11T00:00:00-04:00</updated>
     <id>http://litanyagainstfear.com/articles/2009/08/11/mechanical-turk-and-jquery-external-pages</id>
     <content type="html">&lt;p class="note"&gt;This is part of a series of Mechanical Turk blog posts &amp;#8211; check out more at &lt;a href="/ps/2009/08/26/the-economics-of-mechanical-turk/"&gt;Economics of Mechanical Turk&lt;/a&gt;, &lt;a href="/ps/2009/08/11/mechanical-turk-in-ruby/"&gt;Mechanical Turk in Ruby&lt;/a&gt;, and Building Mechanical Turk into Rails(coming soon).&lt;/p&gt;
&lt;p&gt;If you lazy, and all good programmers are, you don&amp;#8217;t want to spend too much time building the external pages for your Turkers to land on. Ideally you&amp;#8217;d like to be able to simply pass in a variable, have them perform the task, and submit it &amp;#8211; all without you having to be involved.&lt;/p&gt;
&lt;p&gt;Amazon currently uses a static page with javascript in it&amp;#8217;s example ruby pack, and while it&amp;#8217;s a perfectly acceptable example, I prefer to use JQuery when I&amp;#8217;m doing any significant page manipulation. So what I&amp;#8217;ve done is rewrite the page in a more unobtrusive manner and using some tried and true tools to get the work done.&lt;/p&gt;
&lt;h3&gt;Get to the code already!&lt;/h3&gt;
&lt;pre class='prettyprint'&gt;
&amp;lt;!-- This file needs to be hosted on an external server.  --&amp;gt;
&amp;lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&amp;gt;
&amp;lt;html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"&amp;gt;
&amp;lt;head&amp;gt;
  &amp;lt;meta http-equiv="Content-Type" content="text/html;charset=utf-8" /&amp;gt;
  &amp;lt;title&amp;gt;Title, an XHTML requirement. This message brought to you by the W3C&amp;lt;/title&amp;gt;
	&amp;lt;script src="http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js" type="text/javascript"&amp;gt;&amp;lt;/script&amp;gt;
	&amp;lt;script src="http://projects.allmarkedup.com/jquery_url_parser/jquery.url.packed.js" type="text/javascript"&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
&amp;lt;form id="mturk_form" method="post" action="http://www.mturk.com/mturk/externalSubmit" onSubmit="return validFormCheck()"&amp;gt;
&amp;lt;input type="hidden" id="assignmentId" name="assignmentId" value="" /&amp;gt;
&amp;lt;h1&amp;gt;Is this picture inappropriate?&amp;lt;/h1&amp;gt;

&amp;lt;h2&amp;gt;Criteria&amp;lt;/h2&amp;gt;
&amp;lt;ul&amp;gt;
  &amp;lt;li&amp;gt;Look at the picture and tell me if it's inappropriate&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;   
&amp;lt;div id='picture'&amp;gt;&amp;lt;/div&amp;gt;                                 
&amp;lt;input type="radio" name="innappropriate" value="no" /&amp;gt;No&amp;lt;br&amp;gt;
&amp;lt;input type="radio" name="innappropriate" value="yes" /&amp;gt;Yes&amp;lt;br&amp;gt;
&amp;lt;p/&amp;gt;
&amp;lt;input id="submitButton" type="submit" name="Submit" value="Submit" /&amp;gt;
&amp;lt;p/&amp;gt;

&amp;lt;/form&amp;gt;
&amp;lt;script type='text/javascript'&amp;gt;
    /*&amp;lt;![CDATA[*/
 		// From the Jquery plugin - http://projects.allmarkedup.com/jquery_url_parser/
		var assignmentId = $.url.param("assignmentID");
  	$('#assignmentId').value = assignmentId;


    //
    // Check if the worker is PREVIEWING the HIT or if they've ACCEPTED the HIT
    //
    if (assignmentId == "ASSIGNMENT_ID_NOT_AVAILABLE")
    {
      // If we're previewing, disable the button and give it a helpful message
      $('#submitButton').disabled = true;
      $('#submitButton').value = "You must ACCEPT the HIT before you can submit the results.";
    }
		//
		// Now show the image
		//
		$('#picture').append($('&amp;lt;img&amp;gt;').attr('src', $.url.param("picture_uri")));



    /*]]&amp;gt;*/
&amp;lt;/script&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/pre&gt;
&lt;h4&gt;What&amp;#8217;s going on here:&lt;/h4&gt;
&lt;ol&gt;
	&lt;li&gt;First we disable the submit button to keep the user from submitting it without first accepting the assignment. This happens when a Turker is previewing the task.&lt;/li&gt;
	&lt;li&gt;If they&amp;#8217;ve accepted it, and we have an AssignmentID, we go ahead and put the AssignmentID in the form. This is the only way Amazon can know whom the submission belong to.&lt;/li&gt;
	&lt;li&gt;We grab the picture &lt;span class="caps"&gt;URI&lt;/span&gt; from the &lt;span class="caps"&gt;URL&lt;/span&gt; params and display it to the user via JQuery. Obviously we&amp;#8217;re assuming that your task will pass a picture_uri parameter.&lt;/li&gt;
&lt;/ol&gt;&lt;img src="http://feeds.feedburner.com/~r/squarepush/~4/WRcXZD6Y3PI" height="1" width="1"/&gt;</content>
   
 <feedburner:origLink>http://squarepush.com//articles/2009/08/11/mechanical-turk-and-jquery-external-pages</feedburner:origLink></entry>
 
 <entry>
   
     <title>Jekyll Sitemaps</title>
     <link href="http://feedproxy.google.com/~r/squarepush/~3/ku1VJ1e46gU/jekyll-sitemap-xml" />
     <updated>2009-08-10T00:00:00-04:00</updated>
     <id>http://litanyagainstfear.com/articles/2009/08/10/jekyll-sitemap-xml</id>
     <content type="html">&lt;p&gt;We build and deploy this site with &lt;a href='http://jekyllrb.com'&gt;Jekyll&lt;/a&gt;, and one thing I haven't seen written about is a sitemap for Jekyll. Sitemaps are really simple and obvious, but it should save you about 15 minutes if your looking to do a quick sitemap in jekyll. Here's what we currently use for this site.&lt;/p&gt;

&lt;pre class='prettyprint lang-html'&gt;
---
rooturi: http://squarepush.com
---
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;
&amp;lt;urlset xmlns=&amp;quot;http://www.sitemaps.org/schemas/sitemap/0.9&amp;quot;&amp;gt;
   &amp;lt;url&amp;gt;
      &amp;lt;loc&amp;gt;http://squarepush.com/&amp;lt;/loc&amp;gt;
      &amp;lt;lastmod&amp;gt;{&amp;#123; site.time | date: &amp;#x27;%Y-%m-%d&amp;#x27; &amp;#125;}&amp;lt;/lastmod&amp;gt;
      &amp;lt;changefreq&amp;gt;weekly&amp;lt;/changefreq&amp;gt;
      &amp;lt;priority&amp;gt;1&amp;lt;/priority&amp;gt;
   &amp;lt;/url&amp;gt;
   &amp;lt;url&amp;gt;
      &amp;lt;loc&amp;gt;http://squarepush.com/contact.html&amp;lt;/loc&amp;gt;
      &amp;lt;lastmod&amp;gt;{&amp;#123; site.time | date: &amp;#x27;%Y-%m-%d&amp;#x27; &amp;#125;}&amp;lt;/lastmod&amp;gt;
      &amp;lt;changefreq&amp;gt;weekly&amp;lt;/changefreq&amp;gt;
      &amp;lt;priority&amp;gt;0.8&amp;lt;/priority&amp;gt;
   &amp;lt;/url&amp;gt;
   &amp;lt;url&amp;gt;
      &amp;lt;loc&amp;gt;http://squarepush.com/about.html&amp;lt;/loc&amp;gt;
      &amp;lt;lastmod&amp;gt;{&amp;#123; site.time | date: &amp;#x27;%Y-%m-%d&amp;#x27; &amp;#125;}&amp;lt;/lastmod&amp;gt;
      &amp;lt;changefreq&amp;gt;weekly&amp;lt;/changefreq&amp;gt;
      &amp;lt;priority&amp;gt;0.8&amp;lt;/priority&amp;gt;
   &amp;lt;/url&amp;gt;
   &amp;lt;url&amp;gt;
      &amp;lt;loc&amp;gt;http://squarepush.com/ps/&amp;lt;/loc&amp;gt;
      &amp;lt;lastmod&amp;gt;{&amp;#123; site.time | date: &amp;#x27;%Y-%m-%d&amp;#x27; &amp;#125;}&amp;lt;/lastmod&amp;gt;
      &amp;lt;changefreq&amp;gt;daily&amp;lt;/changefreq&amp;gt;
      &amp;lt;priority&amp;gt;1&amp;lt;/priority&amp;gt;
   &amp;lt;/url&amp;gt;
{&amp;#37; for post in site.posts &amp;#37;}
   &amp;lt;url&amp;gt;
      &amp;lt;loc&amp;gt;{&amp;#123; page.rooturl &amp;#125;}{&amp;#123; post.url &amp;#125;}&amp;lt;/loc&amp;gt;
      &amp;lt;lastmod&amp;gt;{&amp;#123; site.time | date: &amp;#x27;%Y-%m-%d&amp;#x27; &amp;#125;}&amp;lt;/lastmod&amp;gt;
      &amp;lt;changefreq&amp;gt;weekly&amp;lt;/changefreq&amp;gt;
      &amp;lt;priority&amp;gt;0.8&amp;lt;/priority&amp;gt;
   &amp;lt;/url&amp;gt;
{&amp;#37; endfor &amp;#37;}
&amp;lt;/urlset&amp;gt;
&lt;/pre&gt;&lt;img src="http://feeds.feedburner.com/~r/squarepush/~4/ku1VJ1e46gU" height="1" width="1"/&gt;</content>
   
 <feedburner:origLink>http://squarepush.com//articles/2009/08/10/jekyll-sitemap-xml</feedburner:origLink></entry>
 
 <entry>
   
     <title>Time Taken - A git time tracker</title>
     <link href="http://feedproxy.google.com/~r/squarepush/~3/SavW7MqHjHo/time-taken-git-time-tracker" />
     <updated>2009-08-06T00:00:00-04:00</updated>
     <id>http://litanyagainstfear.com/articles/2009/08/06/time-taken-git-time-tracker</id>
     <content type="html">&lt;p&gt;If git is such an integral part of your day job, and you work on the clock, wouldn&amp;#8217;t it be nice to combine tracking time with your git commits? Tracking time can be difficult when you have such a different system. I still use a timer to keep track of what project I&amp;#8217;m working on, but inherently there&amp;#8217;s a disconnect from the code. If you&amp;#8217;ve got more technically savvy clients that like to keep track of the git log, combining time tracking also allows them to get an idea or how long something takes and set future expectations.&lt;/p&gt;
&lt;p&gt;But where do we log the time. You could quickly look at a days logs and tally up the time, but with all that data you should be able to mine it for a little more insight than just billing. This is where Google Spreadsheets comes into play. If you weren&amp;#8217;t already aware, Google Spreadsheets now allows you to create a html form for your spreadsheet and let people enter information into it from any website. While this is a cool feature in and of itself, it has the sideeffect of letting of simple &amp;#8216;get&amp;#8217; or &amp;#8216;post&amp;#8217; information from anywhere: the command line, curl, net/http in ruby. By now, where I&amp;#8217;m going with this is pretty obvious.&lt;/p&gt;
&lt;p&gt;A post-receive/post-commit hook &amp;#8211; what I want is to be able to add time taken to a git message like this:&lt;/p&gt;
&lt;pre&gt; git commit -a -m "Added a user method for activation [TT 30m]" &lt;/pre&gt;
&lt;p&gt;Then when I send it up, or commit it(depending on your setup), I should see log entry for that time show up in my Google spreadsheet.&lt;/p&gt;
&lt;p&gt;Here&amp;#8217;s the post-commit script&lt;/p&gt;
&lt;pre class='prettyprint lang-ruby'&gt;
#!/usr/bin/ruby -w

# Time Taken
# A ridiculously simple way to track time in git using Google Spreadsheets
# Copyright: 2009 Mark Percival - SquarePush LLC - squarepush.com
# License: MIT

# Installation
# 1. Create Google spreadsheet and update configuration
# 2. Copy this file to .git/hooks/post-commit in your project
# 3. chmod +x .git/hooks/post-commit

# Usage:
# git commit -a -m "Updated to rails 2.3.2 [TT 30m]"
# git commit -a -m "WTF? No 'goto' in Ruby? [TT 2.5h]"
# git commit -a -m "Switched back to Merb [tt 1.25H]"  &amp;lt;--case insesitive

# How to set this up
#
# Start by building a spreadsheet with the following columns
# Client | Project | Minutes | Message | Commit | Committed By
#
# Then create a form for it and go look at it
# Inside the form source you'll see the post URI
# It should look something like this
# http://spreadsheets.google.com/a/mpercival.com/viewform?hl=en&amp;amp;formkey=dDVWVTZvYmg45ExeVEp0Z0RlN3lCcWc6MA..
# This is all we need to talk with google - The rest should be pretty self explanatory

# Configuration
# The only stuff you need to worry about
# ------------------------------------------------------------------------
form_post_uri="http://spreadsheets.google.com/a/mpercival.com/formResponse?formkey=dDV2VTZFYmg3ME7PVEp240RlN3lCcWc6MA.."
client='Github'
project='Speed improvements'
committer='Mark Percival'
# ------------------------------------------------------------------------



last_log = `git log --pretty=oneline -n1`
last_log.strip.match(/^([0-9a-z]+)\s(.+)$/m)

commit = $1
message = $2

if match = message.match(/\[TT\s(.+)(.)\]/i) 
  time = match[1]
  unit = match[2].downcase
  case unit
  when 'm'
    minutes = time.to_i 
  when 'h'
    minutes = (time.to_f * 60).ceil
  else
    puts "Error logging Time Taken \"#{match[0]}\" \n"
  end
end

if minutes
  # Require only if we have minutes to log, since this is the slowest part
  require 'net/http'
  require 'uri'
  require 'cgi'
  client = CGI.escape(client)
  project = CGI.escape(project)
  message = CGI.escape(message)
  committer = CGI.escape(committer)
  puts "Logging #{time} #{unit == 'h' ? 'hour' : 'minute'}#{time.to_s == '1' ? '' : 's'} to #{client} - #{project} \n"
  begin
    Net::HTTP.get(URI.parse(
    "#{form_post_uri}&amp;amp;entry.0.single=#{client}&amp;amp;entry.1.single=#{project}&amp;amp;entry.2.single=#{minutes}&amp;amp;entry.3.single=#{message}&amp;amp;entry.4.single=#{commit}&amp;amp;entry.5.single=#{committer}"))
  rescue Exception =&amp;gt; e 
    puts '*' * 50
    puts "There was an error updating the spreadsheet - #{e.message}\n"
    puts '*' * 50    
  end
end

# Fin
exit(0)
&lt;/pre&gt;&lt;img src="http://feeds.feedburner.com/~r/squarepush/~4/SavW7MqHjHo" height="1" width="1"/&gt;</content>
   
 <feedburner:origLink>http://squarepush.com//articles/2009/08/06/time-taken-git-time-tracker</feedburner:origLink></entry>
 
 <entry>
   
   
   
 </entry>
 
 
</feed>

