<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Princess Polymath</title>
	<atom:link href="http://www.princesspolymath.com/princess_polymath/?feed=rss2&#038;p=193" rel="self" type="application/rss+xml" />
	<link>http://www.princesspolymath.com/princess_polymath</link>
	<description></description>
	<lastBuildDate>Fri, 15 Mar 2013 17:46:58 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.5.1</generator>
		<item>
		<title>&#8220;Working&#8221; from Home</title>
		<link>http://www.princesspolymath.com/princess_polymath/?p=612</link>
		<comments>http://www.princesspolymath.com/princess_polymath/?p=612#comments</comments>
		<pubDate>Wed, 06 Mar 2013 21:24:39 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://www.princesspolymath.com/princess_polymath/?p=612</guid>
		<description><![CDATA[Telecommuting has been all the buzz this past week, with Yahoo eliminating work from home options for their fulltime telecommuters (but you can still wait for the cable guy!) and Best Buy following their lead soon after.  The articles covering these changes are quite interesting in their slant. From Mashable, Best Buy&#8217;s announcement was spun [...]]]></description>
				<content:encoded><![CDATA[<p>Telecommuting has been all the buzz this past week, with Yahoo eliminating work from home options for their fulltime telecommuters (but you can still wait for the cable guy!) and Best Buy following their lead soon after.  The articles covering these changes are quite interesting in their slant.</p>
<p>From <a href="http://mashable.com/2013/03/05/best-buy-flexible-employees/">Mashable</a>, Best Buy&#8217;s announcement was spun this way:</p>
<blockquote><p>Best Buy announced Monday that it has eliminated a flexible work program introduced in 2005 that gave corporate employees the freedom to work when and where they chose. Through the program, known as Results Only Work Environment, employees were assessed based solely on the quality of work they got done rather than whether or not they showed up to the office. Now, these employees will be expected to work a standard 40-hour workweek and come into the office &#8220;as much as possible.&#8221;</p></blockquote>
<p>So let me get this straight.  The quality of work you do is less important to Best Buy than where you do it.  Seriously.</p>
<p>And in the <a href="http://www.nytimes.com/2013/03/06/technology/yahoos-in-office-policy-aims-to-bolster-morale.html?_r=0&amp;adxnnl=1&amp;adxnnlx=1362577921-y8avcKw+Cr5IeZTI+6CN7Q">New York Times</a> article on Yahoo&#8217;s shift:</p>
<blockquote>
<p itemprop="articleBody">Parking lots and entire floors of cubicles were nearly empty because some employees were working as little as possible and leaving early.</p>
<p itemprop="articleBody">Then there were the 200 or so people who had work-at-home arrangements. Although they collected Yahoo paychecks, some did little work for the company and a few had even begun their own start-ups on the side.</p>
</blockquote>
<p itemprop="articleBody">Does this strike anyone else as odd?  When you have employees who are not getting work done, you fire them.  Managers are supposed to be held responsible for the productivity of their groups.  If your managers can&#8217;t manage people except by counting the hours they&#8217;re in the office, then they should be fired as well.</p>
<p itemprop="articleBody">I&#8217;ve been working for more than 20 years in an industry where I have to focus closely on my work sometimes, brainstorm sometimes, invent sometimes, and always be creative and alert.  Cube farms are soul sucking zombie cages where I just don&#8217;t produce nearly as much.  I am far more productive when working on my own schedule from where I want and I far exceed the output and effectiveness of most people I know.  I have a ransom note on top of my LinkedIn profile which somewhat arrogantly announces that I only want to work for people who want me to work for them.</p>
<p itemprop="articleBody">I have fantastic skills, insight, and motivation.  I will do my best work and encourage and assist others to do the same.  I have experience in varied areas which allow me to complement those around me well.  Sitting in a seat in the office is not something I&#8217;m particularly good at, nor is it something I want to be measured on.</p>
<p itemprop="articleBody">Best Buy and Yahoo are saying they need to rely on the same metrics used by retail establishments.  If you clock in and out on time and don&#8217;t annoy the customers, you&#8217;re doing well.  Your actual contributions to the company are not as important as your presence in the office.  How motivating is that? I can clock in, play scrabble, clock out and I should be fine.  Fortunately for my employer, that sounds horrendous to me.  I love contributing, fixing problems, helping people and getting things done.  Fortunately for me, that&#8217;s what they keep me around for.</p>
<p itemprop="articleBody">A friend of mine pointed out how ironic it was that Marissa Meyer instituted this policy while spending the money and resources to have a <a href="http://daily.jml.is/marissa-mayer-yahoo-ceo-who-banned-telecommuting-had-nursery-installed-in-office-291/">nursery installed in her office</a> at Yahoo.  Perhaps, he suggests, these struggling companies are simply trying to increase attrition to help fend off a future layoff.  If someone&#8217;s built their life structure around a particular work arrangement and has to move or find child care, they may just&#8230; leave.  And if they quit, you don&#8217;t have to fire them.  Win-win, I guess.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.princesspolymath.com/princess_polymath/?feed=rss2&#038;p=612</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Happy Developers</title>
		<link>http://www.princesspolymath.com/princess_polymath/?p=609</link>
		<comments>http://www.princesspolymath.com/princess_polymath/?p=609#comments</comments>
		<pubDate>Fri, 22 Feb 2013 15:04:31 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Geek Stuff]]></category>

		<guid isPermaLink="false">http://www.princesspolymath.com/princess_polymath/?p=609</guid>
		<description><![CDATA[I&#8217;ve always been all about removing barriers to help people get where they want to go.  It&#8217;s how I get gratification from my work, my play, my entire life.  This week at the amazing API Strategy and Practice Conference I&#8217;m surrounded by engaged, curious, challenging people who are committed to bringing the API world to [...]]]></description>
				<content:encoded><![CDATA[<p>I&#8217;ve always been all about removing barriers to help people get where they want to go.  It&#8217;s how I get gratification from my work, my play, my entire life.  This week at the amazing <a href="http://www.apistrategyconference.com">API Strategy and Practice Conference </a>I&#8217;m surrounded by engaged, curious, challenging people who are committed to bringing the API world to the next level.  We&#8217;ve grown the API ecosystem organically up until now, all doing our best to meet our own goals, but being around people who have looked at this world from so many different directions makes it possible for us to see the patterns.</p>
<p>I spoke yesterday on a panel on API design &#8211; and I talked, as I frequently do, about happy developers. Jakub Nešetřil of <a href="http://apiary.io">Apiary.io</a> made an insightful comment when he noted that we&#8217;re used to designing APIs for computers &#8211; they are, after all, programmatic interfaces. But in this world where developers are the interaction level, we have to make user interface choices. Developers are users of your API and if you don&#8217;t treat them as such they&#8217;ll be frustrated and less likely to be successful.</p>
<p>My talk, as always, was peppered with visual metaphors.  Developers are the wild animals out on the savannah, looking for a watering hole they like.  When they come to check out your API they&#8217;re giving you a valuable gift of something they&#8217;ll never get more of &#8211; their time.  Show them you value and respect that gift by communicating with them clearly about your product, your goals, and giving them ways to engage right away.  Play with them &#8211; provide tutorials and examples that give them a sense of progress (yes, level them up.  Game theory isn&#8217;t a bad thing.  Developers are, by nature, gamers.  Use that.)  Give them building blocks of working code, like legos so they can build new toys.  Play with your developers.  Seriously.</p>
<p>And support them.  Not just in the usual ways, but if you want to earn developer love, get them out there evangelizing your product, helping you support other users&#8230; teach them to be successful.  Give them tools, give them strategies, tell them how to solve the problems on their own so when they&#8217;re hacking at 3AM they can finish the toy they wanted to build.  Celebrate their successes with them because when you work with them, those successes are yours as well.</p>
<p>Your developers are users, yes, but they are also your peers.  They may have a different context or be coming to your system from a foreign programming world, but they want to interact with your product, and helping them to be successful is the best way to ensure the success of your own platform.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.princesspolymath.com/princess_polymath/?feed=rss2&#038;p=609</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Review: Intermediate Perl</title>
		<link>http://www.princesspolymath.com/princess_polymath/?p=603</link>
		<comments>http://www.princesspolymath.com/princess_polymath/?p=603#comments</comments>
		<pubDate>Tue, 11 Sep 2012 21:58:57 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://www.princesspolymath.com/princess_polymath/?p=603</guid>
		<description><![CDATA[As a programmer, I&#8217;m always looking for ways to take my coding practices to the next level. As most developers have, I&#8217;ve attended numerous conferences and sat through countless tutorials, only to find that while the topic is engaging during the talk, it doesn&#8217;t &#8220;stick&#8221; when I leave the room. The biggest exception to this [...]]]></description>
				<content:encoded><![CDATA[<p>As a programmer, I&#8217;m always looking for ways to take my coding practices to the next level. As most developers have, I&#8217;ve attended numerous conferences and sat through countless tutorials, only to find that while the topic is engaging during the talk, it doesn&#8217;t &#8220;stick&#8221; when I leave the room. The biggest exception to this was Randal Schwartz&#8217; &#8220;Packages, References, Objects and Modules&#8221; &#8211; I attended this talk upwards of ten years ago, and it still stands out in my head as the biggest stepping stone to improve my maturity as a programmer.</p>
<p>This book, &#8220;<a href="http://www.amazon.com/gp/product/1449393098/ref=as_li_tf_tl?ie=UTF8&amp;camp=1789&amp;creative=9325&amp;creativeASIN=1449393098&amp;linkCode=as2&amp;tag=triathalongwi-20">Intermediate Perl</a>&#8221; is the newest version of that curriculum, and it&#8217;s a fantastic way for someone who has become comfortable with perl but who finds themselves stuck copying other people&#8217;s code or doing things by rote because they don&#8217;t understand all the choices available to them. The book covers Perl&#8217;s reference system, which feels like black magic if you don&#8217;t understand it; it addresses ways to work with complex data and file handles, and advanced use of regular expressions. There&#8217;s also an excellent section on Perl&#8217;s OO functionality (such as it is) and testing.</p>
<p>As someone who has worked with perl for almost 20 years, who went through this class several years ago, I still found the book to be incredibly useful. I love Perl Best Practices, but that&#8217;s more a primer on doing things in a sensible and efficient way. This book takes the time to walk you through these concepts which are so central to Perl, from simple examples to more complex. Best of all, there are exercises at the back of each chapter which really require thought in applying the concepts just discussed. For me, this is the best way to really internalize new information &#8211; give me something to play with, some way to use it, and I&#8217;m much more likely to start using it in my own work.  Each exercise has a solution in the back of the book &#8211; and these solutions are thoughtfully explained so you understand why it works the way it does.</p>
<p>In short, &#8220;Intermediate Perl&#8221; is an excellent next step after &#8220;Learning Perl.&#8221;  I&#8217;d go so far as to say that it&#8217;s a good workbook for long-time Perl programmers to use &#8211; go through the exercises at the back of each chapter and see if there&#8217;s some way you can improve your coding, or something you&#8217;re doing inefficiently.  In the overwhelming sea of blue Perl books, this one provides a great set of exercises to take your development skills to the next level.</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
]]></content:encoded>
			<wfw:commentRss>http://www.princesspolymath.com/princess_polymath/?feed=rss2&#038;p=603</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Asking Questions Well</title>
		<link>http://www.princesspolymath.com/princess_polymath/?p=584</link>
		<comments>http://www.princesspolymath.com/princess_polymath/?p=584#comments</comments>
		<pubDate>Sun, 05 Aug 2012 17:21:04 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://www.princesspolymath.com/princess_polymath/?p=584</guid>
		<description><![CDATA[One of the most frequent issues developers (and support people) have is that questions aren&#8217;t asked completely, and both people end up enmeshed in an iterative clarification cycle, unable to resolve the issue until both people understand what&#8217;s going on. There&#8217;s a decent FAQ on asking questions on StackOverflow, which does tell you to share [...]]]></description>
				<content:encoded><![CDATA[<p>One of the most frequent issues developers (and support people) have is that questions aren&#8217;t asked completely, and both people end up enmeshed in an iterative clarification cycle, unable to resolve the issue until both people understand what&#8217;s going on. There&#8217;s a decent <a href="http://stackoverflow.com/questions/how-to-ask">FAQ on asking questions</a> on StackOverflow, which does tell you to share details and context, but I&#8217;m going to go a little further and give you a template for asking questions which should help you get answers quickly.</p>
<p>What does a good problem statement look like?</p>
<blockquote><p>I did X.<br />
I expected Y to happen.<br />
To my dismay, Z happened instead.</p></blockquote>
<p>All three pieces are necessary, because frequently the problem is your expectation, not the system you&#8217;re trying to use. To clarify, let&#8217;s look at an example.</p>
<blockquote><p>I jumped off a cliff.<br />
I expected to sprout wings and fly across the ravine.<br />
To my dismay, I plunged to my death instead.</p></blockquote>
<p>Bummer. But the problem wasn&#8217;t really about gravity, it was that you expected something to magically happen to counteract it. Frequently users don&#8217;t understand how a system works, or what should be expected &#8211; this doesn&#8217;t make your question bad, by any stretch of the imagination. Part of supporting a product is making sure your users know what to expect, and the documentation may need updating. But the support person isn&#8217;t going to be able to help you if they don&#8217;t know why you think it&#8217;s broken. It may seem obvious to you what should happen, but trust me &#8211; more often than not including this piece will get you to your answer faster.</p>
<p>So, let&#8217;s go through this piece by piece&#8230;</p>
<h2>I did X</h2>
<p>When you&#8217;re explaining what you did, you need to include all the context you can.</p>
<ul>
<li>What were you doing, exactly?  Can you reproduce the issue consistently?</li>
<li>Was it a web issue?  If so, which browser were you using?  Did you try other ones?</li>
<li>What exact URL or application were you using?  How can someone exactly recreate what you were doing?</li>
<li>If you&#8217;re coding, what language are you using?  Are you using a library?  Include an example of the specific code in question, with pointers to the library and examples you&#8217;re following (if applicable)</li>
<li>If you&#8217;re using a web service, what exactly did your request look like?  What were the headers and URL?  If you don&#8217;t know how to get those pieces, you need to know &#8211; read <a href="https://developer.linkedin.com/documents/debugging-api-calls">this article</a> for some pointers.</li>
</ul>
<h2>I expected Y to happen</h2>
<p>What did you hope to happen?  Lots of times with an error message or broken request, it&#8217;s obvious what you wanted to happen.  It&#8217;s still worth your while to include a quick statement.  But if you got a successful response that didn&#8217;t match what your expectation was, explain completely and clearly what you were expecting to get, and why.  Point to the documentation supporting your expectation (because it may well need an update or clarification).</p>
<h2>To my dismay, Z happened instead</h2>
<p>I try to always keep the &#8220;To my dismay&#8230;&#8221; part in there.  It amuses me, it amuses the person answering my question, and it acknowledges the truth of asking a question in this situation &#8211; I&#8217;m frustrated and sad that I&#8217;m stuck because something isn&#8217;t working how I want.</p>
<p>But just like with the &#8220;I tried X&#8221; part, you need to be very specific about what happened.</p>
<ul>
<li>Did you get an error in a console or as a result? What did it say, exactly?</li>
<li>What did you try to get around the issue?</li>
<li>If you&#8217;re using a web service, what were the headers, status code, and/or body returned by the server</li>
<li>Note that if you are using a library that wraps a web service but asking for help from the API provider themselves, you likely need to dig down and see what the call itself looks like (as the API provider probably doesn&#8217;t support the third party libraries)</li>
</ul>
<p>Following these guidelines will mean it takes a little longer for you to craft your question, but it will save you time, and most importantly, it will save people who might have the answer time. Posting incomplete questions wastes the time and energy of people who might otherwise help you (and they may not come back to see if you&#8217;ve added clarification). Showing them that you value their time and energy by completely explaining the issue will increase your chances of getting a complete answer &#8211; and if iteration cycles are needed they&#8217;ll be much more valuable.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.princesspolymath.com/princess_polymath/?feed=rss2&#038;p=584</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>LinkedIn Today SMS Notification System using LinkedIn, Twilio, Django and Heroku</title>
		<link>http://www.princesspolymath.com/princess_polymath/?p=509</link>
		<comments>http://www.princesspolymath.com/princess_polymath/?p=509#comments</comments>
		<pubDate>Fri, 23 Dec 2011 00:23:16 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[heroku]]></category>
		<category><![CDATA[linkedin]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[twilio]]></category>

		<guid isPermaLink="false">http://www.princesspolymath.com/princess_polymath/?p=509</guid>
		<description><![CDATA[At LinkedIn we enjoy a monthly Hackday, and I usually use the opportunity to figure out new ways to combine our platform with other systems &#8211; I love combining different APIs to create new functionality. This month I decided to use my hackday time to create an SMS notification system for our LinkedIn today system. [...]]]></description>
				<content:encoded><![CDATA[<p><a href="http://falling-summer-4605.herokuapp.com/"><img width=550 src="http://www.princesspolymath.com/princess_polymath/wp-content/uploads/2011/12/today_screenshot.png"></a><br />
At LinkedIn we enjoy a monthly <a title="Hackday" href="http://blog.linkedin.com/2011/06/09/10-ways-to-make-hackdays-work/">Hackday</a>, and I usually use the opportunity to figure out new ways to combine our platform with other systems &#8211; I love combining different APIs to create new functionality.</p>
<p>This month I decided to use my hackday time to create an <a href="http://falling-summer-4605.herokuapp.com/">SMS notification system for our LinkedIn today system</a>. Since I&#8217;m not always on the website, I frequently miss the viral articles being discussed until they&#8217;ve passed into my history, and I&#8217;d like to be able to jump in on the conversation while it&#8217;s hot.  Using the LinkedIn Today API (currently only available internally), the system periodically checks each members feed &#8216;s see if there are any articles which are currently generating a huge amount of activity .  Once these &#8220;hot&#8221; articles are found, they are sent via SMS (using Twilio) to the user&#8217;s cell phone and recorded in the DB.  The member can reply to the SMS with &#8220;save&#8221; to add the article to their saved articles at LinkedIn, &#8220;share&#8221; the article with their LinkedIn network, change the notification level or cancel notifications entirely.<br />
<img width=175 src="http://www.princesspolymath.com/princess_polymath/wp-content/uploads/2011/12/IMG_0239.jpg" alt="Welcome message" /> <img width=175 src="http://www.princesspolymath.com/princess_polymath/wp-content/uploads/2011/12/IMG_0238.jpg" alt="Welcome message" /> <img width=175 src="http://www.princesspolymath.com/princess_polymath/wp-content/uploads/2011/12/IMG_0237.jpg" alt="Welcome message" /><br />
In order to build this system, I realized that I had to get several different systems to work together, and working through this process I realized that I had to solve several common problems.  This series of posts will be a tutorial on how to do the following:</p>
<ul>
<li><a href="http://www.princesspolymath.com/princess_polymath/?p=511">Integrate Log in with LinkedIn into Django and deploy to Heroku</a></li>
<li><a href="http://www.princesspolymath.com/princess_polymath/?p=521">Use the Heroku scheduler and python to send SMS messages using Twilio</a></li>
<li><a href="http://www.princesspolymath.com/princess_polymath/?p=531">Handle SMS responses from Twilio in Django (on Heroku)</a></li>
</ul>
<div>These are building blocks that can be used for many similar projects, so I include them here to be an inspiration for future developers trying to get something working.  I enjoyed the resulting demo application enough that I ended up creating a web front-end to the system (so people could configure it) &#8211; you can get to the application at <a href="http://falling-summer-4605.herokuapp.com/">http://falling-summer-4605.herokuapp.com/</a></div>
]]></content:encoded>
			<wfw:commentRss>http://www.princesspolymath.com/princess_polymath/?feed=rss2&#038;p=509</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Handling SMS Responses from Twilio using Django on Heroku</title>
		<link>http://www.princesspolymath.com/princess_polymath/?p=531</link>
		<comments>http://www.princesspolymath.com/princess_polymath/?p=531#comments</comments>
		<pubDate>Fri, 23 Dec 2011 00:16:19 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[API]]></category>
		<category><![CDATA[Geek Stuff]]></category>
		<category><![CDATA[LinkedIn]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[heroku]]></category>
		<category><![CDATA[linkedin]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[twilio]]></category>

		<guid isPermaLink="false">http://www.princesspolymath.com/princess_polymath/?p=531</guid>
		<description><![CDATA[The final piece in the application I&#8217;m working on requires that I process SMS replies from Twilio.  To do this, I need a public-facing server that can handle a POST request from another server so it can do the right thing on my end.  This tutorial will walk through how to do that with a [...]]]></description>
				<content:encoded><![CDATA[<p>The final piece in the application I&#8217;m working on requires that I process SMS replies from Twilio.  To do this, I need a public-facing server that can handle a POST request from another server so it can do the right thing on my end.  This tutorial will walk through how to do that with a Django server on Heroku.  You can grab the twilio_sms application from within my <a href="https://github.com/synedra/django-linkedin-simple">GitHub project </a>for this application, or you can build it based on the walkthrough here.</p>
<h2>URL Setup</h2>
<p>Add a mapping for /reply/ to the urls.py in your top level Django project.</p>
<h4>urls.py</h4>
<pre>url(r'^reply', 'twilio_sms.views.sms_reply'),</pre>
<p>You&#8217;ll need to tell Twilio where to send SMS replies on the <a href="https://www.twilio.com/user/account">dashboard</a>.  For instance, my Heroku instance runs at http://falling-summer-4605.herokuapp.com so my SMS Response URL is set to http://falling-summer-4605.herokuapp.com/reply.</p>
<h2>Creating twilio_sms</h2>
<p>In order for that URL mapping to do the right thing it needs to point to the right application.  If you aren&#8217;t using the code samples from GitHub, you can create a new application with:</p>
<pre>django-admin.py startapp twilio_sms</pre>
<p>Now we need to build the views.py file to handle the responses.</p>
<p>The setup is similar to the linkedin application we made previously.</p>
<pre># Python
import oauth2 as oauth
import simplejson as json
import re
from xml.dom.minidom import getDOMImplementation,parse,parseString

# Django
from django.http import HttpResponse
from django.shortcuts import render_to_response
from django.http import HttpResponseRedirect
from django.conf import settings
from django.views.decorators.csrf import csrf_exempt
from django.contrib.auth.models import User

# Project
from linkedin.models import UserProfile,SentArticle

# from settings.py
consumer = oauth.Consumer(settings.LINKEDIN_TOKEN, settings.LINKEDIN_SECRET)
client = oauth.Client(consumer)</pre>
<p>We&#8217;ll create a convenience method to build SMS responses in the format expected by the Twilio server (so that the user gets a reasonable SMS response)</p>
<pre>def createSmsResponse(responsestring):
	impl = getDOMImplementation()
	responsedoc = impl.createDocument(None,"Response",None)
	top_element = responsedoc.documentElement
	sms_element = responsedoc.createElement("Sms")
	top_element.appendChild(sms_element)
	text_node = responsedoc.createTextNode(responsestring)
	sms_element.appendChild(text_node)
	html = responsedoc.toxml(encoding="utf-8")
	return html</pre>
<p>Because the POST will be coming from an external server without a session cookie, we need to use the @csrf_exempt decorator to tell Django to allow these POSTs without authentication.  For security, you might check the incoming IP to make sure it&#8217;s coming from Twilio, or make sure that the other information matches what you expect.  For this demo, we&#8217;ll allow it to proceed assuming it&#8217;s the right thing.</p>
<p>Grab the parameters, get the user&#8217;s phone number, and determine which of our users matches that phone number, then grab their credentials and create the LinkedIn client to make requests.</p>
<pre>@csrf_exempt
def sms_reply(request):
    if request.method == 'POST':
        params = request.POST
        phone = re.sub('\+1','',params['From'])
        smsuser = User.objects.get(userprofile__phone_number=phone)
        responsetext = "This is my reply text"
        token = oauth.Token(smsuser.get_profile().oauth_token, smsuser.get_profile().oauth_secret)
        client = oauth.Client(consumer,token)</pre>
<p>Figure out what the user wants us to do (save, search, cancel, help, level)</p>
<pre>commandmatch = re.compile(r'(\w+)\b',re.I)
        matches = commandmatch.match(params['Body'])
        command = matches.group(0).lower()</pre>
<p>&#8220;Cancel&#8221; tells the system the user doesn&#8217;t want notifications anymore.  For now, we&#8217;re going to keep their user and profile around, so that we don&#8217;t send them all the same articles again in the future. But sendArticles.py won&#8217;t send them anything if the level is set to zero.</p>
<pre>        # Cancel notifications by setting score to zero
        # Command is 'cancel'
        if command == 'cancel':
        	profile = smsuser.get_profile()
        	profile.min_score = 0
        	profile.save()
        	return HttpResponse(createSmsResponse("Today SMS Service Cancelled"))</pre>
<p>&#8220;Level &lt;number&gt;&#8221; tells the system the user wants to change their notification level.  Higher means fewer messages, as this is used as the &#8220;score&#8221; check against articles based on relevance.  See the <a href="http://www.princesspolymath.com/princess_polymath/?p=521">previous post on Twilio notifications</a> to see how this is implemented.  Notice that in this and the following methods we&#8217;re doing generic error catching &#8211; there&#8217;s a few reasons why it might fail, but the important thing is to tell the user their action didn&#8217;t succeed and give them a hint as to why that might be.</p>
<pre>        # Change level for notifications by setting score to requested level
        # Command is 'level \d'
        if command == 'level':
        	levelmatch = re.compile(r'level (\d)(.*)',re.I)
        	matches = levelmatch.search(params['Body'])

        	try:
	        	level = int(matches.group(1))
	        except:
	        	e = sys.exc_info()[1]
	        	print "ERROR: %s" % (str(e))
	        	return HttpResponse(createSmsResponse("Please use a valid level (1-9)."))

        	profile = smsuser.get_profile()
        	profile.min_score = level
        	profile.save()
        	return HttpResponse(createSmsResponse("Today SMS minimum score changed to %d" % int(level)))</pre>
<p>&#8220;Save &lt;article number&gt;&#8221; saves an article to the user&#8217;s LinkedIn saved articles.  Remember that in the setup we grabbed the credentials for the user who sent the SMS based on their phone number, so this (and share) are done against the LinkedIn API on their behalf.  In this new (preview only) API JSON doesn&#8217;t seem to be working well, so I&#8217;m building and using XML.</p>
<pre>        # Save an article
        # Command is 'save &lt;articlenum&gt;'
        if command == 'save':
        	savematch = re.compile(r'save (\d+)(.*)',re.I)
        	matches = savematch.search(params['Body'])
        	try:
	        	article = matches.group(1)
        		sentarticle = SentArticle.objects.get(user=smsuser, id=article)
	        except:
	        	e = sys.exc_info()[1]
	        	print "ERROR: %s" % (str(e))
	        	return HttpResponse(createSmsResponse("Please use a valid article number with save."))

        	responsetext = "Saved article: %s" % (sentarticle.article_title)
        	saveurl = "http://api.linkedin.com/v1/people/~/articles"

        	# Oddly JSON doesn't seem to work with the article save API
        	# Using XML instead
        	impl = getDOMImplementation()
        	xmlsavedoc = impl.createDocument(None,"article",None)
        	top_element = xmlsavedoc.documentElement
        	article_content_element = xmlsavedoc.createElement("article-content")
        	top_element.appendChild(article_content_element)
        	id_element = xmlsavedoc.createElement("id")
        	article_content_element.appendChild(id_element)
        	text_node = xmlsavedoc.createTextNode(sentarticle.article_number)
        	id_element.appendChild(text_node)
        	body = xmlsavedoc.toxml(encoding="utf-8")

        	resp, content = client.request(saveurl, "POST",body=body,headers={"Content-Type":"text/xml"})
        	if (resp.status == 200):
        		return HttpResponse(createSmsResponse(responsetext))
        	else:
        		return HttpResponse(createSmsResponse("Unable to save post: %s" % content))</pre>
<p>&#8220;Share &lt;article number&gt; &lt;comment&gt;&#8221; shares an article to the user&#8217;s network with a comment.  The comment shouldn&#8217;t really be optional, but typing on T-9 keyboards is a pain, so I wanted to give a default share message.  I&#8217;m not sure I love it as an answer though&#8230;</p>
<pre>        # Share an article
        # Command is 'share &lt;articlenum&gt; &lt;comment&gt;'
        # If no comment is included, a generic one is sent
        if command == 'share':
        	sharematch = re.compile(r'Share (\d+) (.*)')
        	matches = sharematch.search(params['Body'])
        	try:
        		article = matches.group(1)
	        	sentarticle = SentArticle.objects.get(user=smsuser, id=article)
        		comment = matches.group(2)
	        except:
	        	if sentarticle and not comment:
	        		comment = "Sharing an article from the LinkedIn SMS System"
	        	else:
	        		e = sys.exc_info()[1]
	        		print "ERROR: %s" % (str(e))
	        		return HttpResponse(createSmsResponse("Please use a valid article number with share and include a comment."))

        	responsetext = "Shared article: %s" % (sentarticle.article_title)
        	shareurl = "http://api.linkedin.com/v1/people/~/shares"
        	body = {"comment":comment,
        		"content":{
        			"article-id":sentarticle.article_number
       	 		},
        	"visibility":{"code":"anyone"}
        	}

        	resp, content = client.request(shareurl, "POST",body=json.dumps(body),headers={"Content-Type":"application/json"})
        	if (resp.status == 201):
        		return HttpResponse(createSmsResponse(responsetext))
        	else:
        		return HttpResponse(createSmsResponse("Unable to share post: %s" % content))</pre>
<p>If we&#8217;ve fallen through to here, the user may have asked for &#8216;help&#8217; &#8211; but whatever they did we didn&#8217;t understand it so we should give them the help text in any case.</p>
<pre>       # If command is help, or anything we didn't recognize, send help back
        helpstring = "Commands: 'cancel' to cancel Today SMS; 'level #number#' to change minimum score;"
        helpstring += "'save #article#' to save; 'share #article# #comment#' to share"
        return HttpResponse(createSmsResponse(help string))</pre>
<p>&#8230; and, if the request wasn&#8217;t a POST, send a 405 response back to the system (it won&#8217;t be Twilio, it might have been someone else).  This URL is only for processing these SMS messages.</p>
<pre>    # If it's not a post, return an error
    return HttpResponseNotAllowed('POST')</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.princesspolymath.com/princess_polymath/?feed=rss2&#038;p=531</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Sending SMS Notifications using Twilio, Django and the Heroku Scheduler</title>
		<link>http://www.princesspolymath.com/princess_polymath/?p=521</link>
		<comments>http://www.princesspolymath.com/princess_polymath/?p=521#comments</comments>
		<pubDate>Wed, 21 Dec 2011 20:07:28 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[API]]></category>
		<category><![CDATA[Geek Stuff]]></category>
		<category><![CDATA[Heroku]]></category>
		<category><![CDATA[LinkedIn]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[heroku]]></category>
		<category><![CDATA[linkedin]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[twilio]]></category>

		<guid isPermaLink="false">http://www.princesspolymath.com/princess_polymath/?p=521</guid>
		<description><![CDATA[For my Twilio SMS Demo App this month, I wanted to send notifications to LinkedIn members based on hot items for them in LinkedIn Today.  This is a tutorial on creating a python script to access user information in the Django database, pull information from a Web Service (in this case the LinkedIn Today API) [...]]]></description>
				<content:encoded><![CDATA[<p>For my <a href="http://www.princesspolymath.com/princess_polymath/?p=509">Twilio SMS Demo App</a> this month, I wanted to send notifications to LinkedIn members based on hot items for them in LinkedIn Today.  This is a tutorial on creating a python script to access user information in the Django database, pull information from a Web Service (in this case the LinkedIn Today API) and send an SMS to the user.  A couple of caveats are in order here to make sure that people don&#8217;t accidentally run afoul of the <a href="https://developer.linkedin.com/documents/linkedin-apis-terms-use">LinkedIn API Terms of Use</a> when implementing something of this sort.</p>
<ul>
<li>The LinkedIn Today API is only available internally at this point, and should only be used as an example of what&#8217;s possible</li>
<li>I have stored the user&#8217;s phone number in the database &#8211; when storing LinkedIn information you always need to notify the user that you&#8217;re doing that, explicitly.</li>
</ul>
<div>All that having been said, you need the following to get this working:</div>
<div>
<ul>
<li>A working Django system on Heroku.  If you set up my example LinkedIn Django application from <a href="http://www.princesspolymath.com/princess_polymath/?p=511">this post</a>, you&#8217;ll be able to extend that here.</li>
<li>A<a href="http://www.twilio.com/"> Twilio account</a> &#8211; ideally with a purchased number, but you could get this system to work with the sandbox system.  You get $30 in Twilio credit to start out with, which is plenty to set up a demo of this sort.</li>
<li>A Google API key (this is optional, but it&#8217;ll get you more shortened links)</li>
</ul>
</div>
<h2>Install the Twilio Library</h2>
<div>Assuming that you&#8217;re using the LinkedIn Django application from <a href="http://www.princesspolymath.com/princess_polymath/?p=511">my previous post</a> and are in the root level of that structure, you need to add the Twilio library to your installation:</div>
<pre>% pip install twilio
% pip freeze &gt; requirements.txt</pre>
<h2>Adding the Phone Number</h2>
<p>Remember, if you&#8217;re going to store any information at all about a LinkedIn member, you need to explicitly tell them so.  That having been said, you can add the following to your LinkedIn application to get the phone number and store it.  I&#8217;m not going to provide the completed code to do this since you need to implement some user notification if you&#8217;re using this in a production system.</p>
<h3>linkedin/models.py</h3>
<pre>class UserProfile(models.Model):
    user = models.ForeignKey(User)
    oauth_token = models.CharField(max_length=200)
    oauth_secret = models.CharField(max_length=200)
    phone_number = models.CharField(max_length=10)</pre>
<h3>linkedin/views.py</h3>
<p>Modify the LinkedIn profile URL:</p>
<pre>url = "http://api.linkedin.com/v1/people/~:(id,first-name,last-name,industry,phone-numbers)"</pre>
<p>Add this logic before saving the user profile:</p>
<pre>        if "phoneNumbers" in profile:
        	for phone_number in profile['phoneNumbers']['values']:
        		userprofile.phone_number = re.sub(r"\D","",phone_number['phoneNumber'])
        		if phone_number['phoneType'] == 'mobile':
        			continue</pre>
<p>If you&#8217;ve already created your DB you&#8217;ll need to wipe it out and recreate it with these new models in order for this to work.  You can most easily do this by just starting a new project for this version &#8211; while you can drop the database locally and recreate it with syncdb, for heroku you&#8217;ll need to create a new cedar project so a new database will be created.</p>
<h2>Creating the Job</h2>
<p>Now we&#8217;ll walk through the code.  This file wants to live at your LinkedIn project level (so in the example, in the hellodjango directory).  It&#8217;s also available in the GitHub repository for <a href="https://github.com/synedra/django-linkedin-simple">django-linkedin-simple</a>.</p>
<p>First, do some basic setup:</p>
<pre>from django.core.management import setup_environ
import settings
setup_environ(settings)</pre>
<pre>from twilio.rest import TwilioRestClient
import oauth2 as oauth
import time
import simplejson
import datetime
import httplib2
import psycopg2

account = "TWILIO_ACCT_NUMBER"
token = "TWILIO_TOKEN"
twilioclient = TwilioRestClient(account, token)

consumer = oauth.Consumer(
        key="LINKEDIN_CONSUMER_KEY",
        secret="LINKEDIN_CONSUMER_SECRET")
url = "http://api.linkedin.com/v1/people/~/topics:(description,id,topic-stories:(topic-articles:(relevance-data,article-content:(id,title,resolved-url))))"</pre>
<pre><span class="Apple-style-span" style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; font-size: 13px; line-height: 19px; white-space: normal;">Grab the user profiles (this includes the user tokens and secrets)</span></pre>
<pre>users = User.objects.all()</pre>
<p>Iterate over the users, grabbing their token, secret, django userid and phone number, then grab the LinkedIn Today feed for that user.</p>
<pre>for djangouser in users:
	if djangouser.username == "admin":
		continue
	profile = UserProfile.objects.get(user=djangouser)
	token = oauth.Token(
        	key=profile.oauth_token,
        	secret=profile.oauth_secret)
	phone = profile.phone_number
	userid = profile.user_id
	user_articles = SentArticle.objects.filter(user=djangouser)

	# Now make the LinkedIn today call and get the articles in question
	client = oauth.Client(consumer, token)

	resp, content = client.request(url, headers={"x-li-format":'json'})
	results = simplejson.loads(content)</pre>
<p>We only want to send alerts for articles which have a high &#8220;score&#8221;, indicating that they are currently hot topics for this user.  Look through the articles to find those &#8220;hot topics&#8221;:</p>
<pre>	for topic in results['values']:
           for story in topic['topicStories']['values']:
                for article in story['topicArticles']['values']:
                        score = article['relevanceData']['score']
                        if score &gt; 6:</pre>
<p>Now, SMS messages are expensive for us (the application owner) and potentially for the user, so we only want to tell the user if they haven&#8217;t already heard about this article.</p>
<pre>checkarticle = user_articles.filter(article_number__exact=article['articleContent']['id'])</pre>
<p>If there aren&#8217;t any matching articles for this user, go ahead and send them a message, then log it in the database so we don&#8217;t tell them again later:</p>
<pre>if len(checkarticle) == 0:
    # This is where we get the shortened URL from google because LinkedIn doesn't provide one
    http = httplib2.Http()
    body = {"longUrl": article['articleContent']['resolvedUrl']}
    resp,content = http.request("https://www.googleapis.com/urlshortener/v1/url?key=YOUR_GOOGLE_API_KEY","POST",body=simplejson.dumps(body),headers={"Content-Type":"application/json"})
    googleresponse = simplejson.loads(content)
    sentarticle = SentArticle(article_number=article['articleContent']['id'],user=djangouser,timestamp=datetime.datetime.today())
    sentarticle.save()
    bodytext = article['articleContent']['title'] + " " + googleresponse['id']
    bodytext += " ('save %s')" % sentarticle.id
    message = twilioclient.sms.messages.create(to="+1" + phone, from_="+YOUR_TWILIO_NUMBER", body=bodytext)</pre>
<p>Great, we&#8217;ve got an application that&#8217;ll send messages for the users in the Django system when something hot is there to talk about.</p>
<h2>Pushing to Heroku</h2>
<p>Now we need the job to work on Heroku, and get scheduled.  If you went through my previous post using the existing <a href="https://github.com/synedra/django-linkedin-simple">GitHub example</a>, the file is already up at Heroku, otherwise you&#8217;ll need to push the file to your Heroku instance:</p>
<pre>% git add hellodjango/sendArticles.py
% git push heroku master</pre>
<p>Whether it was already there or you just put it there, you should check to make sure that it runs correctly.  Make sure you&#8217;ve logged into the system using your LinkedIn ID and stored your phone number there.</p>
<pre>% heroku run python hellodjango/sendArticles.py</pre>
<p>Did it work?  Great!</p>
<h2>Schedule it in Heroku</h2>
<p>The Heroku Scheduler is a free add-on, but remember that since it uses machine time it can start racking up the charges if you don&#8217;t keep an eye on it.  You can add it using the command line, or you can add it under &#8220;Resources&#8221; for <a href="https://api.heroku.com/myapps">your application</a> on the Heroku site.  Once you&#8217;ve added it you can manage it using the <a href="https://heroku-scheduler.herokuapp.com/dashboard">scheduler dashboard</a>.</p>
<p>Pick whatever frequency you like, and put &#8220;python hellodjango/sendArticles.py&#8221; in the field, and you&#8217;re done.</p>
<p>Don&#8217;t forget to stop the scheduler when you&#8217;re no longer testing so you don&#8217;t get a surprise bill from Heroku later.</p>
<p>The next post will cover handling POST requests from Twilio in response to SMS replies from your users.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.princesspolymath.com/princess_polymath/?feed=rss2&#038;p=521</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Using LinkedIn Authentication with Django on Heroku</title>
		<link>http://www.princesspolymath.com/princess_polymath/?p=511</link>
		<comments>http://www.princesspolymath.com/princess_polymath/?p=511#comments</comments>
		<pubDate>Tue, 20 Dec 2011 18:28:05 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[API]]></category>
		<category><![CDATA[Geek Stuff]]></category>
		<category><![CDATA[Heroku]]></category>
		<category><![CDATA[LinkedIn]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[heroku]]></category>
		<category><![CDATA[linkedin]]></category>
		<category><![CDATA[python]]></category>

		<guid isPermaLink="false">http://www.princesspolymath.com/princess_polymath/?p=511</guid>
		<description><![CDATA[To build the LinkedIn Today SMS Notification System, I needed to find a framework where I could do the following: Integrate LinkedIn OAuth with a more general user management system Post the system to a public site Handle POST requests from external clients For this set of criteria, I chose to use Django and publish [...]]]></description>
				<content:encoded><![CDATA[<p>To build the LinkedIn Today SMS Notification System, I needed to find a framework where I could do the following:</p>
<ul>
<li>Integrate LinkedIn OAuth with a more general user management system</li>
<li>Post the system to a public site</li>
<li>Handle POST requests from external clients</li>
</ul>
<p>For this set of criteria, I chose to use Django and publish it using Heroku.  This post is similar to the Django <a href="http://devcenter.heroku.com/articles/django">Getting Started Guide for Heroku</a>, but it&#8217;s designed for this specific use case &#8211; Using LinkedIn OAuth as the login system and integrating it with the Django user management system.  The code for this example is available <a href="https://github.com/synedra/django-linkedin-simple">here</a>.</p>
<p>Here are the prerequisites for this tutorial:</p>
<ul>
<li>Python, virtualenv, pip, and the Heroku toolbelt as described in <a href="http://devcenter.heroku.com/articles/python">Getting Started with Python on Heroku/Cedar</a>.</li>
<li>An installed version of <a href="http://www.postgresql.org/">Postgres</a> to test locally</li>
<li>A Postgres user and a database to use for Django (in this case I&#8217;m using &#8216;linkedin&#8217;)</li>
<li>A LinkedIn <a href="https://www.linkedin.com/secure/developer">API Key</a>.  If you haven&#8217;t worked with the LinkedIn API before, you&#8217;re encouraged to go through the <a href="https://developer.linkedin.com/documents/quick-start-guide">Quick Start Guide</a>.</li>
</ul>
<h2>Create Your Django Environment</h2>
<div>For the basic setup I&#8217;m following, very closely, the <a href="http://devcenter.heroku.com/articles/django">tutorial for Heroku</a>, so I&#8217;m leaving out some of the explanation &#8211; please refer to the original if you have questions about how things work.  The following steps will create a virtual environment (development sandbox), install needed modules there, and get your Django project and applications created.</div>
<pre lang="term">% mkdir hellodjango &amp;&amp; cd hellodjango
% virtualenv --no-site-packages vent
% source venv/bin/activate
% pip install Django psycopg2 oauth2 simplejson
% django-admin.py startproject hellodjango
% cd hellodjango
% python manage.py startapp linkedin</pre>
<p lang="term">Now that you&#8217;ve done all these pieces, you have a working django system.</p>
<h2 lang="term">File Setup</h2>
<p lang="term">In order to add the LinkedIn integration, you&#8217;ll need to edit the settings and URL files.  I&#8217;ve created a <a href="https://github.com/synedra/django-linkedin-simple">GitHub repository</a> with the code needed for this example, and you can use that as your model &#8211; but I&#8217;ll walk through the changes here so you understand how they work.</p>
<h3 lang="term">hellodjango/settings.py</h3>
<p>Edit the DATABASES section to configure it to use postgres, and your database:</p>
<pre>DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'linkedin',
        'USER': 'postgres',
        'PASSWORD': 'xxx',
        'HOST': '',
        'PORT': '5432',
    }
}</pre>
<div>You also need to add &#8216;linkedin&#8217; to your INSTALLED_APPS:</div>
<div>
<pre>INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'linkedin'
    # Uncomment the next line to enable the admin:
    # 'django.contrib.admin',
    # Uncomment the next line to enable admin documentation:
    # 'django.contrib.admindocs',
)</pre>
<p>And finally, you need to tell the application you want to use LinkedIn for authentication and set up your credentials (this can go anywhere in the settings.py file)</p>
<pre>AUTH_PROFILE_MODULE= 'linkedin.UserProfile'
LINKEDIN_TOKEN='xxx'
LINKEDIN_SECRET='xxx'
LOGIN_URL='/login/'</pre>
<h3>hellodjango/urls.py</h3>
<p>You also need to update the urls.py file to add the LinkedIn authentication pages.</p>
<pre id="LC18">url(r'^login/?$', 'linkedin.views.oauth_login'),
url(r'^logout/?$', 'linkedin.views.oauth_logout'),
url(r'^login/authenticated/?$', 'linkedin.views.oauth_authenticated'),
url(r'^$','linkedin.views.home'),</pre>
<h3 id="LC18">hellodjango/linkedin/models.py</h3>
<p>Using LinkedIn Auth means we&#8217;ll need to store the user&#8217;s token and secret for later requests:</p>
<pre id="LC1">from django.db import models
from django.contrib.auth.models import User
class UserProfile(models.Model):
    user = models.ForeignKey(User)
    oauth_token = models.CharField(max_length=200)
    oauth_secret = models.CharField(max_length=200)</pre>
<h3 id="LC18">hellodjango/linkedin/views.py</h3>
<p>I&#8217;m including the code here, although it&#8217;ll be easier to just get it from GitHub.  This code demonstrates the full OAuth login flow, creating a new Django user using the response, and subsequently making a request using the stored information (home).</p>
<pre id="LC1"># Python
import oauth2 as oauth
import cgi
import simplejson as json
import datetime
import re</pre>
<pre id="LC8"># Django
from django.http import HttpResponse
from django.shortcuts import render_to_response
from django.http import HttpResponseRedirect
from django.conf import settings
from django.contrib.auth import authenticate, login, logout
from django.contrib.auth.models import User
from django.contrib.auth.decorators import login_required</pre>
<pre id="LC17"># Project
from linkedin.models import UserProfile</pre>
<pre id="LC20"># from settings.py
consumer = oauth.Consumer(settings.LINKEDIN_TOKEN, settings.LINKEDIN_SECRET)
client = oauth.Client(consumer)
request_token_url = 'https://api.linkedin.com/uas/oauth/requestToken'
access_token_url = 'https://api.linkedin.com/uas/oauth/accessToken'
authenticate_url = 'https://www.linkedin.com/uas/oauth/authenticate'

# /login
def oauth_login(request):</pre>
<pre id="LC20">    # Step 0. Get the current hostname and port for the callback
    if request.META['SERVER_PORT'] == 443:
     current_server = "https://" + request.META['HTTP_HOST']
    else:
     current_server = "http://" + request.META['HTTP_HOST']
     oauth_callback = current_server + "/login/authenticated"</pre>
<pre id="LC35">    # Step 1. Get a request token from Provider.
    resp, content = client.request("%s?oauth_callback=%s" % (request_token_url,oauth_callback), "GET")
    if resp['status'] != '200':
        raise Exception("Invalid response from Provider.")
    # Step 2. Store the request token in a session for later use.
    request.session['request_token'] = dict(cgi.parse_qsl(content))
    # Step 3. Redirect the user to the authentication URL.
    url = "%s?oauth_token=%s" % (authenticate_url,
        request.session['request_token']['oauth_token'])
    print url
    return HttpResponseRedirect(url)</pre>
<pre># / (requires login)
@login_required
def home(request):</pre>
<pre>    html = "&lt;html&gt;&lt;body&gt;"
    token = oauth.Token(request.user.get_profile().oauth_token,request.user.get_profile().oauth_secret)
    client = oauth.Client(consumer,token)
    headers = {'x-li-format':'json'}
    url = "http://api.linkedin.com/v1/people/~:(id,first-name,last-name,headline)"
    resp, content = client.request(url, "GET", headers=headers)
    profile = json.loads(content)
    html += profile['firstName'] + " " + profile['lastName'] + "&lt;br/&gt;" + profile['headline']
    return HttpResponse(html)</pre>
<pre id="LC49"># /logout (requires login)
@login_required
def oauth_logout(request):</pre>
<pre id="LC56">    # Log a user out using Django's logout function and redirect them
    # back to the homepage.
    logout(request)
    return HttpResponseRedirect('/')</pre>
<pre id="LC63">#/login/authenticated/
def oauth_authenticated(request):
    # Step 1. Use the request token in the session to build a new client.
    token = oauth.Token(request.session['request_token']['oauth_token'],
        request.session['request_token']['oauth_token_secret'])
    if 'oauth_verifier' in request.GET:
        token.set_verifier(request.GET['oauth_verifier'])
    client = oauth.Client(consumer, token)
    # Step 2. Request the authorized access token from Provider.
    resp, content = client.request(access_token_url, "GET")
    if resp['status'] != '200':
        print content
        raise Exception("Invalid response from Provider.")
    access_token = dict(cgi.parse_qsl(content))
    headers = {'x-li-format':'json'}
    url = "http://api.linkedin.com/v1/people/~:(id,first-name,last-name,industry)"
    token = oauth.Token(access_token['oauth_token'],
        access_token['oauth_token_secret'])
    client = oauth.Client(consumer,token)
    resp, content = client.request(url, "GET", headers=headers)
    profile = json.loads(content)<span class="Apple-style-span" style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; font-size: 13px; line-height: 19px; white-space: normal;">    </span></pre>
<pre id="LC86">    # Step 3. Lookup the user or create them if they don't exist.
    firstname = profile['firstName']
    lastname = profile['lastName']
    identifier = profile['id']
    try:
        user = User.objects.get(username=identifier)
    except User.DoesNotExist:
        user = User.objects.create_user(identifier,
            '%s@linkedin.com' % identifier,
            access_token['oauth_token_secret'])<span class="Apple-style-span" style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; font-size: 13px; line-height: 19px; white-space: normal;">         </span>        user.first_name = firstname
        user.last_name = lastname
        user.save()
        # Save our permanent token and secret for later.
        userprofile = UserProfile()
        userprofile.user = user
        userprofile.oauth_token = access_token['oauth_token']
        userprofile.oauth_secret = access_token['oauth_token_secret']
        userprofile.save()
    # Authenticate the user and log them in using Django's pre-built
    # functions for these things.
    user = authenticate(username=identifier,
        password=access_token['oauth_token_secret'])</pre>
<pre id="LC111">    login(request, user)
    return HttpResponseRedirect('/')</pre>
<p id="LC111">For any future requests, you&#8217;ll grab the user&#8217;s token and secret from the profile to make the request just like what&#8217;s shown in the &#8220;home&#8221; example above.  I&#8217;ll go into this in more detail in the following posts, on creating scheduled requests and handling POST requests from external systems.</p>
<h3 lang="term">Procfile</h3>
<p>This is needed at the top level (same level as the hellodjango project directory) to tell Heroku how to run your application.</p>
<pre>web: python hellodjango/manage.py runserver "0.0.0.0:$PORT"</pre>
<h3 lang="term">.gitignore</h3>
<p>Ignore the venv directory and compiled python files</p>
<pre>venv
*.pyc</pre>
<h2>Test Django Server Locally</h2>
<p>Ok, now that you&#8217;ve gotten everything all set up (or grabbed it from github), you&#8217;re ready to run the server and make sure it works correctly.</p>
<pre>% python manage.py syncdb
% python manage.py runserver</pre>
<p>This should bring up a server on localhost which does the basic authentication dance and presents the user&#8217;s name and headline (so exciting!)</p>
<h2>Deploy to Heroku</h2>
<p>Finally, we can get to the Heroku deployment.  Again, this is taken almost verbatim from the <a href="http://devcenter.heroku.com/articles/django">Getting Started With Django</a> document on Heroku, so refer to that for more information on what&#8217;s happening.</p>
<pre>% git init
% git add .
% git commit -m "My LinkedIn Django App"
% heroku create --stack cedar
% git push heroku master
% heroku run python hellodjango/manage.py syncdb 
% heroku open</pre>
<p>This should give you the same experience on Heroku that you have on your local system &#8211; the user is sent to LinkedIn to log in, they&#8217;re added to the system and presented a fascinating page with their basic information.</p>
<p>I&#8217;ve made this example as simple as possible to make it easy to integrate into your Django Application without unnecessary overhead.  Comments, clarifications or other questions welcome.</p>
</div>
]]></content:encoded>
			<wfw:commentRss>http://www.princesspolymath.com/princess_polymath/?feed=rss2&#038;p=511</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Documenting your API with IODocs on Heroku</title>
		<link>http://www.princesspolymath.com/princess_polymath/?p=489</link>
		<comments>http://www.princesspolymath.com/princess_polymath/?p=489#comments</comments>
		<pubDate>Tue, 18 Oct 2011 00:20:21 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[API]]></category>
		<category><![CDATA[LinkedIn]]></category>
		<category><![CDATA[heroku]]></category>
		<category><![CDATA[iodocs]]></category>
		<category><![CDATA[linkedin]]></category>
		<category><![CDATA[node.js]]></category>
		<category><![CDATA[oauth]]></category>

		<guid isPermaLink="false">http://www.princesspolymath.com/princess_polymath/?p=489</guid>
		<description><![CDATA[Neil Mansilla over at Mashery has created an awesome new system called IODocs, which can be used to create a system for browsing through OAuth-secured APIs. Being a Developer Advocate at LinkedIn, I decided to try and get this up and running with LinkedIn&#8217;s API.  Since it was LinkedIn&#8217;s internal hackday on Friday, I decided [...]]]></description>
				<content:encoded><![CDATA[<p>Neil Mansilla over at <a href="http://www.mashery.com">Mashery</a> has created an awesome new system called <a href="https://github.com/mashery/iodocs">IODocs</a>, which can be used to create a system for browsing through OAuth-secured APIs. Being a Developer Advocate at LinkedIn, I decided to try and get this up and running with LinkedIn&#8217;s API.  Since it was LinkedIn&#8217;s internal hackday on Friday, I decided to go the extra mile and try to build the configuration dynamically from our nascent discovery API system, and run it on one of Heroku&#8217;s node.js hosted systems.</p>
<p>Thanks in part to a heavy piece of furniture (which fell on me on Friday, relegating me to bed rest for the weekend) I got it working. You can visit the <a href="http://electric-meadow-1119.herokuapp.com/linkedin">LinkedIn API Documentation</a> site and use your <a href="https://www.linkedin.com/secure/developer">LinkedIn API key and secret</a> to browse through the many API resources LinkedIn has to offer.</p>
<p><a href="http://www.princesspolymath.com/princess_polymath/wp-content/uploads/2011/10/companies.png"><img class="alignnone size-medium wp-image-500" title="companies" src="http://www.princesspolymath.com/princess_polymath/wp-content/uploads/2011/10/companies-300x176.png" alt="" width="300" height="176" /></a></p>
<p>Getting the IODocs system running on Heroku required several steps, but once you have them it&#8217;s pretty straightforward. So I share them with you here so you can get IODocs running on your own Heroku instance, in the hopes that all of the OAuth APIs out there can leverage this functionality and improve developers&#8217; lives.</p>
<p>Consider this an inclusive set of instructions to the ones they have on the site.</p>
<ol>
<li>Get &#8220;Local Workstation Setup&#8221; done from <a href="http://devcenter.heroku.com/articles/node-js">http://devcenter.heroku.com/articles/node-js</a></li>
<li>Do the initial IODocs setup
<pre>% git clone http://github.com/mashery/iodocs.git
% cd iodocs/
% npm install
% echo "web: node app.js" &gt; Procfile
% cp config.json.sample config.json
% vi config.json (remove address line)</pre>
</li>
<li>Add the following block under &#8220;var db;&#8221; to app.js:
<pre>if (process.env.REDISTOGO_URL) {
   var rtg   = require("url").parse(process.env.REDISTOGO_URL);
   db = require("redis").createClient(rtg.port, rtg.hostname);
   db.auth(rtg.auth.split(":")[1]);
} else {
   db = redis.createClient(config.redis.port, config.redis.host);
   db.auth(config.redis.password);
}

And then this in the Load API Configs section, after reading the config file:

var app = module.exports = express.createServer();

var hostname, port, password

if (process.env.REDISTOGO_URL) {
    var rtg   = require("url").parse(process.env.REDISTOGO_URL);
    hostname = rtg.hostname;
    port = rtg.port;
    password = rtg.auth.split(":")[1];
} else {
    hostname = config.redis.host;
    port = config.redis.port;
    password = config.redis.password;
}


</pre>
</li>
<li>Add a LinkedIn block to public/data/apiconfig.json:
<pre>"linkedin": {
   "name": "LinkedIn",
   "protocol": "http",
   "baseURL": "api.linkedin.com",
   "publicPath": "",
   "privatePath": "/v1",
   "auth": "oauth",
   "oauth": {
      "type": "three-legged",
      "requestURL": "https://api.linkedin.com/uas/oauth/requestToken",
      "signinURL": "https://api.linkedin.com/uas/oauth/authorize?oauth_token=",
      "accessURL": "https://api.linkedin.com/uas/oauth/accessToken",
      "version": "1.0",
      "crypt": "HMAC-SHA1"
   },
   "keyParam":""
 },</pre>
</li>
<li>Copy <a href="http://www.princesspolymath.com/linkedin.json">linkedin.json</a> into your public/data folder</li>
<li>Set up your git repository for heroku
<pre>% git init
% git add .
% git add -f config.json Procfile public/data/*
% git commit -m "init"</pre>
</li>
<li>Set up heroku
<pre>% heroku create --stack cedar;
% heroku addons:add redistogo [note: this may require that you add a credit card to your heroku account.  there is no charge, though]
% heroku addons:add logging 
% git push heroku master
% heroku ps:scale web=1</pre>
<pre></pre>
</li>
</ol>
<p>Once you&#8217;ve gotten all this set up, you&#8217;re ready to rock.  Heroku will give you the right system name and port, and you can use &#8216;heroku logs&#8217; to check the logs for the node.js server.</p>
<p>You can set up your own APIs using the config file &#8211; the IODocs documentation is fantastic, with OCD-level detail on how to set up configurations for new APIs, and I&#8217;m certain you can make it work with whatever you&#8217;re trying to use.</p>
<p>&nbsp;</p>
]]></content:encoded>
			<wfw:commentRss>http://www.princesspolymath.com/princess_polymath/?feed=rss2&#038;p=489</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Hiring Generalists</title>
		<link>http://www.princesspolymath.com/princess_polymath/?p=487</link>
		<comments>http://www.princesspolymath.com/princess_polymath/?p=487#comments</comments>
		<pubDate>Tue, 19 Jul 2011 17:12:15 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Geek Stuff]]></category>
		<category><![CDATA[LinkedIn]]></category>

		<guid isPermaLink="false">http://www.princesspolymath.com/princess_polymath/?p=487</guid>
		<description><![CDATA[One of the things that a lot of companies in the Silicon Valley (and, by extension, the rest of the country) struggle with is the hiring process.  I work at a company where we have a lot of phenomenally talented people working here, and an almost infinite supply of talented people who want to work [...]]]></description>
				<content:encoded><![CDATA[<p>One of the things that a lot of companies in the Silicon Valley (and, by extension, the rest of the country) struggle with is the hiring process.  I work at a company where we have a lot of phenomenally talented people working here, and an almost infinite supply of talented people who want to work here.  As with a lot of other companies, we&#8217;ve been streamlining our process somewhat and have created a process where people from different groups interview candidates for particular skills, with the theory that the person might fit well somewhere else in the company, and we don&#8217;t want to miss that chance. We&#8217;ve got various specific skills we&#8217;re supposed to interview on, and it&#8217;s kind of a cookie cutter experience.  You have these skills or you don&#8217;t, someone likes you or they don&#8217;t.</p>
<p>There&#8217;s a problem with this kind of hiring, though.  It&#8217;s a great idea if you&#8217;re trying to hire someone who can already do exactly the tasks that you&#8217;re hiring them for.  However, an interview process like that is likely to eliminate people who can do a phenomenal job at that task even though they&#8217;ve never done it before.  I frequently joke that &#8220;I don&#8217;t know anything, but I can *do* anything.&#8221;  Which isn&#8217;t really true.  I know a lot of things.  But they don&#8217;t extend to CS algorithm puzzles because I don&#8217;t have a CompSci degree.  I can, in fact, study up and pass the Google Interview Questions&#8230; but I won&#8217;t.  Because I am a kick ass engineer, my code is solid, my problem solving skills are phenomenal, and more importantly, if the company needs me to do some magical new thing I wasn&#8217;t hired for, there&#8217;s a good chance I&#8217;ll be happy to do it, and do it exceedingly well.  I don&#8217;t want someone to hire me because I&#8217;m willing to pretend to be something I&#8217;m not.  I want them to hire me for the spectacularly cool things I bring to the company.  If they don&#8217;t value those more than my ability to reverse a linked list, then they don&#8217;t really get why they should hire me.</p>
<p>Sure, specialists are great.  You know what you&#8217;re getting, you can put them in front of a defined, bounded task and they&#8217;ll do it in the expected way.  But in today&#8217;s environment where companies need to stretch, innovate and grow &#8211; at a company where the monthly hackday consistently puts out products the company can leverage &#8211; companies need to understand the value of someone who can figure out how to do new tasks, not just hire people who can do the tasks already identified. Every company has people who they hired to do particular things because they can do them.  The spectacular companies also have a large percentage of individuals who thrive on taking on new challenges and exploring new frontiers.</p>
<p>On the other hand, as a generalist, I frequently have helpful feedback in these interviews where we&#8217;re looking for skills I don&#8217;t currently possess.  But my feedback is usually about the candidate as a thinker, as a doer, as a leader, as a participant in the conversation.  And less about the SQL statements.</p>
<p>Posted in Geek Stuff, LinkedIn on the blog.  Friends and Open Source on G+.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.princesspolymath.com/princess_polymath/?feed=rss2&#038;p=487</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>
