<?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>Proudly ProcrasDonating</title>
	<atom:link href="http://proudly.procrasdonate.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://proudly.procrasdonate.com</link>
	<description>Technology Thoughts</description>
	<lastBuildDate>Wed, 03 Mar 2010 23:07:16 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.4</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>ProcrasDoCoder Conversation Starter</title>
		<link>http://proudly.procrasdonate.com/procrasdocoder-conversation-starter/</link>
		<comments>http://proudly.procrasdonate.com/procrasdocoder-conversation-starter/#comments</comments>
		<pubDate>Mon, 15 Feb 2010 03:21:21 +0000</pubDate>
		<dc:creator>Lucy</dc:creator>
				<category><![CDATA[conceptual]]></category>
		<category><![CDATA[technical]]></category>

		<guid isPermaLink="false">http://proudly.procrasdonate.com/?p=766</guid>
		<description><![CDATA[
The ProcrasDoCoder Ring is

a conversation starter for my startup, ProcrasDonate
gives the team a warm fuzzy feeling while we&#8217;re working
a fun introduction to microcontrollers

The ring, or possibly a pin next to one&#8217;s name tag or attached to one&#8217;s hat, consists of a bright BlinkM RGB LED with long wires to an arduino unit hidden in one&#8217;s [...]]]></description>
			<content:encoded><![CDATA[<p><img style="text-align: center;" src="http://github.com/diN0bot/ProcrasDoCoder/raw/master/docs/circuit2.JPG" width="50%" /></p>
<p>The <a href="http://github.com/diN0bot/ProcrasDoCoder">ProcrasDoCoder Ring</a> is</p>
<ul>
<li>a conversation starter for my startup, ProcrasDonate</li>
<li>gives the team a warm fuzzy feeling while we&#8217;re working</li>
<li>a fun introduction to microcontrollers</li>
</ul>
<p>The ring, or possibly a pin next to one&#8217;s name tag or attached to one&#8217;s hat, consists of a bright BlinkM RGB LED with long wires to an arduino unit hidden in one&#8217;s pocket or belt loop. The LED lights up colorfully and brilliantly for a few seconds whenever a user of ProcrasDonate donates to a charity, makes a pledge, downloads our software or participates in some user test.</p>
<p>Currently, the code to do all this has been written, but the hardware housing is still in prototype phase. I&#8217;ll update the following videos and pictures soon; hopefully with a real use case in the wild.</p>
<p>This video shows a simple demo of the WiShield and BlinkM.</p>
<p><object width="333" height="222"><param name="movie" value="http://www.youtube.com/v/yvCtAjzHpDE&#038;hl=en_US&#038;fs=1&#038;"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param>
<embed src="http://www.youtube.com/v/yvCtAjzHpDE&#038;hl=en_US&#038;fs=1&#038;" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="333"  height="222"></embed></object></p>
<h3>How it works</h3>
<p>The ProcrasDoCoderRing is a simple mashup that stands on the shoulders of 4 giants:</p>
<p>Arduino + WiShield + BlinkM + ProcrasDonate web server</p>
<p><img style="text-align: center;"  src="http://github.com/diN0bot/ProcrasDoCoder/raw/master/docs/layers.JPG" width="50%" /></p>
<p>In case it&#8217;s not clear: much of the WiShield and BlinkM code is copied from relevant examples.</p>
<ul>
<li><strong>Arduino</strong> &#8211; Arduino starter pack from AdaFruit, snap.</li>
<li><strong>WiShield</strong> &#8211; from AsyncLabs
<p>Nice library. Needed to hack a few config lines in the C code for WEP 5 byte passcode (see section below), but otherwise comes with good documentation.</li>
<li><strong>BlinkM</strong> &#8211; RGB LED kit from ThingM
<p>Comes with a nice arduino library that abstracts fading the LED to a specific RGB or HSB.</li>
<li><strong>ProcrasDonate web server </strong>- python Django website</li>
</ul>
<p>There are two different ways this could work:</p>
<ol>
<li>The arduino registers itself with the ProcrasDonate site, which then pushes information to the arduino using HTTP web requests to the WiShield webpage server.</li>
<li>Instead, the arduino regularly pings a small Django app running on ProcrasDonate. The app has a single webpage, which returns the donation counts, etc.</li>
</ol>
<p>To view the code and more detailed documentation, see the <a href="http://github.com/diN0bot/ProcrasDoCoder#readme">ProcrasDoCoder github repository</a>.</p>
<p>Proudly ProcrasDonating,</p>
<p>Lucy.</p>
]]></content:encoded>
			<wfw:commentRss>http://proudly.procrasdonate.com/procrasdocoder-conversation-starter/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>RescueTime integration test: donations as time management incentive</title>
		<link>http://proudly.procrasdonate.com/rescuetime-integration-test-donations-as-time-management-incentive/</link>
		<comments>http://proudly.procrasdonate.com/rescuetime-integration-test-donations-as-time-management-incentive/#comments</comments>
		<pubDate>Fri, 12 Feb 2010 02:40:55 +0000</pubDate>
		<dc:creator>Lucy</dc:creator>
				<category><![CDATA[release]]></category>
		<category><![CDATA[testing]]></category>

		<guid isPermaLink="false">http://proudly.procrasdonate.com/?p=746</guid>
		<description><![CDATA[Check out the test app &#8212;> give feedback
Today I broke from solidifying and bug-fixing the ProcrasDonate Firefox extension in order to test donations as a time management incentive.
I created a small app that uses RescueTime&#8217;s API to retrieve a user&#8217;s RescueTime time tracking data. 
The user authorizes ProcrasDonate to donate to their selected charity at [...]]]></description>
			<content:encoded><![CDATA[<blockquote><p><a href="http://procrasdonate.com/rt/signup">Check out the test app</a> &#8212;> <a href="http://feedback.procrasdonate.com">give feedback</a></p></blockquote>
<p>Today I broke from solidifying and bug-fixing the ProcrasDonate <a href="http://procrasdonate.com">Firefox extension</a> in order to test donations as a time management incentive.</p>
<p>I created a small app that uses <a href="https://www.rescuetime.com/anapi/setup">RescueTime&#8217;s API</a> to retrieve a user&#8217;s RescueTime time tracking data. </p>
<p>The user authorizes ProcrasDonate to donate to their selected charity at the indicated $/hr rate for all hours spent on activities with a negative RescueTime productivity score.</p>
<p>Keep in mind: this is a barebones minimal test app. <a href="http://procrasdonate.com/rt/signup">Try it out</a> and <a href="http://feedback.procrasdonate.com">tell us what you think</a>.</p>
<p>Proudly ProcrasDonating,</p>
<p>Lucy.</p>
]]></content:encoded>
			<wfw:commentRss>http://proudly.procrasdonate.com/rescuetime-integration-test-donations-as-time-management-incentive/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Django Tricks, Part 7 &#8211; Django from shell</title>
		<link>http://proudly.procrasdonate.com/django-tricks-part-7-django-from-shell/</link>
		<comments>http://proudly.procrasdonate.com/django-tricks-part-7-django-from-shell/#comments</comments>
		<pubDate>Mon, 08 Feb 2010 01:15:43 +0000</pubDate>
		<dc:creator>Lucy</dc:creator>
				<category><![CDATA[technical]]></category>

		<guid isPermaLink="false">http://proudly.procrasdonate.com/?p=687</guid>
		<description><![CDATA[This post is the seventh and last in a series on Django tricks that began with automating Admin Model creation.
This is one of my favorite tricks: setup the Django environment from the Python interpreter.


&#40;master&#41; $ python
&#62;&#62;&#62; from lib.django_from_shell import start_django as sd
&#62;&#62;&#62; sd&#40;&#41;
&#62;&#62;&#62; from procrasdonate.models import User
&#62;&#62;&#62; User.objects.count&#40;&#41;
22


All we need to do is wrap a [...]]]></description>
			<content:encoded><![CDATA[<p>This post is the seventh and last in a series on Django tricks that began with <a href="http://proudly.procrasdonate.com/django-tricks-1-adminizer/">automating Admin Model creation</a>.</p>
<p>This is one of my favorite tricks: setup the Django environment from the Python interpreter.</p>
<pre>
<div class="codesnip-container" >
<div class="python codesnip" style="font-family:monospace;"><span class="br0">&#40;</span>master<span class="br0">&#41;</span> $ python
<span class="sy0">&gt;&gt;&gt;</span> <span class="kw1">from</span> lib.<span class="me1">django_from_shell</span> <span class="kw1">import</span> start_django <span class="kw1">as</span> sd
<span class="sy0">&gt;&gt;&gt;</span> sd<span class="br0">&#40;</span><span class="br0">&#41;</span>
<span class="sy0">&gt;&gt;&gt;</span> <span class="kw1">from</span> procrasdonate.<span class="me1">models</span> <span class="kw1">import</span> User
<span class="sy0">&gt;&gt;&gt;</span> User.<span class="me1">objects</span>.<span class="me1">count</span><span class="br0">&#40;</span><span class="br0">&#41;</span>
<span class="nu0">22</span></div>
</div>
</pre>
<p>All we need to do is wrap a few Django calls like so:</p>
<pre>
<div class="codesnip-container" >
<div class="python codesnip" style="font-family:monospace;"><span class="kw1">def</span> start_django<span class="br0">&#40;</span><span class="br0">&#41;</span>:
&nbsp; &nbsp; <span class="kw1">import</span> <span class="kw3">sys</span>
&nbsp; &nbsp; <span class="co1">#Now the nearest ancestor directory with a 'settings.py' file</span>
&nbsp; &nbsp; settings_dir = find_file_in_ancestors<span class="br0">&#40;</span><span class="st0">&quot;settings.py&quot;</span><span class="br0">&#41;</span>
&nbsp; &nbsp; <span class="kw3">sys</span>.<span class="me1">path</span>.<span class="me1">append</span><span class="br0">&#40;</span>settings_dir<span class="br0">&#41;</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw1">from</span> django.<span class="me1">core</span>.<span class="me1">management</span> <span class="kw1">import</span> setup_environ
&nbsp; &nbsp; <span class="kw1">import</span> settings
&nbsp; &nbsp; setup_environ<span class="br0">&#40;</span>settings<span class="br0">&#41;</span></div>
</div>
</pre>
<p>By being flexible about finding the global settings.py file, we can call start_django from other scripts buried in bin or cron directories:</p>
<pre>
<div class="codesnip-container" >
<div class="python codesnip" style="font-family:monospace;"><span class="kw1">def</span> find_file_in_ancestors<span class="br0">&#40;</span>filename<span class="br0">&#41;</span>:
&nbsp; &nbsp; <span class="st0">&quot;&quot;&quot;
&nbsp; &nbsp; For each parent directory, check if 'filename' exists. &nbsp;If found, return
&nbsp; &nbsp; the path; otherwise raise RuntimeError.
&nbsp; &nbsp; &quot;&quot;&quot;</span>
&nbsp; &nbsp; <span class="kw1">import</span> <span class="kw3">os</span>
&nbsp; &nbsp; path = <span class="kw3">os</span>.<span class="me1">path</span>.<span class="me1">realpath</span><span class="br0">&#40;</span><span class="kw3">os</span>.<span class="me1">path</span>.<span class="me1">curdir</span><span class="br0">&#41;</span>
&nbsp; &nbsp; <span class="kw1">while</span> <span class="kw1">not</span> filename <span class="kw1">in</span> <span class="kw3">os</span>.<span class="me1">listdir</span><span class="br0">&#40;</span>path<span class="br0">&#41;</span>:
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">#if filename in os.listdir(path):</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1"># &nbsp; &nbsp;return path</span>
&nbsp; &nbsp; &nbsp; &nbsp; newpath = <span class="kw3">os</span>.<span class="me1">path</span>.<span class="me1">split</span><span class="br0">&#40;</span>path<span class="br0">&#41;</span><span class="br0">&#91;</span>0<span class="br0">&#93;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> path == newpath:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">raise</span> <span class="kw2">RuntimeError</span><span class="br0">&#40;</span><span class="st0">&quot;No file '%s' found in ancestor directories.&quot;</span> <span class="sy0">%</span> filename<span class="br0">&#41;</span>
&nbsp; &nbsp; &nbsp; &nbsp; path = newpath
&nbsp; &nbsp; <span class="kw1">return</span> path</div>
</div>
</pre>
<h2>Conclusion</h2>
<p>The goal of these tricks is to reduce copy/past/alter drudgery. It&#8217;s rewarding to automate the creation of fluff code, even easy fluff that knocked my socks off when I first learned Django. </p>
<p>Of course, it&#8217;s hard to stay impressed with myself or Dan for long since I quickly forget that the fluff code even existed. I suppose that&#8217;s part of the point. I can forget about creating Admin Models, and new models still show up in the Admin app as I thoughtlessly expected.</p>
<p>Still, even if I&#8217;m not impressed, I do notice how often I get to write new, interesting code. That&#8217;s why I&#8217;m Proudly ProcrasDonating,</p>
<p>Lucy.</p>
]]></content:encoded>
			<wfw:commentRss>http://proudly.procrasdonate.com/django-tricks-part-7-django-from-shell/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Django Tricks, Part 6 &#8211; View Utilities</title>
		<link>http://proudly.procrasdonate.com/django-tricks-part-6-view-utilities/</link>
		<comments>http://proudly.procrasdonate.com/django-tricks-part-6-view-utilities/#comments</comments>
		<pubDate>Mon, 08 Feb 2010 01:11:18 +0000</pubDate>
		<dc:creator>Lucy</dc:creator>
				<category><![CDATA[technical]]></category>

		<guid isPermaLink="false">http://proudly.procrasdonate.com/?p=688</guid>
		<description><![CDATA[This post is the sixth in a series on Django tricks.
We started with automating Admin Model creation, and continued with automating initialization and installation, model mixins, forms and app settings.
This post diverges from automation to point out some useful view utilities.
Render Response
This trick is more like standard Django practice: wrap render_to_response with your own function [...]]]></description>
			<content:encoded><![CDATA[<p>This post is the sixth in a series on Django tricks.</p>
<p>We started with <a href="http://proudly.procrasdonate.com/django-tricks-1-adminizer/">automating Admin Model creation</a>, and continued with automating <a href="http://proudly.procrasdonate.com/django-tricks-2-initialize-and-install/">initialization and installation</a>, <a href="http://proudly.procrasdonate.com/django-tricks-3-model-mixins/">model mixins</a>, <a href="http://proudly.procrasdonate.com/django-tricks-4-forms/">forms</a> and <a href="http://proudly.procrasdonate.com/django-tricks-5-automatic-app-settings/">app settings</a>.</p>
<p>This post diverges from automation to point out some useful view utilities.</p>
<h2>Render Response</h2>
<p>This trick is more like standard Django practice: wrap render_to_response with your own function that uses your own RequestContext.</p>
<pre>
<div class="codesnip-container" >
<div class="python codesnip" style="font-family:monospace;"><span class="kw1">def</span> render_response<span class="br0">&#40;</span>request, template, dictionary=<span class="kw2">None</span><span class="br0">&#41;</span>:
&nbsp; &nbsp; <span class="st0">&quot;&quot;&quot;
&nbsp; &nbsp; Return render_to_response with context_instance=RequestContext(request).
&nbsp; &nbsp; &quot;&quot;&quot;</span>
&nbsp; &nbsp; dictionary = dictionary <span class="kw1">or</span> <span class="br0">&#123;</span><span class="br0">&#125;</span>
&nbsp; &nbsp; <span class="kw1">return</span> render_to_response<span class="br0">&#40;</span>template, dictionary, context_instance=RequestContext<span class="br0">&#40;</span>request<span class="br0">&#41;</span><span class="br0">&#41;</span>

<span class="kw1">def</span> render_string<span class="br0">&#40;</span>request, template, dictionary=<span class="kw2">None</span><span class="br0">&#41;</span>:
&nbsp; &nbsp; dictionary = dictionary <span class="kw1">or</span> <span class="br0">&#123;</span><span class="br0">&#125;</span>
&nbsp; &nbsp; <span class="kw1">return</span> render_to_string<span class="br0">&#40;</span>template, dictionary, context_instance=RequestContext<span class="br0">&#40;</span>request<span class="br0">&#41;</span><span class="br0">&#41;</span></div>
</div>
</pre>
<p>One small thing sometimes forgotten is that when you call render_response from your view, you don&#8217;t have to write out the dictionary, which is really just a copy/double-paste of local variable names. Instead, call locals().</p>
<h3>some_view.py</h3>
<pre>
<div class="codesnip-container" >
<div class="python codesnip" style="font-family:monospace;"><span class="kw1">def</span> info<span class="br0">&#40;</span>request<span class="br0">&#41;</span>:
&nbsp; &nbsp; user_count = User.<span class="me1">objects</span>.<span class="me1">count</span><span class="br0">&#40;</span><span class="br0">&#41;</span>
&nbsp; &nbsp; visitor_count = Visitor.<span class="me1">objects</span>.<span class="me1">count</span><span class="br0">&#40;</span><span class="br0">&#41;</span>
&nbsp; &nbsp; pledge_count = ...
&nbsp; &nbsp; <span class="me1">donation_count</span> = RecipientPayment.<span class="me1">objects</span>.<span class="me1">count</span><span class="br0">&#40;</span><span class="br0">&#41;</span>

&nbsp; &nbsp; <span class="kw1">return</span> render_response<span class="br0">&#40;</span>request, <span class="st0">'procrasdocoder/info.html'</span>, <span class="kw2">locals</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span></div>
</div>
</pre>
<h2>Extract Parameters</h2>
<p>Another useful convenience function is <em>extract_parameters</em>, which extracts expected and optional parameters from a request&#8217;s POST and GET objects, as well as reporting appropriate errors.</p>
<p>Here&#8217;s an example use:</p>
<pre>
<div class="codesnip-container" >
<div class="python codesnip" style="font-family:monospace;"><span class="kw1">def</span> receive_data<span class="br0">&#40;</span>request<span class="br0">&#41;</span>:
&nbsp; &nbsp; expected_parameters = <span class="br0">&#91;</span><span class="st0">&quot;private_key&quot;</span>,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="st0">&quot;prefs&quot;</span><span class="br0">&#93;</span>
&nbsp; &nbsp; optional_parameters = <span class="br0">&#91;</span><span class="st0">&quot;foo&quot;</span>,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="st0">&quot;bar&quot;</span><span class="br0">&#93;</span>
&nbsp; &nbsp; response = extract_parameters<span class="br0">&#40;</span>request, <span class="st0">&quot;POST&quot;</span>, expected_parameters<span class="br0">&#41;</span>
&nbsp; &nbsp; <span class="kw1">if</span> <span class="kw1">not</span> response<span class="br0">&#91;</span><span class="st0">'success'</span><span class="br0">&#93;</span>:
&nbsp; &nbsp; &nbsp; &nbsp; Log.<span class="me1">Error</span><span class="br0">&#40;</span>response<span class="br0">&#91;</span><span class="st0">'reason'</span><span class="br0">&#93;</span>, <span class="st0">&quot;communication_error&quot;</span><span class="br0">&#41;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> json_failure<span class="br0">&#40;</span>message<span class="br0">&#41;</span>

&nbsp; &nbsp; parameters = response<span class="br0">&#91;</span><span class="st0">'parameters'</span><span class="br0">&#93;</span>
&nbsp; &nbsp; private_key = parameters<span class="br0">&#91;</span><span class="st0">'private_key'</span><span class="br0">&#93;</span>
&nbsp; &nbsp; ...</div>
</div>
</pre>
<p>Here is the definition for extract_parameters:</p>
<pre>
<div class="codesnip-container" >
<div class="python codesnip" style="font-family:monospace;"><span class="kw1">def</span> extract_parameters<span class="br0">&#40;</span>request, method_type, expected_parameters, optional_parameters=<span class="kw2">None</span><span class="br0">&#41;</span>:
&nbsp; &nbsp; ret = <span class="br0">&#123;</span><span class="br0">&#125;</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw1">if</span> <span class="kw1">not</span> <span class="kw2">getattr</span><span class="br0">&#40;</span>request, method_type<span class="br0">&#41;</span>:
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> <span class="br0">&#123;</span><span class="st0">'success'</span>: <span class="kw2">False</span>,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">'reason'</span>: <span class="st0">&quot;Expected %s parameters&quot;</span> <span class="sy0">%</span> method_type<span class="br0">&#125;</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw1">for</span> p <span class="kw1">in</span> expected_parameters:
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">try</span>:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; v = <span class="kw2">getattr</span><span class="br0">&#40;</span>request, method_type<span class="br0">&#41;</span>.<span class="me1">get</span><span class="br0">&#40;</span>p, <span class="kw2">None</span><span class="br0">&#41;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> v == <span class="kw2">None</span>:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> <span class="br0">&#123;</span><span class="st0">'success'</span>: <span class="kw2">False</span>,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">'reason'</span>: <span class="st0">&quot;Missing expected parameter: %s&quot;</span> <span class="sy0">%</span> p<span class="br0">&#125;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ret<span class="br0">&#91;</span>p<span class="br0">&#93;</span> = v
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">except</span>:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> <span class="br0">&#123;</span><span class="st0">'success'</span>: <span class="kw2">False</span>,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">'reason'</span>: <span class="st0">&quot;Something unexpected happened while extracting parameters&quot;</span><span class="br0">&#125;</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; optional_parameters = optional_parameters <span class="kw1">or</span> <span class="br0">&#123;</span><span class="br0">&#125;</span>
&nbsp; &nbsp; <span class="kw1">for</span> p <span class="kw1">in</span> optional_parameters:
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">try</span>:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; v = <span class="kw2">getattr</span><span class="br0">&#40;</span>request, method_type<span class="br0">&#41;</span>.<span class="me1">get</span><span class="br0">&#40;</span>p, <span class="kw2">None</span><span class="br0">&#41;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> v:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ret<span class="br0">&#91;</span>p<span class="br0">&#93;</span> = v
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">except</span>:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> <span class="br0">&#123;</span><span class="st0">'success'</span>: <span class="kw2">False</span>,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">'reason'</span>: <span class="st0">&quot;Something unexpected happened while extracting optional parameters&quot;</span><span class="br0">&#125;</span>
&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw1">return</span> <span class="br0">&#123;</span><span class="st0">'success'</span>: <span class="kw2">True</span>,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">'parameters'</span>: ret<span class="br0">&#125;</span></div>
</div>
</pre>
<p>This is a marathon super-bowl blogging experience for me, and the English to Code ratio is quickly approaching zero. I&#8217;ve left the best for last, though, so check it out: <a href="http://proudly.procrasdonate.com/django-tricks-part-7-django-from-shell">Part 7 &#8211; Django from shell</a>.</p>
<p>Proudly ProcrasDonating,</p>
<p>Lucy.</p>
]]></content:encoded>
			<wfw:commentRss>http://proudly.procrasdonate.com/django-tricks-part-6-view-utilities/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Django Tricks, Part 5 &#8211; Automatic App Settings</title>
		<link>http://proudly.procrasdonate.com/django-tricks-part-5-automatic-app-settings/</link>
		<comments>http://proudly.procrasdonate.com/django-tricks-part-5-automatic-app-settings/#comments</comments>
		<pubDate>Mon, 08 Feb 2010 01:01:51 +0000</pubDate>
		<dc:creator>Lucy</dc:creator>
				<category><![CDATA[technical]]></category>

		<guid isPermaLink="false">http://proudly.procrasdonate.com/?p=682</guid>
		<description><![CDATA[This post is the fifth in a series on Django tricks.
We started with automating Admin Model creation, and continued with automating initialization and installation, model mixins and forms.
This post describes how we automate incorporating new Django apps into our project. 
Creating apps is a wonderful way to modularize work. Encapsulation, abstraction, re-use!
To reduce copy/paste/rewrite drudger [...]]]></description>
			<content:encoded><![CDATA[<p>This post is the fifth in a series on Django tricks.</p>
<p>We started with <a href="http://proudly.procrasdonate.com/django-tricks-1-adminizer/">automating Admin Model creation</a>, and continued with automating <a href="http://proudly.procrasdonate.com/django-tricks-2-initialize-and-install/">initialization and installation</a>, <a href="http://proudly.procrasdonate.com/django-tricks-3-model-mixins/">model mixins</a> and <a href="http://proudly.procrasdonate.com/django-tricks-4-forms/">forms</a>.</p>
<p>This post describes how we automate incorporating new Django apps into our project. </p>
<p>Creating apps is a wonderful way to modularize work. Encapsulation, abstraction, re-use!</p>
<p>To reduce copy/paste/rewrite drudger we&#8217;ve automated our project&#8217;s <em>settings.py</em> file according to standard Django conventions.</p>
<h3>APPS list</h3>
<p>First, we need to list what apps are active in the project:</p>
<pre>
<div class="codesnip-container" >
<div class="python codesnip" style="font-family:monospace;">APPS = <span class="br0">&#40;</span><span class="st0">'procrasdonate'</span>, <span class="st0">'crosstester'</span>, <span class="st0">'adwords'</span>, <span class="st0">'procrasdocoder'</span><span class="br0">&#41;</span></div>
</div>
</pre>
<h3>Pathify utility</h3>
<p>Let&#8217;s setup some convenience path functions. </p>
<p><em>path</em> converts a path with unix path separator, /, into a path with the appropriate os-specific path separator. </p>
<p><em>pathify</em> converts a list of path components into a string path with appropriate path separator. </p>
<p>Finally, <em>PROJECT_PATH</em> holds the projects root absolute path.</p>
<pre>
<div class="codesnip-container" >
<div class="python codesnip" style="font-family:monospace;"><span class="kw1">import</span> <span class="kw3">os</span>
<span class="kw1">def</span> path<span class="br0">&#40;</span>p<span class="br0">&#41;</span>:
&nbsp; &nbsp; <span class="st0">&quot;&quot;&quot;
&nbsp; &nbsp; @param p: path with unix path separator
&nbsp; &nbsp; @return: string with os-specific path separator
&nbsp; &nbsp; &quot;&quot;&quot;</span>
&nbsp; &nbsp; <span class="kw1">return</span> <span class="kw3">os</span>.<span class="me1">sep</span>.<span class="me1">join</span><span class="br0">&#40;</span>p.<span class="me1">split</span><span class="br0">&#40;</span><span class="st0">&quot;/&quot;</span><span class="br0">&#41;</span><span class="br0">&#41;</span>

<span class="kw1">import</span> <span class="kw3">re</span>
_space_replace = <span class="kw3">re</span>.<span class="kw2">compile</span><span class="br0">&#40;</span><span class="st0">&quot;([^<span class="es0">\\</span><span class="es0">\]</span>)( )&quot;</span><span class="br0">&#41;</span>
<span class="kw1">def</span> pathify<span class="br0">&#40;</span>lst<span class="br0">&#41;</span>:
&nbsp; &nbsp; <span class="st0">&quot;&quot;&quot;
&nbsp; &nbsp; @param lst: list of path components
&nbsp; &nbsp; @return: string with os-specific path separator between components 
&nbsp; &nbsp; &quot;&quot;&quot;</span>
&nbsp; &nbsp; <span class="co1"># replaces spaces with raw spaces so that spaces in file names work</span>
&nbsp; &nbsp; repl = r<span class="st0">&quot;<span class="es0">\g</span>&lt;1&gt; &quot;</span>
&nbsp; &nbsp; <span class="kw1">return</span> <span class="kw3">os</span>.<span class="me1">sep</span>.<span class="me1">join</span><span class="br0">&#40;</span><span class="br0">&#91;</span>_space_replace.<span class="me1">sub</span><span class="br0">&#40;</span>repl, el<span class="br0">&#41;</span> <span class="kw1">for</span> el <span class="kw1">in</span> lst<span class="br0">&#93;</span><span class="br0">&#41;</span>

PROJECT_PATH = <span class="kw3">os</span>.<span class="me1">path</span>.<span class="me1">dirname</span><span class="br0">&#40;</span><span class="kw3">os</span>.<span class="me1">path</span>.<span class="me1">realpath</span><span class="br0">&#40;</span>__file__<span class="br0">&#41;</span><span class="br0">&#41;</span></div>
</div>
</pre>
<h3>Middleware</h3>
<p>Now we&#8217;re into the core of the work, which is to iterate through each app and append the appropriate path or module to the relevant tuple.</p>
<pre>
<div class="codesnip-container" >
<div class="python codesnip" style="font-family:monospace;">MIDDLEWARE_CLASSES = <span class="br0">&#40;</span>
&nbsp; &nbsp; <span class="st0">'django.middleware.doc.XViewMiddleware'</span>,
&nbsp; &nbsp; <span class="st0">'django.middleware.common.CommonMiddleware'</span>,
&nbsp; &nbsp; <span class="st0">'django.contrib.sessions.middleware.SessionMiddleware'</span>,
&nbsp; &nbsp; <span class="st0">'django.contrib.auth.middleware.AuthenticationMiddleware'</span>,
&nbsp; &nbsp; <span class="st0">'ext.pagination.middleware.PaginationMiddleware'</span>,
&nbsp; &nbsp; <span class="st0">'django.contrib.csrf.middleware.CsrfMiddleware'</span>,
&nbsp; &nbsp; <span class="st0">'lib.ssl_middleware.SSLRedirect'</span>,
<span class="br0">&#41;</span>
<span class="kw1">for</span> app <span class="kw1">in</span> APPS:
&nbsp; &nbsp; <span class="kw1">if</span> <span class="kw3">os</span>.<span class="me1">path</span>.<span class="me1">exists</span><span class="br0">&#40;</span>pathify<span class="br0">&#40;</span><span class="br0">&#91;</span>PROJECT_PATH, app, <span class="st0">'middleware.py'</span><span class="br0">&#93;</span>, file_extension=<span class="kw2">True</span><span class="br0">&#41;</span><span class="br0">&#41;</span>:
&nbsp; &nbsp; &nbsp; &nbsp; MIDDLEWARE_CLASSES += <span class="br0">&#40;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">'%s.middleware.%sMiddleware'</span> <span class="sy0">%</span> <span class="br0">&#40;</span>app, app.<span class="me1">capitalize</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span>,
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#41;</span></div>
</div>
</pre>
<h3>Templates</h3>
<p>Add template directories. </p>
<p>Note: Do you see how we also add the template directory of our Firefox extension? This is so that our server can <a href="http://proudly.procrasdonate.com/template-sharing/">reuse extension templates</a> (OMGSQUEEL).</p>
<pre>
<div class="codesnip-container" >
<div class="python codesnip" style="font-family:monospace;">TEMPLATE_DIRS = <span class="br0">&#40;</span>
&nbsp; &nbsp; <span class="co1">#'procrasdonate/ProcrasDonateFFExtn/content/templates',</span>
&nbsp; &nbsp; pathify<span class="br0">&#40;</span><span class="br0">&#91;</span>PROJECT_PATH, path<span class="br0">&#40;</span><span class="st0">'procrasdonate/ProcrasDonateFFExtn/content/templates'</span><span class="br0">&#41;</span><span class="br0">&#93;</span><span class="br0">&#41;</span>,
<span class="br0">&#41;</span>

<span class="kw1">for</span> app <span class="kw1">in</span> APPS:
&nbsp; &nbsp; <span class="kw1">if</span> <span class="kw3">os</span>.<span class="me1">path</span>.<span class="me1">exists</span><span class="br0">&#40;</span>pathify<span class="br0">&#40;</span><span class="br0">&#91;</span>PROJECT_PATH, app, <span class="st0">'templates'</span><span class="br0">&#93;</span><span class="br0">&#41;</span><span class="br0">&#41;</span>:
&nbsp; &nbsp; &nbsp; &nbsp; TEMPLATE_DIRS += <span class="br0">&#40;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; pathify<span class="br0">&#40;</span><span class="br0">&#91;</span>PROJECT_PATH, path<span class="br0">&#40;</span><span class="st0">'%s/templates'</span> <span class="sy0">%</span> app<span class="br0">&#41;</span><span class="br0">&#93;</span><span class="br0">&#41;</span>,
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#41;</span></div>
</div>
</pre>
<h3>Installed Apps</h3>
<pre>
<div class="codesnip-container" >
<div class="python codesnip" style="font-family:monospace;">INSTALLED_APPS = <span class="br0">&#40;</span>
&nbsp; &nbsp; <span class="st0">'django.contrib.contenttypes'</span>,
&nbsp; &nbsp; <span class="st0">'django.contrib.sessions'</span>,
&nbsp; &nbsp; <span class="st0">'django.contrib.sites'</span>,
&nbsp; &nbsp; <span class="st0">'django.contrib.humanize'</span>,
&nbsp; &nbsp; <span class="st0">'ext.pagination'</span>,
&nbsp; &nbsp; <span class="st0">'django.contrib.auth'</span>,
&nbsp; &nbsp; <span class="st0">'django.contrib.admindocs'</span>,
&nbsp; &nbsp; <span class="st0">'django.contrib.admin'</span>,
<span class="br0">&#41;</span>

<span class="kw1">for</span> app <span class="kw1">in</span> APPS:
&nbsp; &nbsp; INSTALLED_APPS += <span class="br0">&#40;</span>
&nbsp; &nbsp; &nbsp; &nbsp; app,
&nbsp; &nbsp; <span class="br0">&#41;</span></div>
</div>
</pre>
<h3>Context Processors</h3>
<pre>
<div class="codesnip-container" >
<div class="python codesnip" style="font-family:monospace;">TEMPLATE_CONTEXT_PROCESSORS = <span class="br0">&#40;</span>
&nbsp; &nbsp; <span class="st0">'lib.context.defaults'</span>,
&nbsp; &nbsp; <span class="st0">'django.core.context_processors.auth'</span>,
&nbsp; &nbsp; <span class="st0">'django.core.context_processors.debug'</span>,
&nbsp; &nbsp; <span class="st0">'django.core.context_processors.i18n'</span>,
&nbsp; &nbsp; <span class="st0">'django.core.context_processors.media'</span>,
&nbsp; &nbsp; <span class="st0">'django.core.context_processors.request'</span>,
<span class="br0">&#41;</span>

<span class="kw1">for</span> app <span class="kw1">in</span> APPS:
&nbsp; &nbsp; <span class="kw1">if</span> <span class="kw3">os</span>.<span class="me1">path</span>.<span class="me1">exists</span><span class="br0">&#40;</span>pathify<span class="br0">&#40;</span><span class="br0">&#91;</span>PROJECT_PATH, app, <span class="st0">'context.py'</span><span class="br0">&#93;</span>, file_extension=<span class="kw2">True</span><span class="br0">&#41;</span><span class="br0">&#41;</span>:
&nbsp; &nbsp; &nbsp; &nbsp; TEMPLATE_CONTEXT_PROCESSORS += <span class="br0">&#40;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">'%s.context.defaults'</span> <span class="sy0">%</span> app,
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#41;</span></div>
</div>
</pre>
<p>This only works if the app includes a &#8220;context.py&#8221; file with a function called &#8220;defaults&#8221; that returns the context dictionary:</p>
<h3>context.py</h3>
<pre>
<div class="codesnip-container" >
<div class="python codesnip" style="font-family:monospace;"><span class="kw1">def</span> defaults<span class="br0">&#40;</span>request<span class="br0">&#41;</span>:
&nbsp; &nbsp; frame = <span class="br0">&#123;</span><span class="br0">&#125;</span>
&nbsp; &nbsp; frame.<span class="me1">update</span><span class="br0">&#40;</span>useful_settings<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span>
&nbsp; &nbsp; frame.<span class="me1">update</span><span class="br0">&#40;</span>header_data<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span>
&nbsp; &nbsp; ...
&nbsp; &nbsp; <span class="kw1">return</span> frame

<span class="kw1">def</span> useful_settings<span class="br0">&#40;</span><span class="br0">&#41;</span>:
&nbsp; &nbsp; <span class="kw1">return</span> <span class="br0">&#123;</span> <span class="st0">'PDVERSION'</span>: settings.<span class="me1">PDVERSION</span> <span class="br0">&#125;</span>

<span class="kw1">def</span> header_data<span class="br0">&#40;</span><span class="br0">&#41;</span>:
&nbsp; &nbsp; <span class="kw1">return</span> <span class="br0">&#123;</span> ... <span class="br0">&#125;</span></div>
</div>
</pre>
<h3>Urls</h3>
<p>The project&#8217;s base URL file is modified to automatically incorporate each app&#8217;s URLs. </p>
<p>For example, an app called &#8220;magic_cape&#8221; would be accessible from the URL &#8220;/magic_cape/&#8221;, assuming it contained a &#8220;urls.py&#8221; file inside a &#8220;views&#8221; directory.</p>
<p>To customize the urls for an app, it&#8217;s name is added to the CUSTOM_URLS_APPS tuple.</p>
<pre>
<div class="codesnip-container" >
<div class="python codesnip" style="font-family:monospace;">CUSTOM_URLS_APPS = <span class="br0">&#40;</span><span class="st0">'procrasdonate'</span>,<span class="br0">&#41;</span>

<span class="kw1">for</span> app <span class="kw1">in</span> settings.<span class="me1">APPS</span>:
&nbsp; &nbsp; <span class="kw1">if</span> app <span class="kw1">not</span> <span class="kw1">in</span> CUSTOM_URLS_APPS <span class="kw1">and</span> <span class="kw3">os</span>.<span class="me1">path</span>.<span class="me1">exists</span><span class="br0">&#40;</span>pathify<span class="br0">&#40;</span><span class="br0">&#91;</span>settings.<span class="me1">PROJECT_PATH</span>, app, <span class="st0">'views'</span><span class="br0">&#93;</span><span class="br0">&#41;</span><span class="br0">&#41;</span>:
&nbsp; &nbsp; &nbsp; &nbsp; urlpatterns += patterns<span class="br0">&#40;</span><span class="st0">''</span>,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#40;</span>r<span class="st0">'^%s/'</span> <span class="sy0">%</span> app, include<span class="br0">&#40;</span><span class="st0">'%s.views.urls'</span> <span class="sy0">%</span> app<span class="br0">&#41;</span><span class="br0">&#41;</span>,
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#41;</span></div>
</div>
</pre>
<p>Proudly ProcrasDonating,</p>
<p>Lucy.</p>
]]></content:encoded>
			<wfw:commentRss>http://proudly.procrasdonate.com/django-tricks-part-5-automatic-app-settings/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Django Tricks, Part 4 &#8211; Forms</title>
		<link>http://proudly.procrasdonate.com/django-tricks-4-forms/</link>
		<comments>http://proudly.procrasdonate.com/django-tricks-4-forms/#comments</comments>
		<pubDate>Mon, 08 Feb 2010 00:52:34 +0000</pubDate>
		<dc:creator>Lucy</dc:creator>
				<category><![CDATA[technical]]></category>

		<guid isPermaLink="false">http://proudly.procrasdonate.com/?p=681</guid>
		<description><![CDATA[This post is the fourth in a series on Django tricks.
We started with automating Admin Model creation. This post describes how we automate the creation of form classes for models.
The point of automatic forms is to reduce the effort that goes into writing views. Form classes for models should be created on the fly as [...]]]></description>
			<content:encoded><![CDATA[<p>This post is the fourth in a series on Django tricks.</p>
<p>We started with <a href="http://proudly.procrasdonate.com/django-tricks-1-adminizer/">automating Admin Model creation</a>. This post describes how we automate the creation of <em>form classes</em> for models.</p>
<div id="attachment_664" class="wp-caption aligncenter" style="width: 355px"><img src="http://proudly.procrasdonate.com/wp-content/uploads/forms.png" alt="Automatic Model Form creation" title="Automatic Model Form creation" width="345"  class="size-full wp-image-664" /><p class="wp-caption-text">Automatic Model Form creation</p></div>
<p>The point of automatic forms is to reduce the effort that goes into writing views. Form classes for models should be created on the fly as we need them, with the same flexibility as if we had defined the form class directly.</p>
<p>Here&#8217;s an example that retrieves a form for the public fields of the Recipient model:</p>
<pre>
<div class="codesnip-container" >
<div class="python codesnip" style="font-family:monospace;"><span class="kw1">from</span> lib.<span class="me1">forms</span> <span class="kw1">import</span> get_form, EDIT_TYPE

@login_required
<span class="kw1">def</span> edit_public_information<span class="br0">&#40;</span>request<span class="br0">&#41;</span>:
&nbsp; &nbsp; recipient = request.<span class="kw3">user</span>.<span class="me1">get_profile</span><span class="br0">&#40;</span><span class="br0">&#41;</span>.<span class="me1">recipient</span>
&nbsp; &nbsp; FormKlass = get_form<span class="br0">&#40;</span>Recipient, EDIT_TYPE, includes=<span class="br0">&#40;</span><span class="st0">'name'</span>,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="st0">'category'</span>,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="st0">'mission'</span>,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="st0">'description'</span>,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="st0">'url'</span><span class="br0">&#41;</span><span class="br0">&#41;</span>

&nbsp; &nbsp; <span class="kw1">if</span> request.<span class="me1">POST</span>:
&nbsp; &nbsp; &nbsp; &nbsp; form = FormKlass<span class="br0">&#40;</span>request.<span class="me1">POST</span>, instance=recipient<span class="br0">&#41;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> form.<span class="me1">is_valid</span><span class="br0">&#40;</span><span class="br0">&#41;</span>:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; form.<span class="me1">save</span><span class="br0">&#40;</span><span class="br0">&#41;</span></div>
</div>
</pre>
<p>The <em>get_form</em> utility function takes the Model class as a required parameter. If that is the only parameter, it will return the default ModelForm for that model.</p>
<p>The <strong>includes</strong> and <strong>excludes</strong> fields normally specified in the ModelForm&#8217;s <a href="http://docs.djangoproject.com/en/dev/topics/forms/modelforms/#using-a-subset-of-fields-on-the-form">inner Meta class</a> can be provided as parameters to get_form().</p>
<p>The forms module also specifies three ModelForm types, NEW_TYPE, EDIT_TYPE and ADMIN_TYPE. Originally, the includes and excludes were defined per type per model in the forms module. This is useful if there are multiple places in the web app where models are edited, and you don&#8217;t want to keep remembering which immutable or signal-computed fields to exclude.</p>
<p>Note: If we go back to using the NEW_, EDIT_ and ADMIN_TYPEs we would move their definitions to inside models themselves rather than editing a single dictionary in the forms module. Live, learn and move on.</p>
<p>The forms module is appended below.</p>
<p>Proudly ProcrasDonating,</p>
<p>Lucy.</p>
<pre>
<div class="codesnip-container" >
<div class="python codesnip" style="font-family:monospace;"><span class="kw1">from</span> django.<span class="me1">forms</span> <span class="kw1">import</span> ModelForm

<span class="st0">&quot;&quot;&quot;
We want to create three kinds of forms:
1. Admin&lt;Model&gt;Form &nbsp;admin forms. &nbsp; &nbsp; these have all fields and are used for both creation and editing
2. New&lt;Model&gt;Form &nbsp; &nbsp;user new forms. &nbsp;these exclude hidden or contextually obvious fields (eg state, a review's node)
3. Edit&lt;Model&gt;Form &nbsp; user edit forms. these exclude hidden fields
&quot;&quot;&quot;</span>
<span class="co1"># shows all fields</span>
ADMIN_TYPE = <span class="st0">'admin'</span>
<span class="co1"># user facing form for creating new instance</span>
NEW_TYPE = <span class="st0">'new'</span>
<span class="co1"># user facing form for editing existing instance</span>
EDIT_TYPE = <span class="st0">'edit'</span>

<span class="co1"># users should not reference this cache directly.</span>
<span class="co1"># instead, use get_form</span>
FORMS = <span class="br0">&#123;</span> ADMIN_TYPE: <span class="br0">&#123;</span><span class="br0">&#125;</span>,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; NEW_TYPE: <span class="br0">&#123;</span><span class="br0">&#125;</span>,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; EDIT_TYPE: <span class="br0">&#123;</span><span class="br0">&#125;</span>,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span>

<span class="kw1">def</span> get_form<span class="br0">&#40;</span>model, <span class="kw2">type</span>=ADMIN_TYPE, excludes=<span class="kw2">None</span>, includes=<span class="kw2">None</span><span class="br0">&#41;</span>:
&nbsp; &nbsp; <span class="st0">&quot;&quot;&quot;
&nbsp; &nbsp; @param excludes: in addition to type-based excludes. for on-the-fly forms. 
&nbsp; &nbsp; @param includes: for on-the-fly forms. 
&nbsp; &nbsp; return form class for model and type
&nbsp; &nbsp; &quot;&quot;&quot;</span>
&nbsp; &nbsp; <span class="kw1">if</span> model <span class="kw1">in</span> FORMS<span class="br0">&#91;</span><span class="kw2">type</span><span class="br0">&#93;</span> <span class="kw1">and</span> <span class="kw1">not</span> excludes <span class="kw1">and</span> <span class="kw1">not</span> includes:
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1"># don't cache on-the-fly forms</span>
&nbsp; &nbsp; &nbsp; &nbsp; form = FORMS<span class="br0">&#91;</span><span class="kw2">type</span><span class="br0">&#93;</span><span class="br0">&#91;</span>model<span class="br0">&#93;</span>
&nbsp; &nbsp; <span class="kw1">else</span>:
&nbsp; &nbsp; &nbsp; &nbsp; mname = model.__name__
&nbsp; &nbsp; &nbsp; &nbsp; excludes = excludes <span class="kw1">or</span> <span class="br0">&#40;</span><span class="br0">&#41;</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="kw2">type</span> == ADMIN_TYPE:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">exec</span><span class="br0">&#40;</span><span class="st0">'Admin%sForm = model_form_class(model, excludes, includes)'</span> <span class="sy0">%</span> mname <span class="br0">&#41;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; form = <span class="kw2">locals</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#91;</span><span class="st0">'%sForm'</span> <span class="sy0">%</span> mname<span class="br0">&#93;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">elif</span> <span class="kw2">type</span> == NEW_TYPE:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> mname <span class="kw1">in</span> new_forms_excludes:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; excludes += new_forms_excludes<span class="br0">&#91;</span>mname<span class="br0">&#93;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">exec</span><span class="br0">&#40;</span><span class="st0">'New%sForm = model_form_class(model, excludes, includes)'</span> <span class="sy0">%</span> mname <span class="br0">&#41;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; form = <span class="kw2">locals</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#91;</span><span class="st0">'New%sForm'</span> <span class="sy0">%</span> mname<span class="br0">&#93;</span>

&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">elif</span> <span class="kw2">type</span> == EDIT_TYPE:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> mname <span class="kw1">in</span> edit_forms_excludes:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; excludes += edit_forms_excludes<span class="br0">&#91;</span>mname<span class="br0">&#93;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">exec</span><span class="br0">&#40;</span><span class="st0">'Edit%sForm = model_form_class(model, excludes, includes)'</span> <span class="sy0">%</span> mname <span class="br0">&#41;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; form = <span class="kw2">locals</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#91;</span><span class="st0">'Edit%sForm'</span> <span class="sy0">%</span> mname<span class="br0">&#93;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">else</span>:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">raise</span> <span class="st0">&quot;Type no good&quot;</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="kw1">not</span> excludes <span class="kw1">and</span> <span class="kw1">not</span> includes:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1"># don't cache on-the-fly forms</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; FORMS<span class="br0">&#91;</span><span class="kw2">type</span><span class="br0">&#93;</span><span class="br0">&#91;</span>model<span class="br0">&#93;</span> = form
&nbsp; &nbsp; 
&nbsp; &nbsp; <span class="kw1">return</span> form


<span class="kw1">def</span> model_form_class<span class="br0">&#40;</span>_model, excludes=<span class="kw2">None</span>, includes=<span class="kw2">None</span><span class="br0">&#41;</span>:
&nbsp; &nbsp; <span class="kw1">class</span> klass<span class="br0">&#40;</span>ModelForm<span class="br0">&#41;</span>:
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">class</span> Meta:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; model = _model
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> excludes:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; exclude = excludes
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> includes:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fields = includes
&nbsp; &nbsp; <span class="kw1">return</span> klass

new_forms_excludes = <span class="br0">&#123;</span><span class="co1">#'OldModel':('url',),</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">#'OldModel2':('old_model','foo','bar'),</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span>

edit_forms_excludes = <span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">#'MyModel':('slug','immutable','calculated_from_post_save'),</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span></div>
</div>
</pre>
]]></content:encoded>
			<wfw:commentRss>http://proudly.procrasdonate.com/django-tricks-4-forms/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Django Tricks, Part 3 &#8211; Model mixins</title>
		<link>http://proudly.procrasdonate.com/django-tricks-3-model-mixins/</link>
		<comments>http://proudly.procrasdonate.com/django-tricks-3-model-mixins/#comments</comments>
		<pubDate>Sun, 07 Feb 2010 23:52:03 +0000</pubDate>
		<dc:creator>Lucy</dc:creator>
				<category><![CDATA[technical]]></category>

		<guid isPermaLink="false">http://proudly.procrasdonate.com/?p=679</guid>
		<description><![CDATA[This post is the third in a series on Django tricks.
In the last post we covered automating initialization. Continuing with initialization, this post will cover model mixins.
Model mixins mix convenient utilities into model classes.
Specified Mixins
Mixins is a general term for mixing attributes of an abstract class into a non-abstract subclass. 
In this case, we want [...]]]></description>
			<content:encoded><![CDATA[<p>This post is the third in a series on Django tricks.</p>
<p>In the last post we covered <a href="http://proudly.procrasdonate.com/django-tricks-2-initialize-and-install/">automating initialization</a>. Continuing with initialization, this post will cover <em>model mixins</em>.</p>
<p><em>Model mixins</em> mix convenient utilities into model classes.</p>
<h2>Specified Mixins</h2>
<p>Mixins is a general term for mixing attributes of an abstract class into a non-abstract subclass. </p>
<p>In this case, we want to mix convenience methods into Model Foo. The reason why these methods aren&#8217;t defined in Foo is because of clean, modular code. An example will help:</p>
<pre>
<div class="codesnip-container" >
<div class="python codesnip" style="font-family:monospace;"><span class="kw1">class</span> Tagging<span class="br0">&#40;</span>models.<span class="me1">Model</span><span class="br0">&#41;</span>:
&nbsp; &nbsp; tag = models.<span class="me1">ForeignKey</span><span class="br0">&#40;</span>Tag<span class="br0">&#41;</span>
&nbsp; &nbsp; post = models.<span class="me1">ForeignKey</span><span class="br0">&#40;</span>Post<span class="br0">&#41;</span>

&nbsp; &nbsp; @<span class="kw2">classmethod</span>
&nbsp; &nbsp; <span class="kw1">def</span> Initialize<span class="br0">&#40;</span>klass<span class="br0">&#41;</span>:
&nbsp; &nbsp; &nbsp; &nbsp; model_utils.<span class="me1">mixin</span><span class="br0">&#40;</span>TaggingMixin, Post<span class="br0">&#41;</span></div>
</div>
</pre>
<p>There are three models at play in the above example:</p>
<ul>
<li>Post &#8211; a blog post</li>
<li>Tag &#8211; a tag</li>
<li>Tagging &#8211; links a tag with a post</li>
</ul>
<p>There is also a normal python class, TaggingMixin, that provides convenience methods for Posts:</p>
<pre>
<div class="codesnip-container" >
<div class="python codesnip" style="font-family:monospace;"><span class="kw1">class</span> TaggingMixin<span class="br0">&#40;</span><span class="kw2">object</span><span class="br0">&#41;</span>:
&nbsp; &nbsp; <span class="st0">&quot;&quot;&quot; mixed into Post &quot;&quot;&quot;</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; @<span class="kw2">property</span>
&nbsp; &nbsp; <span class="kw1">def</span> tags<span class="br0">&#40;</span><span class="kw2">self</span><span class="br0">&#41;</span>:
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> Tag.<span class="me1">objects</span>.<span class="kw2">filter</span><span class="br0">&#40;</span>tagging_set__post=<span class="kw2">self</span><span class="br0">&#41;</span></div>
</div>
</pre>
<p>In the Tagging model&#8217;s Initialization function, mixin mixes TaggingMixin into Post. This allows us to encapsulate all the Tag logic, including the ForeignKeys to Post, inside the Tagging models and the TagMixin class. The Post model remains clean, with only those functions that deal with its direct fields.</p>
<p>This separation becomes a necessity when Models are spread between different apps.</p>
<p>The mixin code is appended to the end of this post.</p>
<h2>Standard Mixins</h2>
<p>We automatically mixin in the following convenience functions to every Model:</p>
<ul>
<li>add</li>
<li>find</li>
<li>get</li>
<li>get_or_none</li>
</ul>
<p><em>get_or_none</em> is a nice way to retrieve an object if it exists, such as:</p>
<pre>
<div class="codesnip-container" >
<div class="python codesnip" style="font-family:monospace;">lucy = User.<span class="me1">get_or_none</span><span class="br0">&#40;</span>name=<span class="st0">&quot;Lucy&quot;</span><span class="br0">&#41;</span>
<span class="kw1">if</span> lucy:
&nbsp; &nbsp; <span class="kw1">print</span> lucy.<span class="me1">favorite_number</span>
<span class="kw1">else</span>:
&nbsp; &nbsp; <span class="kw1">print</span> <span class="st0">&quot;Lucy is not in the database&quot;</span></div>
</div>
</pre>
<p><em>add</em> is a convenient constructor for creating objects, though it requires adding a Make classmethod to every Model. The advantage is that we not only put constructor logic inside a constructor, but we also abstract that call into add&#8230;in case making things becomes more complicated later.</p>
<h3>some view function</h3>
<pre>
<div class="codesnip-container" >
<div class="python codesnip" style="font-family:monospace;">lucy = User.<span class="me1">add</span><span class="br0">&#40;</span><span class="st0">&quot;Lucy&quot;</span>, <span class="nu0">22</span><span class="br0">&#41;</span></div>
</div>
</pre>
<h3>models.py</h3>
<pre>
<div class="codesnip-container" >
<div class="python codesnip" style="font-family:monospace;"><span class="kw1">class</span> User<span class="br0">&#40;</span>models.<span class="me1">Model</span><span class="br0">&#41;</span>:
&nbsp; &nbsp; name = models.<span class="me1">CharField</span><span class="br0">&#40;</span>max_length=222<span class="br0">&#41;</span>
&nbsp; &nbsp; favorite_number = models.<span class="me1">IntegerField</span><span class="br0">&#40;</span><span class="br0">&#41;</span>

&nbsp; &nbsp; @<span class="kw2">classmethod</span>
&nbsp; &nbsp; <span class="kw1">def</span> make<span class="br0">&#40;</span>klass, name, favnum<span class="br0">&#41;</span>:
&nbsp; &nbsp; &nbsp; &nbsp; favnum = <span class="kw2">int</span><span class="br0">&#40;</span><span class="kw2">round</span><span class="br0">&#40;</span>favnum<span class="br0">&#41;</span><span class="br0">&#41;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> User<span class="br0">&#40;</span>name=name, favorite_number=favnum<span class="br0">&#41;</span></div>
</div>
</pre>
<p>In order to mix these methods into every Model we resort to the __init__.py trick discussed previously:</p>
<pre>
<div class="codesnip-container" >
<div class="python codesnip" style="font-family:monospace;"><span class="kw1">from</span> models <span class="kw1">import</span> ALL_MODELS

<span class="kw1">from</span> lib <span class="kw1">import</span> model_utils
model_utils.<span class="me1">mixin</span><span class="br0">&#40;</span>model_utils.<span class="me1">ModelMixin</span>, ALL_MODELS<span class="br0">&#41;</span></div>
</div>
</pre>
<p>Proudly ProcrasDonating,</p>
<p>Lucy</p>
<h3>mixin definition</h3>
<pre>
<div class="codesnip-container" >
<div class="python codesnip" style="font-family:monospace;"><span class="kw1">def</span> mixin<span class="br0">&#40;</span>mixin, klasses, last=0<span class="br0">&#41;</span>:
&nbsp; &nbsp; <span class="kw1">if</span> <span class="kw1">not</span> <span class="kw2">isinstance</span><span class="br0">&#40;</span>klasses, <span class="br0">&#40;</span><span class="kw2">list</span>, <span class="kw2">tuple</span><span class="br0">&#41;</span><span class="br0">&#41;</span>:
&nbsp; &nbsp; &nbsp; &nbsp; klasses = <span class="br0">&#40;</span>klasses,<span class="br0">&#41;</span>
&nbsp; &nbsp; <span class="kw1">for</span> klass <span class="kw1">in</span> klasses:
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> mixin <span class="kw1">not</span> <span class="kw1">in</span> klass.__bases__:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> last:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; klass.__bases__ = klass.__bases__+<span class="br0">&#40;</span>mixin,<span class="br0">&#41;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">else</span>:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1"># sometimes this fails, but if change order seems to work</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">try</span>:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; klass.__bases__ = <span class="br0">&#40;</span>mixin,<span class="br0">&#41;</span>+klass.__bases__
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">except</span>:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; klass.__bases__ = klass.__bases__+<span class="br0">&#40;</span>mixin,<span class="br0">&#41;</span></div>
</div>
</pre>
<h3>ModelMixin definition</h3>
<pre>
<div class="codesnip-container" >
<div class="python codesnip" style="font-family:monospace;"><span class="kw1">class</span> ModelMixin<span class="br0">&#40;</span><span class="kw2">object</span><span class="br0">&#41;</span>:
&nbsp; &nbsp; <span class="st0">&quot;&quot;&quot;
&nbsp; &nbsp; @summary:
&nbsp; &nbsp; B{ModelMixin} is a mix-in used to provide common methods, attributes,
&nbsp; &nbsp; and hooks across all models.
&nbsp; &nbsp; 
&nbsp; &nbsp; At initialization, ModelMixin is mixed into all models listed in 
&nbsp; &nbsp; &quot;&quot;&quot;</span>
&nbsp; &nbsp; @<span class="kw2">classmethod</span>
&nbsp; &nbsp; <span class="kw1">def</span> find<span class="br0">&#40;</span>klass, ids=<span class="kw2">None</span>, <span class="sy0">**</span>kwargs<span class="br0">&#41;</span>:
&nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">&quot;&quot;&quot;
&nbsp; &nbsp; &nbsp; &nbsp; @summary:
&nbsp; &nbsp; &nbsp; &nbsp; Convenience method: B{find} simply passes arguments through to
&nbsp; &nbsp; &nbsp; &nbsp; klass.objects.filter()
&nbsp; &nbsp; &nbsp; &nbsp; &quot;&quot;&quot;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="kw2">isinstance</span><span class="br0">&#40;</span>ids, <span class="br0">&#40;</span><span class="kw2">int</span>, <span class="kw2">long</span><span class="br0">&#41;</span><span class="br0">&#41;</span>:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> klass.<span class="me1">objects</span>.<span class="kw2">filter</span><span class="br0">&#40;</span><span class="kw2">id</span>=ids, <span class="sy0">**</span>kwargs<span class="br0">&#41;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">elif</span> <span class="kw2">isinstance</span><span class="br0">&#40;</span>ids, <span class="kw2">list</span><span class="br0">&#41;</span>:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> klass.<span class="me1">objects</span>.<span class="kw2">filter</span><span class="br0">&#40;</span>id__in=ids, <span class="sy0">**</span>kwargs<span class="br0">&#41;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">else</span>:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> klass.<span class="me1">objects</span>.<span class="kw2">filter</span><span class="br0">&#40;</span><span class="sy0">**</span>kwargs<span class="br0">&#41;</span>

&nbsp; &nbsp; @<span class="kw2">classmethod</span>
&nbsp; &nbsp; <span class="kw1">def</span> get_or_none<span class="br0">&#40;</span>klass, <span class="sy0">**</span>kwargs<span class="br0">&#41;</span>:
&nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">&quot;&quot;&quot;
&nbsp; &nbsp; &nbsp; &nbsp; @summary:
&nbsp; &nbsp; &nbsp; &nbsp; Convenience method: B{get_or_none} simply passes arguments through to
&nbsp; &nbsp; &nbsp; &nbsp; klass.objects.filter()
&nbsp; &nbsp; &nbsp; &nbsp; &quot;&quot;&quot;</span>
&nbsp; &nbsp; &nbsp; &nbsp; f = klass.<span class="me1">objects</span>.<span class="kw2">filter</span><span class="br0">&#40;</span><span class="sy0">**</span>kwargs<span class="br0">&#41;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="kw2">len</span><span class="br0">&#40;</span>f<span class="br0">&#41;</span> == 0:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> <span class="kw2">None</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">else</span>:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> f<span class="br0">&#91;</span>0<span class="br0">&#93;</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; @<span class="kw2">classmethod</span>
&nbsp; &nbsp; <span class="kw1">def</span> get<span class="br0">&#40;</span>klass, <span class="kw2">id</span>=<span class="kw2">None</span>, <span class="sy0">**</span>kwargs<span class="br0">&#41;</span>:
&nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">&quot;&quot;&quot;
&nbsp; &nbsp; &nbsp; &nbsp; Convenience method: B{get} simply passes arguments through to 
&nbsp; &nbsp; &nbsp; &nbsp; klass.objects.get()
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; @note: Raises exceptions anytime 'get' would.
&nbsp; &nbsp; &nbsp; &nbsp; &quot;&quot;&quot;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="kw2">isinstance</span><span class="br0">&#40;</span><span class="kw2">id</span>, <span class="br0">&#40;</span><span class="kw2">int</span>, <span class="kw2">long</span><span class="br0">&#41;</span><span class="br0">&#41;</span>:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">#return klass.objects.get_object_or_404(id=id, **kwargs)</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> get_object_or_404<span class="br0">&#40;</span>klass, <span class="kw2">id</span>=<span class="kw2">id</span>, <span class="sy0">**</span>kwargs<span class="br0">&#41;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">elif</span> <span class="kw2">len</span><span class="br0">&#40;</span>kwargs<span class="br0">&#41;</span> <span class="sy0">&gt;</span> 0:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> klass.<span class="me1">objects</span>.<span class="me1">get</span><span class="br0">&#40;</span><span class="sy0">**</span>kwargs<span class="br0">&#41;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">else</span>:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">raise</span> <span class="kw2">RuntimeError</span><span class="br0">&#40;</span><span class="st0">&quot;No id or conditions given to 'get'!&quot;</span><span class="br0">&#41;</span>
&nbsp; &nbsp; 
&nbsp; &nbsp; @<span class="kw2">classmethod</span>
&nbsp; &nbsp; <span class="kw1">def</span> add<span class="br0">&#40;</span>klass, <span class="sy0">*</span>args, <span class="sy0">**</span>kwargs<span class="br0">&#41;</span>:
&nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">&quot;&quot;&quot;
&nbsp; &nbsp; &nbsp; &nbsp; @summary:
&nbsp; &nbsp; &nbsp; &nbsp; This is a general method which simply calls 'make' with the same
&nbsp; &nbsp; &nbsp; &nbsp; arguments and then saves the returned object.
&nbsp; &nbsp; &nbsp; &nbsp; &quot;&quot;&quot;</span>
&nbsp; &nbsp; &nbsp; &nbsp; o = klass.<span class="me1">make</span><span class="br0">&#40;</span><span class="sy0">*</span>args, <span class="sy0">**</span>kwargs<span class="br0">&#41;</span>
&nbsp; &nbsp; &nbsp; &nbsp; o.<span class="me1">save</span><span class="br0">&#40;</span><span class="br0">&#41;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">#if publish:</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1"># &nbsp; &nbsp;o.publish()</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> o</div>
</div>
</pre>
]]></content:encoded>
			<wfw:commentRss>http://proudly.procrasdonate.com/django-tricks-3-model-mixins/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Django Tricks, Part 2 &#8211; Initialize and Install</title>
		<link>http://proudly.procrasdonate.com/django-tricks-2-initialize-and-install/</link>
		<comments>http://proudly.procrasdonate.com/django-tricks-2-initialize-and-install/#comments</comments>
		<pubDate>Sun, 07 Feb 2010 23:30:04 +0000</pubDate>
		<dc:creator>Lucy</dc:creator>
				<category><![CDATA[technical]]></category>

		<guid isPermaLink="false">http://proudly.procrasdonate.com/?p=672</guid>
		<description><![CDATA[This post is the second in a series on Django tricks.
We started with automating Admin Model creation. Continuing with automation, this post will cover model initialization and installation.
Initialization is the stuff we would normally put in a constructor, such as register pre-save and post-save signals. 
Models are meta-classes, so they don&#8217;t have normal constructors. Instead, [...]]]></description>
			<content:encoded><![CDATA[<p>This post is the second in a series on Django tricks.</p>
<p>We started with <a href="http://proudly.procrasdonate.com/django-tricks-1-adminizer/">automating Admin Model creation</a>. Continuing with automation, this post will cover model <em>initialization</em> and <em>installation</em>.</p>
<p><em>Initialization</em> is the stuff we would normally put in a constructor, such as register pre-save and post-save signals. </p>
<p>Models are meta-classes, so they don&#8217;t have normal constructors. Instead, we put all the initialization stuff in a classmethod called <em>Initialize</em>:</p>
<pre>
<div class="codesnip-container" >
<div class="python codesnip" style="font-family:monospace;"><span class="kw1">class</span> MyModel<span class="br0">&#40;</span>models.<span class="me1">Model</span><span class="br0">&#41;</span>:
&nbsp; &nbsp; @<span class="kw2">classmethod</span>
&nbsp; &nbsp; <span class="kw1">def</span> Initialize<span class="br0">&#40;</span>klass<span class="br0">&#41;</span>:
&nbsp; &nbsp; &nbsp; &nbsp; models.<span class="me1">signals</span>.<span class="me1">pre_save</span>.<span class="me1">connect</span><span class="br0">&#40;</span>MyModel.<span class="me1">sanitize_user_input</span>, sender=MyModel<span class="br0">&#41;</span></div>
</div>
</pre>
<p>In the app&#8217;s __init__.py file, we automatically look for Initialize methods to call. </p>
<h3>__init__.py</h3>
<pre>
<div class="codesnip-container" >
<div class="python codesnip" style="font-family:monospace;"><span class="kw1">from</span> models <span class="kw1">import</span> ALL_MODELS

<span class="kw1">for</span> model <span class="kw1">in</span> ALL_MODELS:
&nbsp; &nbsp; <span class="kw1">if</span> <span class="kw2">hasattr</span><span class="br0">&#40;</span>model, <span class="st0">'Initialize'</span><span class="br0">&#41;</span>:
&nbsp; &nbsp; &nbsp; &nbsp; model.<span class="me1">Initialize</span><span class="br0">&#40;</span><span class="br0">&#41;</span></div>
</div>
</pre>
<p>We could put signal registration directly in the model&#8217;s file, but it&#8217;s nicer to encapsulate all this Initialization stuff in a function, and then automate that function&#8217;s execution.</p>
<p>For more neat initializations <a href="http://proudly.procrasdonate.com/django-tricks-3-model-mixins">read part 3 of this series, Model mixins</a>.</p>
<p><em>Installation</em> is stuff we want to do once the model class is synched with the database, such as installing a fixture or initial data. </p>
<p>As with Initialization, we can optionally add a classmethod called <em>install</em> to a model. A post_syncdb function will look for install functions to call when iterating over models.</p>
<h3>management.py</h3>
<pre>
<div class="codesnip-container" >
<div class="python codesnip" style="font-family:monospace;"><span class="kw1">from</span> django.<span class="me1">dispatch</span> <span class="kw1">import</span> dispatcher
<span class="kw1">from</span> django.<span class="me1">db</span>.<span class="me1">models</span> <span class="kw1">import</span> signals
&nbsp;
<span class="kw1">import</span> models
&nbsp;
<span class="st0">&quot;&quot;&quot;
@summary: Install initial data when models are first created.
&nbsp;
Automatically called by django.
Find out more be searching for &quot;management.py&quot; and &quot;Extra special stuff&quot; in
http://www.b-list.org/weblog/2006/sep/10/django-tips-laying-out-application/
document on wiki:
http://bilumi.org/trac/wiki/PostSyncdbInstall
&quot;&quot;&quot;</span>
&nbsp;
<span class="kw1">def</span> post_syncdb<span class="br0">&#40;</span><span class="kw3">signal</span>, sender, app, created_models, <span class="sy0">**</span>kwargs<span class="br0">&#41;</span>:
&nbsp; &nbsp; <span class="co1"># only run when models we care about are first created</span>
&nbsp; &nbsp; <span class="kw1">if</span> <span class="kw3">signal</span> == signals.<span class="me1">post_syncdb</span> <span class="kw1">and</span> app == models:
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">for</span> model <span class="kw1">in</span> models.<span class="me1">ALL_MODELS</span>:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="kw2">hasattr</span><span class="br0">&#40;</span>model, <span class="st0">'install'</span><span class="br0">&#41;</span>:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> kwargs<span class="br0">&#91;</span><span class="st0">'verbosity'</span><span class="br0">&#93;</span> <span class="sy0">&gt;</span> 0:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">print</span> <span class="st0">&quot;Installing &quot;</span>, model
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; model.<span class="me1">install</span><span class="br0">&#40;</span><span class="br0">&#41;</span>
&nbsp;
signals.<span class="me1">post_syncdb</span>.<span class="me1">connect</span><span class="br0">&#40;</span>post_syncdb<span class="br0">&#41;</span></div>
</div>
</pre>
<p>Proudly ProcrasDonating,</p>
<p>Lucy.</p>
]]></content:encoded>
			<wfw:commentRss>http://proudly.procrasdonate.com/django-tricks-2-initialize-and-install/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Django Tricks, Part 1 &#8211; Adminizer</title>
		<link>http://proudly.procrasdonate.com/django-tricks-1-adminizer/</link>
		<comments>http://proudly.procrasdonate.com/django-tricks-1-adminizer/#comments</comments>
		<pubDate>Sun, 07 Feb 2010 23:04:21 +0000</pubDate>
		<dc:creator>Lucy</dc:creator>
				<category><![CDATA[technical]]></category>

		<guid isPermaLink="false">http://proudly.procrasdonate.com/?p=659</guid>
		<description><![CDATA[This post is the first in a series on Django tricks.
Since the admin app is one of the stars of Django, we&#8217;ll start with our admin module, Adminizer.
The point of Adminizer is to reduce all that fluff around admin classes. Rather than literally writing admin classes for every one of our models, we automatically create [...]]]></description>
			<content:encoded><![CDATA[<p>This post is the first in a series on Django tricks.</p>
<p>Since the admin app is one of the stars of Django, we&#8217;ll start with our admin module, Adminizer.</p>
<div id="attachment_664" class="wp-caption aligncenter" style="width: 355px"><img src="http://proudly.procrasdonate.com/wp-content/uploads/admins.png" alt="Automate Admin Model creation" title="Automate Admin Model creation" width="345"  class="size-full wp-image-664" /><p class="wp-caption-text">Automate Admin Model creation</p></div>
<p>The point of Adminizer is to reduce all that fluff around admin classes. Rather than literally writing admin classes for every one of our models, we automatically create them.</p>
<p>Adminizer.Adminize() takes a model or list of models as input. For each model, Adminizer creates a ModelAdmin class and registers it with the admin app.</p>
<p>What if we want to provide custom <a href="http://docs.djangoproject.com/en/dev/ref/contrib/admin/#modeladmin-options">admin options</a>? I initially wrote this so that we define a field in our model, called <em>admin_options</em>, that is a dictionary of the desired admin model fields. I&#8217;ve since added detection of an inner class called <em>Admin</em> with fields that match the admin model fields. This is more readable than a dictionary, and seems to be where the admin app is headed.</p>
<p>Voila! I frequently add new models to new and existing apps; by automating the AdminModel creation, I get the Admin app literally for free.</p>
<p>Below are the relevant code snippets building out from the models to the Adminizer.</p>
<p>Proudly ProcrasDonating,</p>
<p>Lucy.</p>
<h3>data.py</h3>
<pre>
<div class="codesnip-container" >
<div class="python codesnip" style="font-family:monospace;"><span class="kw1">class</span> MyModel<span class="br0">&#40;</span>models.<span class="me1">Model</span><span class="br0">&#41;</span>:
&nbsp; &nbsp; slug = models.<span class="me1">SlugField</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp; &nbsp; name = models.<span class="me1">CharField</span><span class="br0">&#40;</span>max_length=<span class="nu0">200</span><span class="br0">&#41;</span>

&nbsp; &nbsp; admin_options = <span class="br0">&#123;</span><span class="st0">'prepopulated_fields'</span>: <span class="br0">&#123;</span><span class="st0">&quot;slug&quot;</span>: <span class="br0">&#40;</span><span class="st0">&quot;name&quot;</span>,<span class="br0">&#41;</span><span class="br0">&#125;</span> <span class="br0">&#125;</span>

&nbsp; &nbsp; <span class="co1">#or</span>

&nbsp; &nbsp; <span class="kw1">class</span> Admin:
&nbsp; &nbsp; &nbsp; &nbsp; prepopulated_fields = <span class="br0">&#123;</span><span class="st0">&quot;slug&quot;</span>: <span class="br0">&#40;</span><span class="st0">&quot;name&quot;</span>,<span class="br0">&#41;</span><span class="br0">&#125;</span>
...

<span class="me1">ALL_MODELS</span> = <span class="br0">&#91;</span>MyModel, ...<span class="br0">&#93;</span></div>
</div>
</pre>
<h3>models.py</h3>
<pre>
<div class="codesnip-container" >
<div class="python codesnip" style="font-family:monospace;"><span class="kw1">from</span> data <span class="kw1">import</span> ALL_MODELS <span class="kw1">as</span> DATA_MODELS
<span class="kw1">from</span> rank <span class="kw1">import</span> ALL_MODELS <span class="kw1">as</span> RANK_MODELS
<span class="kw1">from</span> fps_models <span class="kw1">import</span> ALL_MODELS <span class="kw1">as</span> FPS_MODELS
<span class="kw1">from</span> log <span class="kw1">import</span> ALL_MODELS <span class="kw1">as</span> LOG_MODELS
<span class="kw1">from</span> analytics <span class="kw1">import</span> ALL_MODELS <span class="kw1">as</span> ANALYTICS_MODELS

<span class="kw1">from</span> data <span class="kw1">import</span> <span class="sy0">*</span>
<span class="kw1">from</span> rank <span class="kw1">import</span> <span class="sy0">*</span>
<span class="kw1">from</span> fps_models <span class="kw1">import</span> <span class="sy0">*</span>
<span class="kw1">from</span> log <span class="kw1">import</span> <span class="sy0">*</span>
<span class="kw1">from</span> analytics <span class="kw1">import</span> <span class="sy0">*</span>

ALL_MODELS = DATA_MODELS + RANK_MODELS + FPS_MODELS + LOG_MODELS + ANALYTICS_MODELS</div>
</div>
</pre>
<h3>__init__.py</h3>
<pre>
<div class="codesnip-container" >
<div class="python codesnip" style="font-family:monospace;"><span class="kw1">from</span> models <span class="kw1">import</span> ALL_MODELS

<span class="kw1">from</span> lib.<span class="me1">admins</span> <span class="kw1">import</span> Adminizer
Adminizer.<span class="me1">Adminize</span><span class="br0">&#40;</span>ALL_MODELS<span class="br0">&#41;</span></div>
</div>
</pre>
<h3>lib/admins.py</h3>
<pre>
<div class="codesnip-container" >
<div class="python codesnip" style="font-family:monospace;"><span class="kw1">from</span> django.<span class="me1">contrib</span> <span class="kw1">import</span> admin
<span class="kw1">import</span> settings

<span class="kw1">class</span> Adminizer<span class="br0">&#40;</span><span class="kw2">object</span><span class="br0">&#41;</span>:

&nbsp; &nbsp; @<span class="kw2">classmethod</span>
&nbsp; &nbsp; <span class="kw1">def</span> _model_admin_class<span class="br0">&#40;</span>cls, options<span class="br0">&#41;</span>:
&nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">&quot;&quot;&quot;
&nbsp; &nbsp; &nbsp; &nbsp; @summary: returns an admin.ModelAdmin class with the specified 
&nbsp; &nbsp; &nbsp; &nbsp; ModelAdmin options set. see:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; http://docs.djangoproject.com/en/dev/ref/contrib/admin/#modeladmin-options
&nbsp; &nbsp; &nbsp; &nbsp; @param options: dictionary of ModelAdmin options. key should be string 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; that corresponds to option name (eg, 'date_hierarchy'). value should be
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; intended value for field.
&nbsp; &nbsp; &nbsp; &nbsp; @return: sub-class of admin.ModelAdmin
&nbsp; &nbsp; &nbsp; &nbsp; &quot;&quot;&quot;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">class</span> klass<span class="br0">&#40;</span>admin.<span class="me1">ModelAdmin</span><span class="br0">&#41;</span>:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">pass</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">for</span> field_name, field_value <span class="kw1">in</span> options.<span class="me1">items</span><span class="br0">&#40;</span><span class="br0">&#41;</span>:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">setattr</span><span class="br0">&#40;</span>klass, field_name, field_value<span class="br0">&#41;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> klass
&nbsp; &nbsp; 
&nbsp; &nbsp; @<span class="kw2">classmethod</span>
&nbsp; &nbsp; <span class="kw1">def</span> Adminize<span class="br0">&#40;</span>cls, klasses<span class="br0">&#41;</span>:
&nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">&quot;&quot;&quot;
&nbsp; &nbsp; &nbsp; &nbsp; &quot;&quot;&quot;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="kw1">not</span> <span class="kw2">isinstance</span><span class="br0">&#40;</span>klasses, <span class="br0">&#40;</span><span class="kw2">list</span>, <span class="kw2">tuple</span><span class="br0">&#41;</span><span class="br0">&#41;</span>:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; klasses = <span class="br0">&#40;</span>klasses,<span class="br0">&#41;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">for</span> klass <span class="kw1">in</span> klasses:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="kw2">hasattr</span><span class="br0">&#40;</span>klass, <span class="st0">'admin_options'</span><span class="br0">&#41;</span>:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; options = klass.<span class="me1">admin_options</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">elif</span> <span class="kw2">hasattr</span><span class="br0">&#40;</span>klass, <span class="st0">'Admin'</span><span class="br0">&#41;</span>:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; options = <span class="br0">&#123;</span><span class="br0">&#125;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">for</span> k, v <span class="kw1">in</span> klass.<span class="me1">Admin</span>.<span class="kw4">__dict__</span>.<span class="me1">items</span><span class="br0">&#40;</span><span class="br0">&#41;</span>:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> k<span class="br0">&#91;</span>:<span class="nu0">2</span><span class="br0">&#93;</span> <span class="sy0">!</span>= <span class="st0">&quot;__&quot;</span>:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; options<span class="br0">&#91;</span>k<span class="br0">&#93;</span> = v
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">else</span>:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; options = <span class="br0">&#123;</span><span class="br0">&#125;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; admin_klass = cls._model_admin_class<span class="br0">&#40;</span>options<span class="br0">&#41;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; admin.<span class="kw3">site</span>.<span class="me1">register</span><span class="br0">&#40;</span>klass, admin_klass<span class="br0">&#41;</span></div>
</div>
</pre>
]]></content:encoded>
			<wfw:commentRss>http://proudly.procrasdonate.com/django-tricks-1-adminizer/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>Incentivize something we can measure</title>
		<link>http://proudly.procrasdonate.com/incentivize_measure/</link>
		<comments>http://proudly.procrasdonate.com/incentivize_measure/#comments</comments>
		<pubDate>Sun, 24 Jan 2010 05:36:31 +0000</pubDate>
		<dc:creator>Lucy</dc:creator>
				<category><![CDATA[conceptual]]></category>

		<guid isPermaLink="false">http://proudly.procrasdonate.com/?p=562</guid>
		<description><![CDATA[In five words, the general idea behind ProcrasDonate is
Incentivize something we can measure.
Let me first say that I love the procrastination-donation duo that we&#8217;re starting with. The ProcrasDonate name is dynamite, the explanation is simple&#8212;donate to charity for every hour procrastinated&#8212;and the user response is energetic. 
Nonetheless, it&#8217;s fun to think of other applications for [...]]]></description>
			<content:encoded><![CDATA[<p>In five words, the general idea behind ProcrasDonate is</p>
<blockquote><p>Incentivize something we can measure.</p></blockquote>
<p>Let me first say that I love the procrastination-donation duo that we&#8217;re starting with. The ProcrasDonate name is dynamite, the explanation is simple&#8212;donate to charity for every hour procrastinated&#8212;and the user response is energetic. </p>
<p>Nonetheless, it&#8217;s fun to think of other applications for the general idea. We&#8217;ve received so many ideas that I can&#8217;t help wanting to make sense of them all. Here&#8217;s what I&#8217;ve come up with:</p>
<div class="wp-caption alignleft" style="width: 330px">
<table style="width:100%;">
<tbody>
<tr>
<td></td>
<td></td>
<td style="background: #DDDDDD;" colspan="2">Behavior</td>
</tr>
<tr>
<td></td>
<td></td>
<td style="background: #FFCCCC;">Vice</td>
<td style="background: #FFFFCC;">Virtue</td>
</tr>
<tr>
<td style="background: #DDDDDD;" rowspan="2">Incentive</td>
<td style="background: #CCCCFF;">Punishment</td>
<td>II</td>
<td>I</td>
</tr>
<tr>
<td style="background: #DDFFDD;">Reward</td>
<td>III</td>
<td>IV</td>
</tr>
</tbody>
</table>
<p><p class="wp-caption-text">Behavior-Incentive Chart</p></div></p>
<p>The chart presents two orthogonal dimensions: <strong>behavior</strong> and <strong>incentive</strong>. A good behavior is a <strong>virtue</strong>, a bad behavior is a <strong>vice</strong>. A positive incentive is a <strong>reward</strong>, a negative incentive is a <strong>punishment</strong>.</p>
<p>Ideas are broken down into behaviors and incentives, which are partitioned into each dimension&#8217;s end-points. Taking a cross-product of each behavior against each incentive shows us all the applications we could build.</p>
<p>In this simplistic abstraction I&#8217;ve evaluated each idea in binary. In truth, the &#8220;goodness&#8221; or &#8220;badness&#8221; of each end-point is relative to the individual. So it goes.</p>
<h3>Vices (if harmful)</h3>
<ul>
<li>Online procrastination (facebook, chat, blogs, feeds, games, TV, movies, videos</li>
<li>Missing a deadline</li>
<li>Addictions (smoking, drinking, gambling)</li>
<li>Physical laziness</li>
<li>Staying up or waking up late</li>
<li>Sending too much email</li>
<li>Unhealthy eating</li>
<li>Being mean</li>
<li>Greed</li>
</ul>
<h3>Virtues (if empowering)</h3>
<ul>
<li>Productive</li>
<li>Meet deadline</li>
<li>Learning</li>
<li>Making</li>
<li>Fighting addiction (not smoking, not drinking, etc)</li>
<li>Exercising</li>
<li>Early to bed, early to rise</li>
<li>Taking pills</li>
<li>Good grades</li>
<li>Healthy eating</li>
<li>Being nice</li>
<li>Enabling</li>
</ul>
<h3>Punishments</h3>
<ul>
<li>Lose money</li>
<li>Lose time (signed up to tutor or be tutored, community service)</li>
<li>Volunteer for non-profit or open source project</li>
<li>Shame (publicize results to friends)</li>
<li>Annoying warnings, alarms</li>
<li>Prevent behavior (eg, block procrastination sites)</li>
</ul>
<h3>Rewards</h3>
<ul>
<li>Receive money (savings account, from parents, wager with friends, coupons)</li>
<li>Gain time (virtues are good in themselves??)</li>
<li>Accolades</li>
<li>Enable behavior (desserts after meal)</li>
<li>Affirmation (support, coaching)</li>
<li>Specific goal (eg, plan a trip)
</ul>
<h3>ProcrasDonate</h3>
<p>Where would you put ProcrasDonate on the chart?</p>
<p>I&#8217;d put ProcrasDonate in quadrants II and III. Donations play a dual role of reward&#8212;support good causes&#8212;and punishment&#8212;lose money. This twist piques many people&#8217;s interest, especially mine.</p>
<h3>Bets</h3>
<p>One kind of app that isn&#8217;t clear from the above model is how a group of friends might reward and incentivize each other. </p>
<p>Social dynamics draw on shame, affirmation, competition, and resource re-distribution. For example, each week the friends bet money on their own success. The winners receive either money, decision-making power or a random act of kindness as prizes. The losers give up those things.</p>
<p>This strikes me as a particularly fun and effective behavior-changing strategy, if sufficiently coupled with graphs, of course <img src='http://proudly.procrasdonate.com/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' /> .</p>
<p>The more ideas the merrier, so keep &#8216;em coming.</p>
<p>Proudly ProcrasDonating,<br />
Lucy.</p>
]]></content:encoded>
			<wfw:commentRss>http://proudly.procrasdonate.com/incentivize_measure/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Developer Retrospective</title>
		<link>http://proudly.procrasdonate.com/retrospective/</link>
		<comments>http://proudly.procrasdonate.com/retrospective/#comments</comments>
		<pubDate>Sat, 23 Jan 2010 00:44:43 +0000</pubDate>
		<dc:creator>Lucy</dc:creator>
				<category><![CDATA[planning]]></category>

		<guid isPermaLink="false">http://proudly.procrasdonate.com/?p=578</guid>
		<description><![CDATA[While too much naval gazing detracts from real work, learning from the past is important for improving future outcomes. It&#8217;s also fun to look back and see how much we&#8217;ve accomplished!
I like to evaluate extended periods of technical work in terms of throughput, quality and direction. 
Throughput means getting a lot done. The more quantifiable, [...]]]></description>
			<content:encoded><![CDATA[<p>While too much naval gazing detracts from real work, learning from the past is important for improving future outcomes. It&#8217;s also fun to look back and see how much we&#8217;ve accomplished!</p>
<p>I like to evaluate extended periods of technical work in terms of throughput, quality and direction. </p>
<p><strong>Throughput</strong> means getting a lot done. The more quantifiable, complete results, the better.</p>
<p><strong>Quality</strong> means the work is real and not hidden. Quality work doesn&#8217;t cause more work later by introducing bugs or making it harder to add features. Ideally, <em>past work enables future work so that our progress is like a boulder rolling down hill picking up speed</em>.</p>
<p><strong>Direction</strong> means driving the company towards success. Burning down the wrong mountain is no good.</p>
<p>Looking back on the past three months, which is when we released our new Amazon Payments powered add-on, ProcrasDonate has demonstrated success in all three areas. I&#8217;m personally proud to have kept Throughput and Quality in balance. I would also like to highlight the excellent job Clay is doing with Direction, especially in response to users, market research and outreach efforts.</p>
<p>On the technology side, we accomplished our goals to:</p>
<ul>
<li>Solidify our core product</li>
<li>Iterate each week</li>
<li>Respond to rounds of beta testers</li>
</ul>
<p>Two goals moving forward are to:</p>
<ol>
<li>Continue responding to user feedback and developing the core product</li>
<li>Implement selected ideas based on new market research</li>
</ol>
<h3>Pivotal Tracker Data</h3>
<p>The data from <a href="http://proudly.procrasdonate.com/project-management-and-motivation/">Pivotal Tracker</a>, which we use to manage technology tasks, provides its own narrow but fun perspective. Ain&#8217;t data great!!?!</p>
<p>Below are our release charts from Pivotal Tracker. Every day is spent working towards a specific release. Each release chart plots the number of work left at the close of each day. <strong>Work</strong> is defined in abstract <strong>story points</strong>, which I estimate when stories are created. Sometimes I adjust these amounts when a story is completed. I try to be consistent without worrying about precision.</p>
<p><span style="color:red;">Red</span> and <span style="color:green;">green</span> lines show &#8220;expected&#8221; progress. On the first day, they show the total number of points in the story. At the deadline they show all tasks completed.</p>
<p><span style="color:blue;">Blue</span> lines show how many points actually remained at the end of that day. They do not reflect stories that are in progress. To burn down a story the whole completed story must be delivered.</p>
<p>Chores do not have points and thus do not show up on these charts. Chores include backend work, developer scripts and testing. Bugs do not have points, either. A breakdown of story types is shown in the next section.</p>
<p>Finally, <em>each iteration is exactly one week, but the period of each release varies</em>. Our first public release using Amazon payments, release 0.3.0, is 3 weeks long. The rest are approximately 1 week each, although things often worked out into one long release and one short release. We didn&#8217;t want to be so concerned about deadlines that we didn&#8217;t take risks, but we also wanted to get into the habit of meeting deadlines&#8230;.so we alternated.</p>
<div class="wp-caption alignleft" style="width: 155px"><a href="http://proudly.procrasdonate.com/wp-content/uploads/nov_jan_retrospective/0.3.0.png"><img src="http://proudly.procrasdonate.com/wp-content/uploads/nov_jan_retrospective/0.3.0.png" alt="progress chart" title="0.3.0" width="145" /></a><p class="wp-caption-text">0.3.0 - working</p></div>
<div class="wp-caption alignleft" style="width: 155px"><a href="http://proudly.procrasdonate.com/wp-content/uploads/nov_jan_retrospective/0.3.1.png"><img src="http://proudly.procrasdonate.com/wp-content/uploads/nov_jan_retrospective/0.3.1.png" alt="progress chart" title="0.3.1" width="145" /></a><p class="wp-caption-text">0.3.1 - email</p></div>
<div class="wp-caption alignleft" style="width: 155px"><a href="http://proudly.procrasdonate.com/wp-content/uploads/nov_jan_retrospective/0.3.2.png"><img src="http://proudly.procrasdonate.com/wp-content/uploads/nov_jan_retrospective/0.3.2.png" alt="progress chart" title="0.3.2" width="145" /></a><p class="wp-caption-text">0.3.2 - new homepage</p></div>
<div class="wp-caption alignleft" style="width: 155px"><a href="http://proudly.procrasdonate.com/wp-content/uploads/nov_jan_retrospective/0.3.3.png"><img src="http://proudly.procrasdonate.com/wp-content/uploads/nov_jan_retrospective/0.3.3.png" alt="progress chart" title="0.3.3" width="145" /></a><p class="wp-caption-text">0.3.3 - slim registration</p></div>
<div class="wp-caption alignleft" style="width: 155px"><a href="http://proudly.procrasdonate.com/wp-content/uploads/nov_jan_retrospective/0.3.4.png"><img src="http://proudly.procrasdonate.com/wp-content/uploads/nov_jan_retrospective/0.3.4.png" alt="progress chart" title="0.3.4" width="145" /></a><p class="wp-caption-text">0.3.4 - polish registration</p></div>
<div class="wp-caption alignleft" style="width: 155px"><a href="http://proudly.procrasdonate.com/wp-content/uploads/nov_jan_retrospective/0.3.5.png"><img src="http://proudly.procrasdonate.com/wp-content/uploads/nov_jan_retrospective/0.3.5.png" alt="progress chart" title="0.3.5" width="145" /></a><p class="wp-caption-text">0.3.5 - graphicals</p></div>
<div class="wp-caption alignleft" style="width: 155px"><a href="http://proudly.procrasdonate.com/wp-content/uploads/nov_jan_retrospective/0.3.6.png"><img src="http://proudly.procrasdonate.com/wp-content/uploads/nov_jan_retrospective/0.3.6.png" alt="progress chart" title="0.3.6" width="145" /></a><p class="wp-caption-text">0.3.6 - smooth install</p></div>
<div class="wp-caption alignleft" style="width: 155px"><a href="http://proudly.procrasdonate.com/wp-content/uploads/nov_jan_retrospective/0.3.7.png"><img src="http://proudly.procrasdonate.com/wp-content/uploads/nov_jan_retrospective/0.3.7.png" alt="progress chart" title="0.3.7" width="145" /></a><p class="wp-caption-text">0.3.7 - more info</p></div>
<div class="wp-caption alignleft" style="width: 155px"><a href="http://proudly.procrasdonate.com/wp-content/uploads/nov_jan_retrospective/0.3.8.png"><img src="http://proudly.procrasdonate.com/wp-content/uploads/nov_jan_retrospective/0.3.8.png" alt="progress chart" title="0.3.8" width="145" /></a><p class="wp-caption-text">0.3.8 - community</p></div>
<div class="wp-caption alignleft" style="width: 155px"><a href="http://proudly.procrasdonate.com/wp-content/uploads/nov_jan_retrospective/0.3.9.png"><img src="http://proudly.procrasdonate.com/wp-content/uploads/nov_jan_retrospective/0.3.9.png" alt="progress chart" title="0.3.9" width="145" /></a><p class="wp-caption-text">0.3.9 - ready for strangers</p></div>
<div class="wp-caption alignleft" style="width: 155px"><a href="http://proudly.procrasdonate.com/wp-content/uploads/nov_jan_retrospective/0.4.0.png"><img src="http://proudly.procrasdonate.com/wp-content/uploads/nov_jan_retrospective/0.4.0.png" alt="progress chart" title="0.4.0" width="145" /></a><p class="wp-caption-text">0.4.0 - faster</p></div>
<div class="wp-caption alignleft" style="width: 155px"><a href="http://proudly.procrasdonate.com/wp-content/uploads/nov_jan_retrospective/0.4.1.png"><img src="http://proudly.procrasdonate.com/wp-content/uploads/nov_jan_retrospective/0.4.1.png" alt="progress chart" title="0.4.1" width="145" /></a><p class="wp-caption-text">0.4.1 - communication</p></div>
<div class="wp-caption alignleft" style="width: 155px"><a href="http://proudly.procrasdonate.com/wp-content/uploads/nov_jan_retrospective/0.4.2.png"><img src="http://proudly.procrasdonate.com/wp-content/uploads/nov_jan_retrospective/0.4.2.png" alt="progress chart" title="0.4.2" width="145" /></a><p class="wp-caption-text">0.4.2 - payments</p></div>
<h3>Velocity</h3>
<p>Velocity is the number of points the team accomplishes each iteration. Our iterations span one week, and our team, in terms of developer work, consists primarily of me.</p>
<p>The black line shows the &#8220;running velocity,&#8221; which is the average of the past three weeks. The running velocity is used by Pivotal Tracker to automatically partition our prioritized list of stories into iterations. </p>
<p>It&#8217;s nice to work so fast that new stories have to roll in from the backlog. It&#8217;s motivating to have dipped the velocity so much around New Year&#8217;s that our backlog was temporarily chunked into iterations stretching to the summer! </p>
<div class="wp-caption alignnone" style="width: 365px"><a href="http://proudly.procrasdonate.com/wp-content/uploads/nov_jan_retrospective/velocity.png"><img src="http://proudly.procrasdonate.com/wp-content/uploads/nov_jan_retrospective/velocity.png" alt="Velocity each week" title="velocity each week" width="355" /></a><p class="wp-caption-text">Velocity each week</p></div>
<h3>Story Type Breakdown</h3>
<p>Features have points. They are customer facing value-adds.</p>
<p>Chores and bugs do not have points. Chores include backend work, developer scripts and testing. Bugs are existing features that are broken. </p>
<p>The following chart stacks each of these types on top of each other.</p>
<div class="wp-caption alignnone" style="width: 365px"><a href="http://proudly.procrasdonate.com/wp-content/uploads/nov_jan_retrospective/breakdown.png"><img src="http://proudly.procrasdonate.com/wp-content/uploads/nov_jan_retrospective/breakdown.png" alt="Story type breakdown" title="Story type breakdown" width="355" /></a><p class="wp-caption-text">Story type breakdown: BLUE = features, BLACK = chores, RED = bugs, YELLOW LINE = points completed</p></div>
<h3>Retrospective</h3>
<p>Let me first mention the New Years Eve dip. I took a couple days off to recharge <img src='http://proudly.procrasdonate.com/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' /> </p>
<p>The low-point iterations are because of predominantly backend work, such as automatic backups that are restored on a dedicated test machine, and better build scripts, etc.</p>
<p>In the middle is an iteration in which no stories were completed. A very large chore&#8212;automatic cross-platform testing&#8212;did land in the next iteration, but that only shows up as a single chore.</p>
<p>That large chore was a rabbit hole. The crosstester app I made is totally awesome, but I should have reigned it in to a couple VMs with brief instructions for manual tests. I&#8217;ve learned from this and moved on.</p>
<p>What else did I learn?</p>
<p>We became better at meeting deadlines. <img src='http://proudly.procrasdonate.com/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
<p>Story point estimation and consistency wasn&#8217;t as variable as I expected. I don&#8217;t remember changing story point estimations all that much, but there are many factors influencing what appears to be a level chart across the weeks (other than the rabbit hole dip).</p>
<p>Investigatory stories are always a little troublesome, since after the investigation they either turn into work or are halted, so you&#8217;re never quite sure whether they&#8217;re going to be 1 or 3 points&#8230;.and we don&#8217;t start out thinking they&#8217;re investigations. Not so many stories are investigations.</p>
<p>I have to remind myself that we became faster at certain implementations. <em>Tasks that gave me pause in November (2 or 3 pointers) are no big deal now (easy 1 pointers).</em> This comes down to familiarity and reusable libraries and scripts.</p>
<p>The first time I had to migrate the extension database I thought it might take all day. I&#8217;ve done so many data transformations and database migrations that I&#8217;ve ceased putting &#8220;updates&#8221; and &#8220;release prep&#8221; on Pivotal Tracker (we never create 0 point stories&#8230;just do it!). The first time I wrote tests I also wrote the framework. Some stories get easier over time. Even when additional features are added, or a different kind of test, I can get <b>more</b> done in the same span of time.</p>
<p>Lastly, we averaged completing 7 features (not points), 2 bugs and 1 chore each iteration, with little variation. Keeping bugs and chores low and attacking features mercilessly&#8212;the right features, of course&#8212;sounds good to me. I was curious about this breakdown and variability when we first started using Pivotal Tracker, so it&#8217;s neat to see the results now.</p>
<p>That&#8217;s enough navel gazing for now. </p>
<p>I&#8217;ve appended the release notes below for anyone interested. Release notes are written for non-technical users, so only features and bugs are covered, and then in pretty general terms. (They show up on the Firefox tab that automatically opens to say &#8220;Congratulations! You&#8217;ve installed [or updated]&#8230;&#8221;)</p>
<p>Proudly ProcrasDonating,<br />
Lucy.</p>
<h3>Release Notes</h3>
<p><b>0.3.0.html</b></p>
<ul>
<li>ProcrasDonate only tracks the Firefox tab you are currently viewing. It stops tracking as<br />
	soon as you switch to another application or stop using your computer.</li>
</ul>
<p><b>0.3.1.html</b></p>
<ul>
<li>Minor usability improvements</li>
</ul>
<p><b>0.3.2.html</b></p>
<ul>
<li>New homepage design</li>
<li>Website classifications added to My Progress page below the gauges</li>
<li>Proper uninstall added (all data is deleted)</li>
<li>Add-on build and update process improved. Update download problem (&#8221;incorrect hash&#8221;) should be fixed.</li>
<li>Registration process improvements on payments tab</li>
</ul>
<p><b>0.3.3.html</b></p>
<ul>
<li>User registration streamlined into just 4 steps</li>
<li><em>My Progress</em> page enhancements:</li>
<ul>
<li>Classify websites by entering them directly in the <em>Classifications</em> tab</li>
<li>New <em>Visits</em> tab that shows all of your page visits for the day</li>
</ul>
<li>Time tracking halts when computer goes to sleep<br />
	and resumes, if Firefox is the active application, when computer wakes up <img src='http://proudly.procrasdonate.com/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </li>
<li>Fixed bug in which flash was not being detected on currently viewed webpage,<br />
	thus the user needed only to be idle for 3 minutes for time tracking to halt.<br />
	With the fix, pages that contain flash halt time tracking once the user<br />
	is idle for 20 minutes. See the development blog post on<br />
	<a href="http://proudlyprocrasdonating.wordpress.com/2009/12/05/time-tracker-sleuthing/"><br />
	How we track website visits</a> for more time tracking details.</li>
<li>Fixed toolbar icon bug in which times over 24 hours were reported incorrectly.</li>
</ul>
<p><b>0.3.4.html</b></p>
<ul>
<li>When upgrades are available,<br />
users receive upgrade alert once a day when they visit the ProcrasDonate website.</li>
</ul>
<p><b>0.3.5.html</b></p>
<ul>
<li>Graphicals!</li>
<ul>
<li>Pie chart added to help with choosing donation percentages to multiple charities.</li>
<li>Trend section added to <em>My Progress</em>. Plots hours per day spent at different websites and overall.</li>
</ul>
<li>No visits are recorded while Firefox is in Private Browsing mode. For more information, see<br />
	the FAQ question <a href="{% url faq %}#time">What is tracked?</a></li>
<p>.</p>
<li>If you had trouble with toolbar icons disappearing they should be back, working and shinier than ever.</li>
</ul>
<p><b>0.3.6.html</b></p>
<ul>
<li><a href="{% url my_messages %}">My Messages</a> tab added.<br />
	Go here to view a helpful welcome message and<br />
	your weekly affirmation reports.</li>
<li>Top bar inserted above the first ProcrasDonate website visited<br />
	of the day <i>if</i> there are unread messages waiting.</li>
<li>Hold down <a href="{% url my_settings %}">My Settings</a> arrows<br />
	for rapid fire incrementing and decrementing.</li>
<li>Fixed post-install bug requiring additional restart</li>
</ul>
<p><b>0.3.7.html</b></p>
<ul>
<li>Play nicely with other Firefox add-ons, or at least don&#8217;t pee in the sandbox.</li>
<li>Enabled announcements from ProcrasDonate and newsletters from charities to be uploaded<br />
to My Messages, as enabled by your update settings.</li>
<li>Duplicate messages removed and underlying bug fixed.</li>
</ul>
<p><b>0.3.8.html</b></p>
<ul>
<li>Community statistics added to homepage</li>
<li>Private beta testing preparation: check</li>
<li>Fixed extension bug in which dollars were being stored as cents. Data stored prior<br />
to this release will remain unchanged. Only new webpage visits are effected.</li>
</ul>
<p><b>0.3.9.html</b></p>
<ul>
<li>Gracefully handle non-http page visits, eg viewing local files</li>
<li>Fixed bug in My Impact display</li>
<li>Removed Buy It Like You Mean It! as a pre-selected charity when downloading<br />
	ProcrasDonate with no pre-selected charity selected.</li>
</ul>
<p><b>0.4.0.html</b></p>
<ul>
<li>Smoother registration process</li>
<li>Fixed co-extension bug: 0.4.0 plays nice with everyone, even bullies</li>
</ul>
<p><b>0.4.1.html</b></p>
<ul>
<li>Welcome and registration emails sent to users.</li>
<li>Homepage improvements: added social sharing site links,<br />
  added <a href="http://twitter.com/procrasdonate">recent tweets</a>,<br />
  process charity logos without reducing apparent quality,<br />
  enabled video playback when charity logos are clicked,<br />
  popup helper instructions when user clicks download button!</li>
<li>Registration incentive page clarified thanks to user feedback.</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://proudly.procrasdonate.com/retrospective/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>ProcrasDonate Press Release</title>
		<link>http://proudly.procrasdonate.com/procrasdonate-press-release/</link>
		<comments>http://proudly.procrasdonate.com/procrasdonate-press-release/#comments</comments>
		<pubDate>Fri, 22 Jan 2010 17:19:00 +0000</pubDate>
		<dc:creator>Lucy</dc:creator>
				<category><![CDATA[news]]></category>
		<category><![CDATA[release]]></category>

		<guid isPermaLink="false">http://proudly.procrasdonate.com/?p=565</guid>
		<description><![CDATA[Help us spread the word: ProcrasDonate is looking for adventurous procrastinators and donators to download our extension and tell us what they think.
Press Release

]]></description>
			<content:encoded><![CDATA[<p>Help us spread the word: ProcrasDonate is looking for adventurous procrastinators and donators to download our extension and tell us what they think.</p>
<h3>Press Release</h3>
<p><iframe src="http://docs.google.com/gview?url=http://proudly.procrasdonate.com/wp-content/uploads/ProcrasDonate_Beta_PressRelease.pdf&#038;embedded=true" style="width:360px; height:480px;" frameborder="0"></iframe></p>
]]></content:encoded>
			<wfw:commentRss>http://proudly.procrasdonate.com/procrasdonate-press-release/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Super Submenu Abstraction</title>
		<link>http://proudly.procrasdonate.com/super-submenu-abstraction/</link>
		<comments>http://proudly.procrasdonate.com/super-submenu-abstraction/#comments</comments>
		<pubDate>Fri, 15 Jan 2010 22:00:43 +0000</pubDate>
		<dc:creator>Lucy</dc:creator>
				<category><![CDATA[conceptual]]></category>
		<category><![CDATA[technical]]></category>

		<guid isPermaLink="false">http://proudlyprocrasdonating.wordpress.com/?p=401</guid>
		<description><![CDATA[Keeping in line with my last post on our testing sequentializer, another neat code snippet I&#8217;d like to pluck for attention is how we generate submenus on extension pages. 
Extension pages are web pages that are constructed directly by the extension on a user&#8217;s computer. For example, My Progress pages have four tabs:

 Gauges
 Sites
 [...]]]></description>
			<content:encoded><![CDATA[<p>Keeping in line with my last post on our <a href="http://proudly.procrasdonate.com/testing-sequentializer/">testing sequentializer</a>, another neat code snippet I&#8217;d like to pluck for attention is how we generate submenus on extension pages. </p>
<p>Extension pages are web pages that are constructed directly by the extension on a user&#8217;s computer. For example, My Progress pages have four tabs:</p>
<ol>
<li><img width="20" src="https://procrasdonate.com/procrasdonate_media/img/GaugesButton.png" /> Gauges</li>
<li><img width="20" src="https://procrasdonate.com/procrasdonate_media/img/LargeUnsortedIcon.png" /> Sites</li>
<li><img width="20" src="https://procrasdonate.com/procrasdonate_media/img/VisitsButton.png" /> Visits</li>
<li><img width="20" src="https://procrasdonate.com/procrasdonate_media/img/TrendsButton.png" /> Trends</li>
</ol>
<p>If a user without our extension goes to the <a href="http://procrasdonate.com/my_progress/">My Progress</a> page, http://procrasdonate.com/my_progress/ , then a single error page is generated by our server. However, a user with our extension is able to view multiple pages at that same URL simply by clicking items in a submenu.</p>
<p>Below are two screenshots of the My Progress pages. In the first, the &#8220;Gauges&#8221; tab is shown. In the second, the &#8220;Trends&#8221; tab is shown. Note that the submenu is the same on all My Progress tabs. </p>
<div id="attachment_542" class="wp-caption alignleft" style="width: 155px"><a href="http://proudly.procrasdonate.com/wp-content/uploads/my_progress_screenshot.png"><img src="http://proudly.procrasdonate.com/wp-content/uploads/my_progress_screenshot.png" alt="my_progress_screenshot" title="my_progress_screenshot" width="145" class="alignleft size-full wp-image-541" /></a><p class="wp-caption-text">Gauges tab on My Progress page</p></div>
<div id="attachment_542" class="wp-caption alignleft" style="width: 155px"><a href="http://proudly.procrasdonate.com/wp-content/uploads/my_progress_screenshot2.png"><img src="http://proudly.procrasdonate.com/wp-content/uploads/my_progress_screenshot2.png" alt="Trends tab on My Progress page" title="my_progress_screenshot2" width="145" class="size-full wp-image-542" /></a><p class="wp-caption-text">Trends tab on My Progress page</p></div>
<p style="clear:both;">
<p>Below are two screens of other extension pages. The My Impact pages all have a single table with four tabs for displaying different tables. The Registration pages are a series of forms for setting up ProcrasDonate for the first time. Both My Impact and Registration use a submenu to generate content.</p>
<div class="wp-caption alignleft" style="width: 155px"><a href="http://proudly.procrasdonate.com/wp-content/uploads/my_impact_screenshot.png"><img src="http://proudly.procrasdonate.com/wp-content/uploads/my_impact_screenshot.png" alt="my_impact_screenshot" title="my_impact_screenshot" width="145" /></a><p class="wp-caption-text">Show All tab on My Impact page</p></div>
<div class="wp-caption alignleft" style="width: 155px"><a href="http://proudly.procrasdonate.com/wp-content/uploads/register_screenshot.png"><img src="http://proudly.procrasdonate.com/wp-content/uploads/register_screenshot.png" alt="Charities tab on Registration page" title="Charities tab on Registration page" width="145" /></a><p class="wp-caption-text">Charities tab on Registration page</p></div>
<p style="clear:both;">
<h3>Submenu Abstraction</h3>
<p>You&#8217;re probably already smiling with how cool this situation is. Let&#8217;s break it down  to savor the moment.</p>
<ol>
<li>We have a single submenu concept: a list of submenu pages plus metadata such as tab name and tab content. The data depends on the page set, eg My Progress, My Impact or Registration, but the data <strong>structure</strong> is the same.</li>
<li>Submenus are displayed differently on each page set, but the functionality&#8212;click on menu item to change page content&#8212;remains the same.</li>
</ol>
<p>The second abstract in which we display the same kind of data differently can be handled by creating a different html template for each submenu. The template for displaying the My Impact table tables has different HTML than the template for displaying My Progress images and tab names.</p>
<p>The super awesome part is that the structure of the submenu data is the same regardless of the page set. This allows the view to <strong>re-use submenu creation and event handling</strong>.</p>
<h3>Submenu Data Structure</h3>
<p>Each time a view is called to render the contents of a tab, it must generate a submenu data structure. This data structure is a list of submenu items. Each item contains the following properties:</p>
<ul>
<li>substate (useful as id)</li>
<li>tab name (user visible)</li>
<li>list of properties (eg: prev, selected)</li>
</ul>
<p>Observe that this structure is keenly linked to the display abstraction. Each menu item includes properties relevant to display and user events. However, actual display implementations are left to the templates.</p>
<p>This data structure is generated by the view based on the user&#8217;s state and the submenu specifications.</p>
<h3>Submenu Specification</h3>
<p>The submenu specification is listed as constants in the extension&#8217;s settings file. </p>
<p>The specifications include the tab names, substate names, submenu images if desired, content insertion functions (aka views) and process functions if desired (useful on registration forms to detect whether errors need to be resolved before continuing).</p>
<p>The My Progress submenu data structure looks like this:</p>
<pre>
<div class="codesnip-container" >
<div class="javascript codesnip" style="font-family:monospace;">constants.<span class="me1">DEFAULT_PROGRESS_STATE</span> <span class="sy0">=</span> <span class="st0">&quot;gauges&quot;</span><span class="sy0">;</span>
constants.<span class="me1">PROGRESS_STATE_ENUM</span> <span class="sy0">=</span> <span class="br0">&#91;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">&quot;gauges&quot;</span><span class="sy0">,</span> <span class="st0">&quot;classifications&quot;</span><span class="sy0">,</span> <span class="st0">&quot;visits&quot;</span><span class="sy0">,</span> <span class="st0">&quot;trends&quot;</span><span class="sy0">,</span> <span class="co2">/*&quot;stacks&quot;, &quot;averages&quot;, &quot;ratios&quot;*/</span>
<span class="br0">&#93;</span><span class="sy0">;</span>
constants.<span class="me1">PROGRESS_STATE_TAB_NAMES</span> <span class="sy0">=</span> <span class="br0">&#91;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">&quot;Gauges&quot;</span><span class="sy0">,</span> <span class="st0">&quot;Sites&quot;</span><span class="sy0">,</span> <span class="st0">&quot;Visits&quot;</span><span class="sy0">,</span> <span class="st0">&quot;Trends&quot;</span><span class="sy0">,</span> <span class="co2">/*&quot;Stacks&quot;, &quot;Averages&quot;, &quot;Ratios&quot;*/</span>
<span class="br0">&#93;</span><span class="sy0">;</span>
constants.<span class="me1">PROGRESS_STATE_INSERTS</span> <span class="sy0">=</span> <span class="br0">&#91;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">&quot;insert_progress_gauges&quot;</span><span class="sy0">,</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">&quot;insert_progress_classifications&quot;</span><span class="sy0">,</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">&quot;insert_progress_visits&quot;</span><span class="sy0">,</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">&quot;insert_progress_trends&quot;</span><span class="sy0">,</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="coMULTI">/*&quot;insert_progress_stacks&quot;,
&nbsp; &nbsp; &nbsp; &nbsp; &quot;insert_progress_averages&quot;,
&nbsp; &nbsp; &nbsp; &nbsp; &quot;insert_progress_ratios&quot;*/</span>
<span class="br0">&#93;</span><span class="sy0">;</span></div>
</div>
</pre>
<p>I included the commented out submenu items because they illustrate why this abstraction is so cool. <strong>Once the abstraction is written, adding submenu items or even whole submenus and pagesets, becomes easier and more importantly, less tedious.</strong> The more real coding instead of infrastructure copy/pasting that I do, the more productive and happier I am, and the less likely the code contains stupid bugs.</p>
<h3>Submenu Activation and Processing</h3>
<p>All extension views share a common process:</p>
<ol>
<li>Set substate if necessary</li>
<li>Generate the submenu</li>
<li>Retrieve content data structures</li>
<li>Render template and insert into page</li>
<li>Activate html</li>
<li>Activate submenu html</li>
</ol>
<p>Activation means adding event handlers to the inserted html. Activating the submenu specifically means adding click events for each submenu item. </p>
<p>When a user clicks a submenu item, the appropriate view needs to be called. If a content processor function is specified, then that needs to be called first to ensure that the user doesn&#8217;t leave a page with erroneous inputs.</p>
<p>I don&#8217;t want to go into our view system too much, but it should be clear how the submenu framework fits cleanly into the view model. Our main goal is to make writing views actually equal writing the data retrieval, template html and html activation if there is any. All the other bullet items could be automated by the view framework.</p>
<p>(Whether everything is abstracted and squirreled away into a frameworked is one of those engineering decisions based on trade-offs. Even when the scope of the problem is well known so that a framework is an obvious non-constraining, yes-extensibility decision, some costs are so low that it&#8217;s better to just got on with the coding and do a little copy and paste. We can&#8217;t write programs to write <em>all</em> our programs for us. Not until the singularity, anyway.)</p>
<h3>All together</h3>
<p>When we put this all together we get an extensible submenu system that reduces the coding of new pages and subpages to the heart of the matter&#8212;specifying data and writing the middle of the view (retrieve content data structures)&#8212;while leaving the tedious submenu data structure generation and activation to already written helper methods.</p>
<p>Some pseudocode and diagrams would probably make this clearer, but for now I hope this overview is still mildly informative. </p>
<p>For those of you starving for more details, I&#8217;ve appended some code snippets to the bottom of this post. You might be particularly interested in the non-trivial closure in the activation function.</p>
<p>Proudly ProcrasDonating,<br />
Lucy.</p>
<pre>
<div class="codesnip-container" >
<div class="javascript codesnip" style="font-family:monospace;">activate_substate_menu_items<span class="sy0">:</span> <span class="kw2">function</span><span class="br0">&#40;</span>request<span class="sy0">,</span> current_substate<span class="sy0">,</span> enums<span class="sy0">,</span> inserts<span class="sy0">,</span> processors<span class="br0">&#41;</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">var</span> self <span class="sy0">=</span> <span class="kw1">this</span><span class="sy0">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; _iterate<span class="br0">&#40;</span>enums<span class="sy0">,</span> <span class="kw2">function</span><span class="br0">&#40;</span>key<span class="sy0">,</span> substate<span class="sy0">,</span> index<span class="br0">&#41;</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>processors<span class="br0">&#41;</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; request.<span class="me1">jQuery</span><span class="br0">&#40;</span><span class="st0">&quot;#substate_tab_&quot;</span><span class="sy0">+</span>substate<span class="sy0">+</span><span class="st0">&quot;, .substate_tab_&quot;</span><span class="sy0">+</span>substate<span class="br0">&#41;</span>.<span class="me1">click</span><span class="br0">&#40;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; self._process<span class="br0">&#40;</span>current_substate<span class="sy0">,</span> enums<span class="sy0">,</span> processors<span class="sy0">,</span> inserts<span class="br0">&#91;</span>index<span class="br0">&#93;</span><span class="sy0">,</span> request<span class="br0">&#41;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span> <span class="kw1">else</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; request.<span class="me1">jQuery</span><span class="br0">&#40;</span><span class="st0">&quot;#substate_tab_&quot;</span><span class="sy0">+</span>substate<span class="sy0">+</span><span class="st0">&quot;, .substate_tab_&quot;</span><span class="sy0">+</span>substate<span class="br0">&#41;</span>.<span class="me1">click</span><span class="br0">&#40;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; self._proceed<span class="br0">&#40;</span>inserts<span class="br0">&#91;</span>index<span class="br0">&#93;</span><span class="sy0">,</span> request<span class="br0">&#41;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><span class="sy0">,</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; _process<span class="sy0">:</span> <span class="kw2">function</span><span class="br0">&#40;</span>current_substate<span class="sy0">,</span> enums<span class="sy0">,</span> processors<span class="sy0">,</span> event<span class="sy0">,</span> request<span class="br0">&#41;</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">var</span> self <span class="sy0">=</span> <span class="kw1">this</span><span class="sy0">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> <span class="kw2">function</span><span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; _iterate<span class="br0">&#40;</span>enums<span class="sy0">,</span> <span class="kw2">function</span><span class="br0">&#40;</span>key<span class="sy0">,</span> substate<span class="sy0">,</span> index<span class="br0">&#41;</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>substate <span class="sy0">==</span> current_substate<span class="br0">&#41;</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">var</span> success <span class="sy0">=</span> self<span class="br0">&#91;</span>processors<span class="br0">&#91;</span>index<span class="br0">&#93;</span><span class="br0">&#93;</span><span class="br0">&#40;</span>request<span class="sy0">,</span> event<span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>success<span class="br0">&#41;</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; self<span class="br0">&#91;</span>event<span class="br0">&#93;</span><span class="br0">&#40;</span>request<span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><span class="sy0">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><span class="sy0">,</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">/// necessary because when did direct closure</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">/// (function(event) { return event; })(self[event])</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// .click(</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// &nbsp; &nbsp; (function(event) { return event; })(self[event])</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// )</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">/// called functions had incorrect this</span>
&nbsp; &nbsp; &nbsp; &nbsp; _proceed<span class="sy0">:</span> <span class="kw2">function</span><span class="br0">&#40;</span>event<span class="sy0">,</span> request<span class="br0">&#41;</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">var</span> self <span class="sy0">=</span> <span class="kw1">this</span><span class="sy0">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> <span class="kw2">function</span><span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">//self[fnname].apply(self, args);</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; self<span class="br0">&#91;</span>event<span class="br0">&#93;</span><span class="br0">&#40;</span>request<span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><span class="sy0">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><span class="sy0">,</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="coMULTI">/*
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;* @param: images: may be undefined
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;* @return: dictionary of:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;* &nbsp; &nbsp; &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; menu_items: list of (id, klasses, value) tuples for substate menu
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;* &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;id is &quot;substate_tab_&quot;+enums[index]
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;* &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;value is tab_names[index]
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;* &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;klasses is [&quot;substate_tab&quot;, &quot;current_tab&quot;]&lt;---if current tab
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;* &nbsp; &nbsp; &nbsp;next: tuple of next tab or null
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;* &nbsp; &nbsp; &nbsp;prev: tuple of prev tab or null
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;*/</span>
&nbsp; &nbsp; &nbsp; &nbsp; make_substate_menu_items<span class="sy0">:</span> <span class="kw2">function</span><span class="br0">&#40;</span>current_substate<span class="sy0">,</span> enums<span class="sy0">,</span> tab_names<span class="sy0">,</span> images<span class="br0">&#41;</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">var</span> menu_items <span class="sy0">=</span> <span class="br0">&#91;</span><span class="br0">&#93;</span><span class="sy0">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">var</span> prev <span class="sy0">=</span> <span class="kw2">null</span><span class="sy0">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">var</span> next <span class="sy0">=</span> <span class="kw2">null</span><span class="sy0">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">var</span> _last <span class="sy0">=</span> <span class="kw2">null</span><span class="sy0">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">var</span> _one_past_current <span class="sy0">=</span> <span class="kw2">false</span><span class="sy0">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">var</span> _two_past_current <span class="sy0">=</span> <span class="kw2">false</span><span class="sy0">;</span> <span class="co1">// because can't tell the difference bw nulls</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; _iterate<span class="br0">&#40;</span>tab_names<span class="sy0">,</span> <span class="kw2">function</span><span class="br0">&#40;</span>key<span class="sy0">,</span> value<span class="sy0">,</span> index<span class="br0">&#41;</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// figure out menu item</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">var</span> klasses <span class="sy0">=</span> <span class="br0">&#91;</span><span class="st0">&quot;substate_tab&quot;</span><span class="br0">&#93;</span><span class="sy0">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>enums<span class="br0">&#91;</span>index<span class="br0">&#93;</span> <span class="sy0">==</span> current_substate<span class="br0">&#41;</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; klasses.<span class="me1">push</span><span class="br0">&#40;</span><span class="st0">&quot;current_tab&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span> <span class="kw1">else</span> <span class="kw1">if</span> <span class="br0">&#40;</span><span class="sy0">!</span>_one_past_current<span class="br0">&#41;</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; klasses.<span class="me1">push</span><span class="br0">&#40;</span><span class="st0">&quot;past&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span> <span class="kw1">else</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; klasses.<span class="me1">push</span><span class="br0">&#40;</span><span class="st0">&quot;future&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">var</span> img <span class="sy0">=</span> <span class="st0">&quot;&quot;</span><span class="sy0">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">var</span> bar <span class="sy0">=</span> <span class="st0">&quot;&quot;</span><span class="sy0">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>enums<span class="br0">&#91;</span>index<span class="br0">&#93;</span> <span class="sy0">==</span> current_substate<span class="br0">&#41;</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>images<span class="br0">&#41;</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>images<span class="br0">&#91;</span>index<span class="br0">&#93;</span>.<span class="me1">selected</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; img <span class="sy0">=</span> constants.<span class="me1">MEDIA_URL</span><span class="sy0">+</span><span class="st0">&quot;img/&quot;</span><span class="sy0">+</span>images<span class="br0">&#91;</span>index<span class="br0">&#93;</span>.<span class="me1">selected</span><span class="sy0">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span> <span class="kw1">else</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; img <span class="sy0">=</span> constants.<span class="me1">MEDIA_URL</span><span class="sy0">+</span><span class="st0">&quot;img/&quot;</span><span class="sy0">+</span>images<span class="br0">&#91;</span>index<span class="br0">&#93;</span>.<span class="me1">past</span><span class="sy0">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span> <span class="kw1">else</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; img <span class="sy0">=</span> constants.<span class="me1">MEDIA_URL</span><span class="sy0">+</span><span class="st0">&quot;img/StepCircle&quot;</span><span class="sy0">+</span><span class="br0">&#40;</span>index<span class="sy0">+</span><span class="nu0">1</span><span class="br0">&#41;</span><span class="sy0">+</span><span class="st0">&quot;Done.png&quot;</span><span class="sy0">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bar <span class="sy0">=</span> constants.<span class="me1">MEDIA_URL</span><span class="sy0">+</span><span class="st0">&quot;img/Dash.png&quot;</span><span class="sy0">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span> <span class="kw1">else</span> <span class="kw1">if</span> <span class="br0">&#40;</span><span class="sy0">!</span>_one_past_current<span class="br0">&#41;</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>images<span class="br0">&#41;</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; img <span class="sy0">=</span> constants.<span class="me1">MEDIA_URL</span><span class="sy0">+</span><span class="st0">&quot;img/&quot;</span><span class="sy0">+</span>images<span class="br0">&#91;</span>index<span class="br0">&#93;</span>.<span class="me1">past</span><span class="sy0">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span> <span class="kw1">else</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; img <span class="sy0">=</span> constants.<span class="me1">MEDIA_URL</span><span class="sy0">+</span><span class="st0">&quot;img/StepCircle&quot;</span><span class="sy0">+</span><span class="br0">&#40;</span>index<span class="sy0">+</span><span class="nu0">1</span><span class="br0">&#41;</span><span class="sy0">+</span><span class="st0">&quot;Done.png&quot;</span><span class="sy0">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bar <span class="sy0">=</span> constants.<span class="me1">MEDIA_URL</span><span class="sy0">+</span><span class="st0">&quot;img/Dash.png&quot;</span><span class="sy0">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span> <span class="kw1">else</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>images<span class="br0">&#41;</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>images<span class="br0">&#91;</span>index<span class="br0">&#93;</span>.<span class="me1">future</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; img <span class="sy0">=</span> constants.<span class="me1">MEDIA_URL</span><span class="sy0">+</span><span class="st0">&quot;img/&quot;</span><span class="sy0">+</span>images<span class="br0">&#91;</span>index<span class="br0">&#93;</span>.<span class="me1">future</span><span class="sy0">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span> <span class="kw1">else</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; img <span class="sy0">=</span> constants.<span class="me1">MEDIA_URL</span><span class="sy0">+</span><span class="st0">&quot;img/&quot;</span><span class="sy0">+</span>images<span class="br0">&#91;</span>index<span class="br0">&#93;</span>.<span class="me1">past</span><span class="sy0">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span> <span class="kw1">else</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; img <span class="sy0">=</span> constants.<span class="me1">MEDIA_URL</span><span class="sy0">+</span><span class="st0">&quot;img/StepCircle&quot;</span><span class="sy0">+</span><span class="br0">&#40;</span>index<span class="sy0">+</span><span class="nu0">1</span><span class="br0">&#41;</span><span class="sy0">+</span><span class="st0">&quot;.png&quot;</span><span class="sy0">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bar <span class="sy0">=</span> constants.<span class="me1">MEDIA_URL</span><span class="sy0">+</span><span class="st0">&quot;img/DashGreen.png&quot;</span><span class="sy0">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">var</span> menu_item <span class="sy0">=</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; id<span class="sy0">:</span> <span class="st0">&quot;substate_tab_&quot;</span><span class="sy0">+</span>enums<span class="br0">&#91;</span>index<span class="br0">&#93;</span><span class="sy0">,</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; klasses<span class="sy0">:</span> klasses<span class="sy0">,</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; value<span class="sy0">:</span> value<span class="sy0">,</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; img<span class="sy0">:</span> img<span class="sy0">,</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bar<span class="sy0">:</span> bar
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// set next</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>_one_past_current <span class="sy0">&amp;&amp;</span> <span class="sy0">!</span>_two_past_current<span class="br0">&#41;</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; next <span class="sy0">=</span> menu_item<span class="sy0">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; _two_past_current <span class="sy0">=</span> <span class="kw2">true</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// set prev</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>enums<span class="br0">&#91;</span>index<span class="br0">&#93;</span> <span class="sy0">==</span> current_substate<span class="br0">&#41;</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; _one_past_current <span class="sy0">=</span> <span class="kw2">true</span><span class="sy0">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; prev <span class="sy0">=</span> _last<span class="sy0">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// add to menu items</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>value <span class="sy0">!=</span> <span class="st0">&quot;XXX&quot;</span> <span class="sy0">&amp;&amp;</span> value <span class="sy0">!=</span> <span class="st0">&quot;XXXX&quot;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; menu_items.<span class="me1">push</span><span class="br0">&#40;</span>menu_item<span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; _last <span class="sy0">=</span> menu_item<span class="sy0">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; menu_items<span class="sy0">:</span> menu_items<span class="sy0">,</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; next<span class="sy0">:</span> next<span class="sy0">,</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; prev<span class="sy0">:</span> prev
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><span class="sy0">,</span></div>
</div>
</pre>
]]></content:encoded>
			<wfw:commentRss>http://proudly.procrasdonate.com/super-submenu-abstraction/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Testing Sequentializer</title>
		<link>http://proudly.procrasdonate.com/testing-sequentializer/</link>
		<comments>http://proudly.procrasdonate.com/testing-sequentializer/#comments</comments>
		<pubDate>Fri, 15 Jan 2010 19:07:29 +0000</pubDate>
		<dc:creator>Lucy</dc:creator>
				<category><![CDATA[technical]]></category>
		<category><![CDATA[testing]]></category>

		<guid isPermaLink="false">http://proudlyprocrasdonating.wordpress.com/?p=402</guid>
		<description><![CDATA[I&#8217;d like to share with you a neat function we wrote to sequentialize testing tasks. 
Most of our tests are not concerned with timing, but we do have a few dozen time tracking scenarios. Each scenario re-enacts a user taking particular actions for particular durations. In addition to viewing webpages, we throw in application switches, [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;d like to share with you a neat function we wrote to sequentialize testing tasks. </p>
<p>Most of our tests are not concerned with timing, but we do have a few dozen time tracking scenarios. Each scenario re-enacts a user taking particular actions for particular durations. In addition to viewing webpages, we throw in application switches, the computer going to sleep, and other <a href="http://proudly.procrasdonate.com/time-tracker-sleuthing/">time tracking</a> influences. </p>
<p>We&#8217;re particularly interested in how different time tracking triggers interact with each other, especially since some of the notifications can be delayed up to 5 seconds.</p>
<p>An important requirement is that we be able to write a test that plainly lists the actions and durations to take; such as the following:</p>
<ul>
<li>Start viewing a web page; say, http://foo.com/bar</li>
<li>30 seconds later notify the &#8220;user is idle&#8221; observer</li>
<li>40 seconds later notify the &#8220;user is back&#8221; observer&#8221;</li>
<li>10 seconds later start viewing a new web page</li>
</ul>
<p>We expect to nicely abstract the test framework stuff that checks the actual behavior against the expected behavior (in this case a 30 seconds visit followed by a 10 second visit to web page http://foo.com/bar).</p>
<h3>Algorithm Overview</h3>
<p>The Javascript tool at our disposal is
<div class="codesnip-container" >setTimeout</div>
<p>, which waits for a given duration before executing a given expression.</p>
<p>Thus, the core of our sequentializer is turning a list of expressions and durations into a setTimeout expression that itself calls setTimeout with an expression that calls setTimeout&#8230; The recursive nature of the algorithm is best viewed through pseudocode:</p>
<pre>
<div class="codesnip-container" >
<div class="javascript codesnip" style="font-family:monospace;"># input items<span class="sy0">:</span> list of <span class="br0">&#40;</span>action<span class="sy0">,</span> duration<span class="br0">&#41;</span> pairs
# <span class="br0">&#91;</span> <span class="br0">&#123;</span>action<span class="sy0">:</span> some <span class="kw2">function</span><span class="sy0">,</span> duration<span class="sy0">:</span> seconds<span class="br0">&#125;</span><span class="sy0">,</span> 
# &nbsp; &nbsp; ...<span class="sy0">,</span>
# &nbsp; &nbsp;<span class="br0">&#123;</span>action<span class="sy0">:</span> some <span class="kw2">function</span><span class="br0">&#125;</span>
# &nbsp;<span class="br0">&#93;</span>
# goal <span class="kw1">is</span> to execute items<span class="br0">&#91;</span>idx<span class="br0">&#93;</span>.<span class="me1">action</span><span class="sy0">,</span> wait items<span class="br0">&#91;</span>idx<span class="br0">&#93;</span>.<span class="me1">duration</span><span class="sy0">,</span>
# and then execute items<span class="br0">&#91;</span>idx<span class="sy0">+</span>1<span class="br0">&#93;</span>.<span class="me1">action</span><span class="sy0">,</span> wait items<span class="br0">&#91;</span>idx<span class="sy0">+</span>1<span class="br0">&#93;</span>.<span class="me1">duration</span><span class="sy0">,</span>
# and then execute items<span class="br0">&#91;</span>idx<span class="sy0">+</span>2<span class="br0">&#93;</span>.<span class="me1">action</span><span class="sy0">,</span> .... <span class="me1">until</span> all actions
# are executed. 
# The duration field<span class="sy0">,</span> <span class="kw1">if</span> present<span class="sy0">,</span> <span class="kw1">in</span> the last dictionary <span class="kw1">in</span> items
# <span class="kw1">is</span> ignored.
#
# input idx<span class="sy0">:</span> index into items of next pair to execute
sequentialize<span class="br0">&#40;</span> items<span class="sy0">,</span> idx <span class="br0">&#41;</span>
&nbsp; &nbsp; &nbsp; &nbsp; # <span class="kw1">if</span> there <span class="kw1">is</span> an action to execute<span class="sy0">,</span> execute it
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> idx <span class="sy0">&lt;</span> items.<span class="me1">length</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; execute<span class="br0">&#40;</span> items<span class="br0">&#91;</span>idx<span class="br0">&#93;</span>.<span class="me1">action</span> <span class="br0">&#41;</span>
&nbsp; &nbsp; &nbsp; &nbsp; fi

&nbsp; &nbsp; &nbsp; &nbsp; # <span class="kw1">if</span> there are more actions to execute<span class="sy0">,</span> recurse
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> idx <span class="sy0">+</span> <span class="nu0">1</span> <span class="sy0">&lt;</span> items.<span class="me1">length</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; setTimeout<span class="br0">&#40;</span> sequentialize<span class="br0">&#40;</span>items<span class="sy0">,</span> idx<span class="sy0">+</span><span class="nu0">1</span><span class="br0">&#41;</span><span class="sy0">,</span> items<span class="br0">&#91;</span>idx<span class="br0">&#93;</span>.<span class="me1">duration</span> <span class="br0">&#41;</span>
&nbsp; &nbsp; &nbsp; &nbsp; fi</div>
</div>
</pre>
<h3>Javascript Solution</h3>
<p>When we converted this pseudocode to a Javascript sequentializer function, we added a _create_sequentializer helper method to do testing stuff so that each test case need only specify the items data structure.</p>
<p>Here&#8217;s the full Javascript:</p>
<pre>
<div class="codesnip-container" >
<div class="javascript codesnip" style="font-family:monospace;"><span class="coMULTI">/**
&nbsp;* @param items: list of functions to execute.
&nbsp;* &nbsp; &nbsp; &nbsp;has the following form:
&nbsp;* [
&nbsp;* &nbsp;{fn, self, args, interval},
&nbsp;* &nbsp;{fn, self, args, interval},
&nbsp;* &nbsp;...
&nbsp;* ]
&nbsp;*/</span>
sequentialize<span class="sy0">:</span> <span class="kw2">function</span><span class="br0">&#40;</span>items<span class="sy0">,</span> idx<span class="br0">&#41;</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">var</span> self <span class="sy0">=</span> <span class="kw1">this</span><span class="sy0">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>idx <span class="sy0">&lt;</span> items.<span class="me1">length</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; items<span class="br0">&#91;</span>idx<span class="br0">&#93;</span>.<span class="me1">fn</span>.<span class="me1">apply</span><span class="br0">&#40;</span>items<span class="br0">&#91;</span>idx<span class="br0">&#93;</span>.<span class="me1">self</span><span class="sy0">,</span> items<span class="br0">&#91;</span>idx<span class="br0">&#93;</span>.<span class="me1">args</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>items<span class="br0">&#91;</span>idx<span class="br0">&#93;</span>.<span class="me1">interval</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; setTimeout<span class="br0">&#40;</span><span class="kw2">function</span><span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; self.<span class="me1">sequentialize</span><span class="br0">&#40;</span>items<span class="sy0">,</span> idx<span class="sy0">+</span>1<span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><span class="sy0">,</span> items<span class="br0">&#91;</span>idx<span class="br0">&#93;</span>.<span class="me1">interval</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span> <span class="kw1">else</span> <span class="kw1">if</span> <span class="br0">&#40;</span>idx<span class="sy0">+</span><span class="nu0">1</span> <span class="sy0">&lt;</span> items.<span class="me1">length</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// flexible: in case we have actions we don't want to wait on</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; self.<span class="me1">sequentialize</span><span class="br0">&#40;</span>items<span class="sy0">,</span> idx<span class="sy0">+</span>1<span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span>
<span class="br0">&#125;</span><span class="sy0">,</span>

<span class="coMULTI">/**
&nbsp;* Test framework stuff
&nbsp;*/</span>
_create_sequence<span class="sy0">:</span> <span class="kw2">function</span><span class="br0">&#40;</span>testrunner<span class="sy0">,</span> display_results_callback<span class="sy0">,</span> site<span class="sy0">,</span> actions<span class="br0">&#41;</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">var</span> self <span class="sy0">=</span> <span class="kw1">this</span><span class="sy0">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">var</span> expected_durations <span class="sy0">=</span> <span class="br0">&#91;</span><span class="br0">&#93;</span><span class="sy0">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">var</span> sequence <span class="sy0">=</span> <span class="br0">&#91;</span><span class="br0">&#93;</span><span class="sy0">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; _iterate<span class="br0">&#40;</span>actions<span class="sy0">,</span> <span class="kw2">function</span><span class="br0">&#40;</span>key<span class="sy0">,</span> action<span class="sy0">,</span> index<span class="br0">&#41;</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>action.<span class="me1">expected_visit</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; expected_durations.<span class="me1">push</span><span class="br0">&#40;</span>Math.<span class="me1">round</span><span class="br0">&#40;</span>action.<span class="me1">interval</span><span class="sy0">/</span>1000.0<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; sequence.<span class="me1">push</span><span class="br0">&#40;</span><span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fn<span class="sy0">:</span> action.<span class="me1">fn</span><span class="sy0">,</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; self<span class="sy0">:</span> action.<span class="me1">self</span><span class="sy0">,</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; args<span class="sy0">:</span> action.<span class="me1">args</span><span class="sy0">,</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; interval<span class="sy0">:</span> action.<span class="me1">interval</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// append tests to sequence</span>
&nbsp; &nbsp; &nbsp; &nbsp; sequence.<span class="me1">push</span><span class="br0">&#40;</span><span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fn<span class="sy0">:</span> self.<span class="me1">check_visits</span><span class="sy0">,</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; self<span class="sy0">:</span> self<span class="sy0">,</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; args<span class="sy0">:</span> <span class="br0">&#91;</span>testrunner<span class="sy0">,</span> display_results_callback<span class="sy0">,</span> site<span class="sy0">,</span> expected_durations<span class="br0">&#93;</span><span class="sy0">,</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; interval<span class="sy0">:</span> <span class="nu0">1</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><span class="br0">&#41;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// initiate sequence execution</span>
&nbsp; &nbsp; &nbsp; &nbsp; self.<span class="me1">sequentialize</span><span class="br0">&#40;</span>sequence<span class="sy0">,</span> 0<span class="br0">&#41;</span><span class="sy0">;</span>
<span class="br0">&#125;</span><span class="sy0">,</span>

<span class="co1">// here is the above example test case in code</span>
<span class="co1">// it reenacts a user initiating a view of a webpage, going idle,</span>
<span class="co1">// coming back, and then ending that page view</span>
self._create_sequence<span class="br0">&#40;</span>
&nbsp; &nbsp; &nbsp; &nbsp; testrunner<span class="sy0">,</span>
&nbsp; &nbsp; &nbsp; &nbsp; display_results_callback<span class="sy0">,</span>
&nbsp; &nbsp; &nbsp; &nbsp; site<span class="sy0">,</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#91;</span><span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fn<span class="sy0">:</span> self.<span class="me1">time_tracker</span>.<span class="me1">start_recording</span><span class="sy0">,</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; self<span class="sy0">:</span> self.<span class="me1">time_tracker</span><span class="sy0">,</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; args<span class="sy0">:</span> <span class="br0">&#91;</span>site.<span class="me1">url</span><span class="br0">&#93;</span><span class="sy0">,</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; interval<span class="sy0">:</span> Math.<span class="me1">floor</span><span class="br0">&#40;</span>Math.<span class="me1">random</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">*</span>7000<span class="br0">&#41;</span><span class="sy0">+</span>2000<span class="sy0">,</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; expected_visit<span class="sy0">:</span> <span class="kw2">true</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><span class="sy0">,</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fn<span class="sy0">:</span> self.<span class="me1">idle_back_listener</span>.<span class="me1">idle</span><span class="sy0">,</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; self<span class="sy0">:</span> self.<span class="me1">idle_back_listener</span><span class="sy0">,</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; args<span class="sy0">:</span> <span class="br0">&#91;</span><span class="br0">&#93;</span><span class="sy0">,</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; interval<span class="sy0">:</span> Math.<span class="me1">floor</span><span class="br0">&#40;</span>Math.<span class="me1">random</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">*</span>7000<span class="br0">&#41;</span><span class="sy0">+</span>2000
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><span class="sy0">,</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fn<span class="sy0">:</span> self.<span class="me1">idle_back_listener</span>.<span class="kw3">back</span><span class="sy0">,</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; self<span class="sy0">:</span> self.<span class="me1">idle_back_listener</span><span class="sy0">,</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; args<span class="sy0">:</span> <span class="br0">&#91;</span><span class="br0">&#93;</span><span class="sy0">,</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; interval<span class="sy0">:</span> Math.<span class="me1">floor</span><span class="br0">&#40;</span>Math.<span class="me1">random</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">*</span>7000<span class="br0">&#41;</span><span class="sy0">+</span>2000<span class="sy0">,</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; expected_visit<span class="sy0">:</span> <span class="kw2">true</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><span class="sy0">,</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fn<span class="sy0">:</span> self.<span class="me1">time_tracker</span>.<span class="me1">stop_recording</span><span class="sy0">,</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; self<span class="sy0">:</span> self.<span class="me1">time_tracker</span><span class="sy0">,</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; args<span class="sy0">:</span> <span class="br0">&#91;</span><span class="br0">&#93;</span><span class="sy0">,</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; interval<span class="sy0">:</span> 0
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><span class="br0">&#93;</span>
<span class="br0">&#41;</span><span class="sy0">;</span></div>
</div>
</pre>
<p>Proudly ProcrasDonating,<br />
Lucy.</p>
]]></content:encoded>
			<wfw:commentRss>http://proudly.procrasdonate.com/testing-sequentializer/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Time Tracking Information Space</title>
		<link>http://proudly.procrasdonate.com/time-tracking-information-space/</link>
		<comments>http://proudly.procrasdonate.com/time-tracking-information-space/#comments</comments>
		<pubDate>Mon, 14 Dec 2009 04:25:43 +0000</pubDate>
		<dc:creator>Lucy</dc:creator>
				<category><![CDATA[conceptual]]></category>

		<guid isPermaLink="false">http://proudlyprocrasdonating.wordpress.com/?p=462</guid>
		<description><![CDATA[One form of brainstorming that I particularly enjoy is mapping out information spaces. For example,
What are all the visualizations and analyses we could do for tracking time?
This already has one of my favorite features: enumeration. I&#8217;m a natural judger: I perceive more than I observe; I think from my gut and heart. Despite this, or [...]]]></description>
			<content:encoded><![CDATA[<p>One form of brainstorming that I particularly enjoy is mapping out information spaces. For example,</p>
<blockquote><p>What are all the visualizations and analyses we could do for tracking time?</p></blockquote>
<p>This already has one of my favorite features: <b>enumeration</b>. I&#8217;m a natural judger: I perceive more than I observe; I think from my gut and heart. Despite this, or perhaps because of it, I love building out the space before narrowing it down.</p>
<p>Mapping out a space can be especially useful after an initial creative brainstorm. It forces one to consider the corners and holes one didn&#8217;t know existed.</p>
<p>Breaking down ideas also makes them more precise. It helps us understand the salient points, even if the direction of our decision is already made.</p>
<p>Below are the <em>7 types of time tracking information</em> I&#8217;ve brainstormed so far.</p>
<h3>1. <em>Timeline</em>, chronology, notable events</h3>
<p><a href="/wp-content/uploads/sketches/timeline.jpg"><img src="/wp-content/uploads/sketches/timeline.jpg?w=300" alt="" title="timeline" width="250" height="110" class="aligncenter size-medium wp-image-475" /></a></p>
<p>A timeline or chronology shows events over time. At most basic it is a list, such as the <b>Visits</b> tab on ProcrasDonate&#8217;s <b>My Progress</b> page.</p>
<blockquote><p>started <strong>viewing new url</strong><br />
ended by <strong>application switching away from Firefox</strong><br />
<em>2 secs</em><br />
<a href="https://ProcrasDonate.com/my_progress/">https://ProcrasDonate.com/my_progress/</a><br />
<i>December 13, 2009, 11:52:20 </i></p></blockquote>
<p>It could also be an actual timeline, where the spacing of events is based on the time the events occured. The events themselves can be marked using icons of different shape, size and color to indicate different things.</p>
<p>Timelines can be useful for seeing correlations between events. For example, it might be interesting to mark the end of each week with whether one&#8217;s goals were met, as well as when one visited the My Progress page, or whether the computer was asleep before midnight. Does ProcrasDonation lessen after payments are made?</p>
<h3>2. <em>Ranking</em>, ordered list</h3>
<p><a href="/wp-content/uploads/sketches/rank.jpg"><img src="/wp-content/uploads/sketches/rank.jpg" alt="" title="rank" width="270" height="135" class="aligncenter size-full wp-image-491" /></a></p>
<p>Rankings list items according to some metric, such as number of hours spent on websites.</p>
<p>Rankings can show the hotspot websites, unless the sites at the top of the list are already optimized&#8212;I <b>can&#8217;t</b> use email any less!&#8212;and it is the sites somewhere in the middle that take up some amount of unnecessary time&#8212;maybe I can watch a TV show instead of a movie.</p>
<p>Rankings can also be useful as bookmarks, especially if more recent behavior is valued higher than behavior deep in the past. Hotspots are hot for a reason.</p>
<h3>3. <em>Amplitude</em>, breakdown over time</h3>
<p><a href="/wp-content/uploads/sketches/amplitude_line.jpg"><img src="/wp-content/uploads/sketches/amplitude_line.jpg?w=300" alt="" title="amplitude_line" width="250" height="129" class="aligncenter size-medium wp-image-481" /></a></p>
<p>Amplitude charts are the canonical time tracking visualization. I made these kinds of graphs first for my own <a href="http://proudlyprocrasdonating.wordpress.com/2009/12/05/life-tracking/">life tracking</a>. How much time did I engage in certain activities over a span of time.</p>
<p>This is the simplest was to view a trend over time. It&#8217;s like viewing a chronology of quantitatively comparable events.</p>
<p><a href="/wp-content/uploads/sketches/amplitude_stacked.jpg"><img src="/wp-content/uploads/sketches/amplitude_stacked.jpg?w=300" alt="" title="amplitude_stacked" width="250" height="128" class="aligncenter size-medium wp-image-482" /></a></p>
<p>Amplitude charts are great ways to view a break down of activities. In addition to overlapping line charts or side-by-side bar charts, one can also stack the lines or bars.</p>
<p>The <em>trend</em> tab on the <em>My Progress</em> page is a good start, but I&#8217;d love to see my ProcrasDonate curve broken down by the top 5 ProcrasDonate sites I visit. Then I could see what sites contribute to the different humps.</p>
<p><a href="/wp-content/uploads/sketches/amplitude_bars.jpg"><img src="/wp-content/uploads/sketches/amplitude_bars.jpg?w=300" alt="" title="amplitude_bars" width="250" height="120" class="aligncenter size-medium wp-image-480" /></a></p>
<p>I will say that I&#8217;ve already found the trends <a href="http://proudly.procrasdonate.com/magical-graphicals/">informative</a>. It&#8217;s clear that every other day I watch a couple TV shows. I avoid hackernews and email most days, and then splurge (note that some of the big humps are from not stopping the time tracker when my laptop goes to sleep for the night; this has been <a href="http://proudlyprocrasdonating.wordpress.com/2009/12/05/extension-listeners/">fixed</a>!).</p>
<h3>4. <em>Ratio</em>, comparison, balance</h3>
<p><a href="/wp-content/uploads/sketches/ratio_line.jpg"><img src="/wp-content/uploads/sketches/ratio_line.jpg?w=300" alt="" title="ratio_line" width="250" height="89" class="aligncenter size-medium wp-image-476" /></a></p>
<p>I initially overlooked ratios, but after Clay introduced us I see that they&#8217;re pretty cool.</p>
<p>Ratios show how much you do one thing versus another. For example, maybe you want to be producing 5 times as often as you are chilling.</p>
<p><a href="/wp-content/uploads/sketches/ratio_pie.jpg"><img src="/wp-content/uploads/sketches/ratio_pie.jpg?w=254" alt="" title="ratio_pie" width="122" height="150" class="aligncenter size-medium wp-image-477" /></a></p>
<p>Having another perspective on one&#8217;s time could be more useful than amplitude alone.  People who use the <a href="http://www.pomodorotechnique.com/">Pomodoro</a> method, which uses two timers to segregate productive time from chilling time, would find this especially useful.</p>
<p><a href="/wp-content/uploads/sketches/ratio_sped.jpg"><img src="/wp-content/uploads/sketches/ratio_sped.jpg?w=300" alt="" title="ratio_sped" width="125" height="109" class="aligncenter size-medium wp-image-478" /></a></p>
<p>I envision a simple, sparkline-esque monitor of one&#8217;s ProcrasDonate v TimeWellSpent ratio, perhaps red or green based on whether that day was above or below the ratio goal. Or we could give up the over time view and go with a dynamic pie chart, akin to the progress meters and gauges we have currently.</p>
<h3>5. <em>Accumulation</em>, burn down/up, progress</h3>
<p><a href="/wp-content/uploads/sketches/accumulative.jpg"><img src="/wp-content/uploads/sketches/accumulative.jpg" alt="" title="accumulative" width="270" height="168" class="aligncenter size-full wp-image-486" /></a></p>
<p>Accumulation shows ones progress towards a particular total. This is my favorite motivational chart, a la the release story burn down. Perhaps more than the other information types listed so far, this one is conducive to understanding the present in relation to the past and future.</p>
<p>When Pivotal Tracker shows <a href="http://proudly.procrasdonate.com/0-3-0-released-on-time/">my current burn down</a> compared to the linear burn down, I can see whether I&#8217;m on track or not <em>right now</em>.</p>
<p><a href="/wp-content/uploads/sketches/accumulative2.jpg"><img src="/wp-content/uploads/sketches/accumulative2.jpg" alt="" title="accumulative2" width="270" height="124" class="aligncenter size-full wp-image-487" /></a></p>
<p>Even better is when Pivotal Tracker estimates when I&#8217;ll be done. The first iteration it was quite pessimistic about my abilities. Even though I was below the linear progress line, I felt spurred on because nuts if I was going to sit still with my expected completion date 2 weeks later than the deadline!</p>
<p>With ProcrasDonate it would be neat to actually set the goal line. I think a burn up makes more sense, so instead of a goal line that starts at 0 hours Monday morning and rises to 12 hours Sunday night, what about a fairly flat goal line that rises sharply on the weekend? What about requiring a near flat line during the mornings? or after 10pm?</p>
<p>Maybe that can even influence alerts and preventative actions by ProcrasDonate.</p>
<p>I&#8217;d like to return a moment to my battle with Pivotal Tracker&#8217;s expected burn down line. That little guy was full of character. It egged me on in the beginning, and seemed to pat me on the back later. Our relationship is more distant now, but were it an actual cartoon character I could relate to and who responded to my behavior, perhaps I would still find it motivational.</p>
<p>Clay is especially looking forward to exploring this kind of user interaction. I have to say I am, too.</p>
<h3>6. <em>Average</em>, compressed trend, period</h3>
<p><a href="/wp-content/uploads/sketches/average_dots.jpg"><img src="/wp-content/uploads/sketches/average_dots.jpg" alt="" title="average_dots" width="270" height="164" class="aligncenter size-full wp-image-488" /></a></p>
<p>Averages compress trends over time to a canonical view of some period. Averages are a meta chart, since they can average any of the information types mentioned so far.</p>
<p>This is pretty exciting because after a few weeks, and certainly after months and years, the chronological trends start needing aggregation, and even then there is just too much space to look at. Maybe you see a recurring pattern to your productivity and procrastination, but how do you know if you are doing better this week without making a huge change?</p>
<p>Averages gives you a number that you can strive against. For example, a chart might show you the average number of minutes you ProcrasDonated during any hour of the week. You might see the days of the week on the yaxis and the hours of the day on the xaxis. Perhaps you can mark regions of the chart to indicate goals and even preventive measures, as mentioned in the accumulative type above.</p>
<p>Maybe the points in the chart use size or color to indicate their value, or maybe the points are themselves pie charts indicating the ratio of ProcrasDonation to TimeWellSpent. Perhaps their value, instead of hours spent ProcrasDonating, is simply ProcrasDonation or TimeWellSpent, whichever occurs more often during that hour.</p>
<p>The averages charts can also average amplitude trends over time, and then use arrows to show the current derivatives.</p>
<p><a href="/wp-content/uploads/sketches/average_line.jpg"><img src="/wp-content/uploads/sketches/average_line.jpg" alt="" title="average_line" width="270" height="153" class="aligncenter size-full wp-image-489" /></a></p>
<p>Another neat averages chart is to see the day, as specified by &#8220;waking up&#8221; and &#8220;going to sleep&#8221;, with the most viewed website shown for each hour.</p>
<p><a href="/wp-content/uploads/sketches/average_sites.jpg"><img src="/wp-content/uploads/sketches/average_sites.jpg" alt="" title="average_sites" width="270" height="130" class="aligncenter size-full wp-image-490" /></a></p>
<p>As a ProcrasDonate developer, I&#8217;d be particularly interested in viewing averages where the period is defined by ProcrasDonate releases.</p>
<h3>7. <em>Combination</em>, overlays of above types</h3>
<p>Finally, we have combinations, which overlay charts on top of each other.</p>
<p>Without fully understanding the information space, it can be easy to start with a graph that seems useful, and then add features that overtime become quite muddled. Feature creep is what happens when you stop iterating on design.</p>
<p>There is a good chance we find something revolutionary in the combination type. Until we build more basic types and iterate them with user feedback, I don&#8217;t even feel ready to speculate.</p>
<p>Proudly ProcrasDonating,</p>
<p>Lucy.</p>
]]></content:encoded>
			<wfw:commentRss>http://proudly.procrasdonate.com/time-tracking-information-space/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Magical Graphicals</title>
		<link>http://proudly.procrasdonate.com/magical-graphicals/</link>
		<comments>http://proudly.procrasdonate.com/magical-graphicals/#comments</comments>
		<pubDate>Sun, 13 Dec 2009 17:24:57 +0000</pubDate>
		<dc:creator>Lucy</dc:creator>
				<category><![CDATA[release]]></category>

		<guid isPermaLink="false">http://proudlyprocrasdonating.wordpress.com/?p=432</guid>
		<description><![CDATA[After we released 0.3.4 four days late (my bad) I told Clay that meeting 0.3.5&#8217;s upcoming Friday release was important to me, even if that meant under-committing the next 3 days.
Clay said to view this short iteration as a celebration or break, a chance to recharge by doing whatever I wanted.
What I heard was
GRAPHS GRAPHS [...]]]></description>
			<content:encoded><![CDATA[<p>After we released 0.3.4 four days late (my bad) I told Clay that meeting 0.3.5&#8217;s upcoming Friday release was important to me, even if that meant under-committing the next 3 days.</p>
<p>Clay said to view this short iteration as a celebration or break, a chance to recharge by doing whatever I wanted.</p>
<p>What I heard was</p>
<blockquote><p>GRAPHS GRAPHS GRAPHS!</p></blockquote>
<div id="attachment_433" class="wp-caption aligncenter" style="width: 260px"><a href="/wp-content/uploads/tws_trend.png"><img class="size-medium wp-image-433" title="Screenshot: hours per day spent on Pivotal Tracker compared to all sites classified as Time Well Spen" src="/wp-content/uploads/tws_trend.png?w=300" alt="Screenshot: hours per day spent on Pivotal Tracker compared to all sites classified as Time Well Spen" width="250" height="217" /></a><p class="wp-caption-text">ProcrasDonate Screenshot: hours per day spent on Pivotal Tracker compared to all sites classified as Time Well Spent</p></div>
<p>I&#8217;ve been looking forward to graphs for a while. Graphs are part of a larger vision of actionable visualizations and analysis.</p>
<p>The first step was integrating existing Javascript graph libraries into our extension. As <a href="http://proudly.procrasdonate.com/xpcnativewrapper-location-must-include-absolute-url/">alluded to elsewhere</a>, and based on previous trouble integrating jQuery UI, I expected this to be troublesome.</p>
<p>In fact, the first night I solved non-graph problems instead, such as honoring Firefox&#8217;s<a href="https://procrasdonate.com/faq/#time">private browsing mode</a>. By Thursday I thought I was going to work on email newsletters or community page aggregations and return to graphs when I had more time.</p>
<p>I admit I was a little afraid. What if I made graphs that people laughed at? &#8220;These aren&#8217;t useful! Why do ProcrasDonate developers waste their time making these?&#8221; I <em>like</em> looking forward to graphs.</p>
<div id="attachment_434" class="wp-caption aligncenter" style="width: 260px"><a href="/wp-content/uploads/pd_trend.png"><img src="/wp-content/uploads/pd_trend.png?w=300" alt="ProcrasDonate Screenshot: Hours per day spent at Hacker News and Hulu" title="pd_trend" width="250" height="160" class="size-medium wp-image-434" /></a><p class="wp-caption-text">ProcrasDonate Screenshot: Hours per day spent at Hacker News and Hulu</p></div>
<p>Clay told me not to worry, and my unconscious mind must have heard, because that night I got graphs working. I woke up in the early morning and worked on them some more, took a nap, and kept going. Have track to roll on is such a pleasure.</p>
<p>It was already Friday, release day, and we were in good shape. We decided to push through the <em>Registration</em> page&#8217;s recipient percent pie chart and the <em>My Progress</em> page&#8217;s trend line chart. Screenshots of these are throughout this post.</p>
<div id="attachment_435" class="wp-caption aligncenter" style="width: 260px"><a href="/wp-content/uploads/pie_percents.png"><img src="/wp-content/uploads/pie_percents.png?w=290" alt="ProcrasDonate Screenshot: Percent of each donation received by selected charities. The full donation amount is determined by the user&#39;s $/hr rate times the number of hours ProcrasDonated, limited by the user&#39;s maximum weekly donation amount." title="pie_percents" width="250" height="255" class="size-medium wp-image-435" /></a><p class="wp-caption-text">ProcrasDonate Screenshot: Percent of each donation received by selected charities. The full donation amount is determined by the user's $/hr rate times the number of hours ProcrasDonated, limited by the user's maximum weekly donation amount.</p></div>
<p>We pushed to release Friday afternoon, but I ended up spending until Saturday morning getting all the user interaction elements up to snuff. Even if meeting deadlines is important, there is a minimum quality, including quality of process and decision making, that is even more important. Deadlines are a single factor in a larger equation.</p>
<p>I should also mention that our Friday deadlines are internal. Releasing for real Friday afternoon is still a good goal to work towards, but I do like the idea of using the weekend as a buffer if necessary, with releases out the door by Sunday night. Buffers as flex time, to be used for loose ends, documentation, blog posts, celebration or getting ahead on the next iteration, are wonderful.</p>
<p>We also like having challenging goals. It pushes us to accomplish more. Varying within the bounds is ok.</p>
<p>For those of you interested in the technical details, they are appended below.</p>
<p>Proudly ProcrasDonating,</p>
<p>Lucy.</p>
<h3>Pie Chart Technical Details</h3>
<p>The pie charts are created using <a href="http://g.raphaeljs.com">gRaphael</a> Javascript library.</p>
<p>In order to provide Raphael with the right <em>document</em>, I wrapped the library in a function with a single document parameter:</p>
<pre>
<div class="codesnip-container" >
<div class="javascript codesnip" style="font-family:monospace;"><span class="kw2">function</span> init_raphael<span class="br0">&#40;</span>document<span class="br0">&#41;</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#91;</span>Raphael library code<span class="br0">&#93;</span>
<span class="br0">&#125;</span></div>
</div>
</pre>
<p>I wrapped the gRaphael files in functions, too, since they rely on Raphael being initialized first.</p>
<p>I then called everything from my extension&#8217;s view like so:</p>
<pre>
<div class="codesnip-container" >
<div class="javascript codesnip" style="font-family:monospace;">init_raphael<span class="br0">&#40;</span>request.<span class="me1">get_document</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
init_graphael<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
init_graphael_pie<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>

<span class="co1">// create pie chart</span>
<span class="kw2">var</span> paper <span class="sy0">=</span> Raphael<span class="br0">&#40;</span><span class="st0">&quot;pie_chart&quot;</span><span class="sy0">,</span> 550<span class="sy0">,</span> 250<span class="br0">&#41;</span><span class="sy0">;</span>
<span class="kw2">var</span> pie <span class="sy0">=</span> paper.<span class="me1">g</span>.<span class="me1">piechart</span><span class="br0">&#40;</span><span class="nu0">125</span><span class="sy0">,</span> <span class="nu0">125</span><span class="sy0">,</span> <span class="nu0">100</span><span class="sy0">,</span>
&nbsp; &nbsp; data<span class="sy0">,</span>
&nbsp; &nbsp; <span class="br0">&#123;</span> legend<span class="sy0">:</span> legend<span class="sy0">,</span>
&nbsp; &nbsp; &nbsp; legendpos<span class="sy0">:</span> <span class="st0">&quot;east&quot;</span><span class="sy0">,</span>
&nbsp; &nbsp; &nbsp; colors<span class="sy0">:</span> colors <span class="br0">&#125;</span><span class="br0">&#41;</span><span class="sy0">;</span>

<span class="co1">// add cool effects (copied from g.raphael demo)</span>
pie.<span class="me1">hover</span><span class="br0">&#40;</span><span class="kw2">function</span> <span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; <span class="kw1">this</span>.<span class="me1">sector</span>.<span class="kw3">stop</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp; &nbsp; <span class="kw1">this</span>.<span class="me1">sector</span>.<span class="me1">scale</span><span class="br0">&#40;</span>1.1<span class="sy0">,</span> 1.1<span class="sy0">,</span> <span class="kw1">this</span>.<span class="me1">cx</span><span class="sy0">,</span> <span class="kw1">this</span>.<span class="me1">cy</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span><span class="kw1">this</span>.<span class="me1">label</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">this</span>.<span class="me1">label</span><span class="br0">&#91;</span>0<span class="br0">&#93;</span>.<span class="kw3">stop</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">this</span>.<span class="me1">label</span><span class="br0">&#91;</span>0<span class="br0">&#93;</span>.<span class="me1">scale</span><span class="br0">&#40;</span>1.5<span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">this</span>.<span class="me1">label</span><span class="br0">&#91;</span><span class="nu0">1</span><span class="br0">&#93;</span>.<span class="me1">attr</span><span class="br0">&#40;</span><span class="br0">&#123;</span><span class="st0">&quot;font-weight&quot;</span><span class="sy0">:</span> 800<span class="br0">&#125;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp; &nbsp; <span class="br0">&#125;</span>
<span class="br0">&#125;</span><span class="sy0">,</span> <span class="kw2">function</span> <span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">this</span>.<span class="me1">sector</span>.<span class="me1">animate</span><span class="br0">&#40;</span><span class="br0">&#123;</span>scale<span class="sy0">:</span> <span class="br0">&#91;</span>1<span class="sy0">,</span> 1<span class="sy0">,</span> <span class="kw1">this</span>.<span class="me1">cx</span><span class="sy0">,</span> <span class="kw1">this</span>.<span class="me1">cy</span><span class="br0">&#93;</span><span class="br0">&#125;</span><span class="sy0">,</span> <span class="nu0">500</span><span class="sy0">,</span> <span class="st0">&quot;bounce&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span><span class="kw1">this</span>.<span class="me1">label</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">this</span>.<span class="me1">label</span><span class="br0">&#91;</span><span class="nu0">0</span><span class="br0">&#93;</span>.<span class="me1">animate</span><span class="br0">&#40;</span><span class="br0">&#123;</span>scale<span class="sy0">:</span> <span class="nu0">1</span><span class="br0">&#125;</span><span class="sy0">,</span> <span class="nu0">500</span><span class="sy0">,</span> <span class="st0">&quot;bounce&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">this</span>.<span class="me1">label</span><span class="br0">&#91;</span><span class="nu0">1</span><span class="br0">&#93;</span>.<span class="me1">attr</span><span class="br0">&#40;</span><span class="br0">&#123;</span><span class="st0">&quot;font-weight&quot;</span><span class="sy0">:</span> 400<span class="br0">&#125;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span>
&nbsp; &nbsp; <span class="br0">&#125;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</div>
</pre>
<h3>Trend Chart Technical Details</h3>
<p>The trend charts are created using the <a href="http://www.danvk.org/dygraphs/">DyGraph</a> Javascript library.</p>
<p>I used a similar function-wrapping method to provide the right <em>document</em>.</p>
<pre>
<div class="codesnip-container" >
<div class="javascript codesnip" style="font-family:monospace;">init_dygraph_canvas<span class="br0">&#40;</span>request.<span class="me1">get_document</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
init_dygraph<span class="br0">&#40;</span>request.<span class="me1">get_document</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
g <span class="sy0">=</span> <span class="kw2">new</span> DateGraph<span class="br0">&#40;</span>
&nbsp; &nbsp; request.<span class="me1">jQuery</span><span class="br0">&#40;</span><span class="st0">&quot;#&quot;</span><span class="sy0">+</span>div_id<span class="br0">&#41;</span>.<span class="me1">get</span><span class="br0">&#40;</span>0<span class="br0">&#41;</span><span class="sy0">,</span>
&nbsp; &nbsp; &nbsp; &nbsp; data<span class="sy0">,</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span>showRoller<span class="sy0">:</span> <span class="kw2">false</span><span class="sy0">,</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">//labelsDivWidth: 350,</span>
&nbsp; &nbsp; &nbsp; &nbsp; labelsDiv<span class="sy0">:</span> request.<span class="me1">jQuery</span><span class="br0">&#40;</span><span class="st0">&quot;#legend&quot;</span><span class="br0">&#41;</span>.<span class="me1">get</span><span class="br0">&#40;</span>0<span class="br0">&#41;</span><span class="sy0">,</span>
&nbsp; &nbsp; &nbsp; &nbsp; labelsSeparateLines<span class="sy0">:</span> <span class="kw2">true</span><span class="sy0">,</span>
&nbsp; &nbsp; &nbsp; &nbsp; axisLabelFontSize<span class="sy0">:</span> <span class="nu0">14</span><span class="sy0">,</span>
&nbsp; &nbsp; &nbsp; &nbsp; pixelsPerXLabel<span class="sy0">:</span> <span class="nu0">50</span><span class="sy0">,</span>
&nbsp; &nbsp; &nbsp; &nbsp; pixelsPerYLabel<span class="sy0">:</span> <span class="nu0">50</span><span class="sy0">,</span>
&nbsp; &nbsp; &nbsp; &nbsp; gridLineColor<span class="sy0">:</span> <span class="st0">&quot;#BBBBBB&quot;</span><span class="sy0">,</span>
&nbsp; &nbsp; &nbsp; &nbsp; strokeWidth<span class="sy0">:</span> <span class="nu0">4</span><span class="sy0">,</span>
&nbsp; &nbsp; &nbsp; &nbsp; highlightCircleSize<span class="sy0">:</span> <span class="nu0">6</span><span class="sy0">,</span>
&nbsp; &nbsp; &nbsp; &nbsp; colors<span class="sy0">:</span> <span class="br0">&#91;</span>acolor<span class="sy0">,</span> bcolor<span class="br0">&#93;</span><span class="sy0">,</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="coMULTI">/*xAxisLabelWidth: 50,
&nbsp; &nbsp; &nbsp; &nbsp; yAxisLabelWidth: 50*/</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><span class="br0">&#41;</span><span class="sy0">;</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 

request.<span class="me1">jQuery</span><span class="br0">&#40;</span><span class="st0">&quot;#trend_chart&quot;</span><span class="br0">&#41;</span>.<span class="me1">children</span><span class="br0">&#40;</span><span class="br0">&#41;</span>.<span class="me1">children</span><span class="br0">&#40;</span><span class="br0">&#41;</span>.<span class="me1">each</span><span class="br0">&#40;</span><span class="kw2">function</span><span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>$<span class="br0">&#40;</span><span class="kw1">this</span><span class="br0">&#41;</span>.<span class="me1">attr</span><span class="br0">&#40;</span><span class="st0">&quot;style&quot;</span><span class="br0">&#41;</span>.<span class="me1">match</span><span class="br0">&#40;</span><span class="st0">&quot;bottom: 0px&quot;</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp; &nbsp; $<span class="br0">&#40;</span><span class="kw1">this</span><span class="br0">&#41;</span>.<span class="me1">css</span><span class="br0">&#40;</span><span class="st0">&quot;margin-bottom&quot;</span><span class="sy0">,</span> <span class="st0">&quot;-.5em&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; <span class="br0">&#125;</span>
<span class="br0">&#125;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</div>
</pre>
<p>By the way, you can click on the graph to zoom in, double click to zoom out and even set the rolling average were I to enable that input book!</p>
]]></content:encoded>
			<wfw:commentRss>http://proudly.procrasdonate.com/magical-graphicals/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Life Tracking</title>
		<link>http://proudly.procrasdonate.com/life-tracking/</link>
		<comments>http://proudly.procrasdonate.com/life-tracking/#comments</comments>
		<pubDate>Sun, 06 Dec 2009 04:22:41 +0000</pubDate>
		<dc:creator>Lucy</dc:creator>
				<category><![CDATA[conceptual]]></category>
		<category><![CDATA[planning]]></category>

		<guid isPermaLink="false">http://procrasdonate.wordpress.com/?p=209</guid>
		<description><![CDATA[Some people use calendars to organize future meetings. I primarily use my calendar to track my past.
When I mention my meticulous time tracking to friends they react in one of two ways:

Neat. I do that, too.
How do you track the time you spend tracking your time?

If your reaction was the first one, I salute you. [...]]]></description>
			<content:encoded><![CDATA[<div id="attachment_416" class="wp-caption aligncenter" style="width: 260px"><a href="/wp-content/uploads/calendar_screenshot.png"><img src="/wp-content/uploads/calendar_screenshot.png?w=300" alt="A good week in iCalendar" title="calendar_screenshot" width="250" height="160" class="size-medium wp-image-416" /></a><p class="wp-caption-text">A good week in iCalendar</p></div>
<p>Some people use calendars to organize future meetings. I primarily use my calendar to track my past.</p>
<p>When I mention my meticulous time tracking to friends they react in one of two ways:</p>
<ol>
<li>Neat. I do that, too.</li>
<li>How do you track the time you spend tracking your time?</li>
</ol>
<p>If your reaction was the first one, I salute you. Please leave a comment telling me all about it.</p>
<p>For the rest of you&#8230;yes, haha&#8230;you make a good point. There are significant benefits to tracking one&#8217;s time, but it only works if the tracking itself takes hardly any effort or time.</p>
<h3>So easy I don&#8217;t even notice</h3>
<p>Tracking webpages should take no time. I switch tabs every few seconds. I view hundreds of different webpages a day without even realizing. Tracking my web habits with deep granularity is not something I could manually do. That&#8217;s part of what makes <a href="http://ProcrasDonate.com">ProcrasDonate</a> so good.</p>
<p>On the other hand, updating my <a href="http://proudly.procrasdonate.com/hello-world/">MacBook Air</a>&#8217;s <a href="http://www.apple.com/macosx/what-is-macosx/mail-ical-address-book.html">iCal</a> program a few times a day takes hardly any time. I simply bring iCal into focus and drag-n-drop events.</p>
<p>I track my time with minimal granularity:</p>
<ul>
<li>15 minutes is the smallest increment</li>
<li>Estimation is ok</li>
<li>I care only about the overall category of my actions</li>
<li>I write short notes for fun if at all</li>
</ul>
<p>Whenever I want to get feedback on my activity habits, I run a <a href="http://github.com/diN0bot/iCal-Analyzer">python script</a> that reads my iCal data and generates weekly statistics and graphs.</p>
<h3>Analysis</h3>
<p>I began tracking my time because I wanted to see how well I could balance a full time summer job with a demanding startup. It turns out I&#8217;m really bad at doing two serious projects at the same time.</p>
<p>I&#8217;d like to think I&#8217;m not as bad as the time tracking makes out, but the truth is that one serious project, with time for sports and simple building projects to recoup creative energies, is my stable resting state. Since I didn&#8217;t collect data early enough, the work that went into ProcrasDonate&#8217;s winning entry is the stuff of legend and lore.</p>
<div id="attachment_417" class="wp-caption aligncenter" style="width: 260px"><a href="/wp-content/uploads/calendar_chart1.png"><img src="/wp-content/uploads/calendar_chart1.png?w=300" alt="Weekly chart for a good week" title="calendar_chart" width="250" height="117" class="size-medium wp-image-417" /></a><p class="wp-caption-text">Weekly chart for a good week</p></div>
<p>My initial interest was simply to track how many hours a week I worked versus wasted time. Culturally, that&#8217;s a compelling number. The python script created a webpage with a list of pretty charts, one week after the other, and weekly statistics. The charts were easy to make using <a href="http://pygooglechart.slowchop.com/">google charts</a>, and looking at colorful charts is uplifting. Nonetheless, I primarily look at the weekly totals.</p>
<div id="attachment_409" class="wp-caption aligncenter" style="width: 260px"><a href="/wp-content/uploads/lifetrack1.png"><img src="/wp-content/uploads/lifetrack1.png?w=300" alt="LifeTracking Trend" title="lifetrack1" width="250" height="160" class="size-medium wp-image-409" /></a><p class="wp-caption-text">LifeTracking Trend</p></div>
<p>After a handful of weeks it became clear that what I wanted to see was trends. How am I changing each week? As well as averages. What do my cycles or weeks or days tend to look like? The big questions I wanted to answer were:</p>
<ul>
<li>Am I improving each week?</li>
<li>What can I learn about myself in order to get better?</li>
</ul>
<h3>Results</h3>
<p>In my first month after the summer I spent most of my time implementing a library to interact with Amazon Flexible Payments Service (FPS). It was mucky and slow going. I put in 25-35 hours per week on ProcrasDonate. Surfing the web, eating meals, and talking to people are not included.</p>
<p>The next month got better as I saw the big picture for completing our first beta release. My technical skills were also improving, and in general I was ramping up on how to do this work more and more efficiently.</p>
<p>Finally, in November we started using Pivotal Tracker and sending out weekly releases. That brought my weekly totals up to 50-55 hours per week. Looks like my gushing over Pivotal Tracker is justified <img src='http://proudly.procrasdonate.com/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
<h3>Answering the interesting questions</h3>
<p>The interesting questions go beyond summing one&#8217;s hours per week. They rely on better charts and better analysis. Signal analysis comes to mind.</p>
<p>At first I&#8217;d like to focus on better trend charts, perhaps animated to move across weeks. Perhaps stacked lines? Perhaps 24 hr pie charts? Perhaps correlated with Pivotal Tracker or git events?</p>
<p>I&#8217;d like to see all my days on top of each other, with the hours of the day and the number of hours spent on the axes. Then I could see when times of the day were prone to time wasting, and how much variance is in my working hou<br />
I&#8217;d like to see all my weeks on top of each other, with the days of the week and the hours of the day on each axis. That way I could see when I am most productive, assuming I have a roughly week-long period for my productivity cycles.</p>
<p>Perhaps I can automatically split the trends into cycles and then look for similarities. How much variance is there in cycle length and productivity? Do sports and other projects seem to spur productivity versus take it away? How does sleep fit into all of this?</p>
<p>That this brainstorming heavily ties into ProcrasDonate hasn&#8217;t escaped my attention; that working on these charts conflicts with directly working on ProcrasDonate hasn&#8217;t escaped, either <img src='http://proudly.procrasdonate.com/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
<p>Even so, these questions won&#8217;t go unanswered for long.</p>
<p>Proudly ProcrasDonating,</p>
<p>Lucy.</p>
]]></content:encoded>
			<wfw:commentRss>http://proudly.procrasdonate.com/life-tracking/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>How we track website visits</title>
		<link>http://proudly.procrasdonate.com/time-tracker-sleuthing/</link>
		<comments>http://proudly.procrasdonate.com/time-tracker-sleuthing/#comments</comments>
		<pubDate>Sat, 05 Dec 2009 22:39:56 +0000</pubDate>
		<dc:creator>Lucy</dc:creator>
				<category><![CDATA[technical]]></category>

		<guid isPermaLink="false">http://proudlyprocrasdonating.wordpress.com/?p=375</guid>
		<description><![CDATA[Users often wonder what, exactly, does ProcrasDonate track. This is an entirely reasonable thing to wonder. Clay and the rounds of beta testers are iterating on how to make this clearer on the website.
Proudly ProcrasDonating is a technology blog, so I can happily overwhelm readers with all the subtleties of time track and if no [...]]]></description>
			<content:encoded><![CDATA[<p>Users often wonder what, exactly, does <a href="http://ProcrasDonate.com" target="_blank">ProcrasDonate</a> track. This is an entirely reasonable thing to wonder. Clay and the rounds of beta testers are iterating on how to make this clearer on the website.</p>
<p>Proudly ProcrasDonating is a technology blog, so I can happily overwhelm readers with all the subtleties of time track and if no one is the wiser then I&#8217;ll return to coding undeterred, and will try another blog post later, no harm done. If you have feedback, do share <img src='http://proudly.procrasdonate.com/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
<p>The <a href="http://ProcrasDonate.com" target="_blank">ProcrasDonate</a> extension tracks the <em>webpage currently being viewed</em>. Let&#8217;s say you open Firefox and start reading <a href="http://gmail.com">GMail</a>. Our extension records the start time.</p>
<p>Your friend has emailed you a link to <a href="http://proudlyprocrasdonating.wordpress.com">Proudly ProcrasDonating</a>, which opens in a new tab when you click on it. When you go to the new tab, the URL bar changes, which triggers our url bar listener. The listener stops recording the GMail visit, which is in turn saved in the database, and starts recording the Proudly ProcrasDonating visit.</p>
<p>If you switch back to the GMail tab, the visit to Proudly ProcrasDonating is halted and saved, and a visit to GMail is initiated. Same for switching between multiple Firefox windows.</p>
<p><em>We only record the webpage currently being viewed.</em></p>
<p>(This might be problematic for people who listen to internet radio or stream tv shows while they work. If you never visit the audio page, then we don&#8217;t know about it. I personally wouldn&#8217;t count that time as ProcrasDonating if the tabs I were actively viewing are work, but if I wanted to contribute to the audio content providers via TimeWellSpent, I wouldn&#8217;t be able to.)</p>
<p style="text-align:center;">* * *</p>
<p>Tracking the URL bar notifies us whenever the user visits a different webpage. However, we also need to be notified when the user does other things, including:</p>
<ol>
<li>Opens or closes Firefox</li>
<li>Switches to another application and back to Firefox</li>
<li>Walks away from the computer and comes back</li>
<li>Puts his computer to sleep and wakes it up</li>
<li>Force quits Firefox (or Firefox quits unexpectedly)</li>
</ol>
<p>I&#8217;m going to explain each of these issues in turn, including the current state of things.</p>
<h3>User opens or closes Firefox</h3>
<p>This is covered by our <em>InitListener</em>, which is an
<div class="codesnip-container" >EventListener</div>
<p> on the browser for &#8216;load&#8217; and &#8216;unload&#8217; events. Its time tracking is straightforward: stop recording when Firefox closes.</p>
<p>The tricky part is that when Firefox opens we don&#8217;t know whether it is in focus or not. The user might have opened Firefox and then switched to a different application before our extension is fully loaded and listening.</p>
<p>Right now we assume Firefox is out of focus; thus, we err on not recording the initial visit rather than record a false visit.</p>
<h3>User switches to another application and back to Firefox</h3>
<p>This is covered by our <em>FocusBlurListener</em>, which is an
<div class="codesnip-container" >EventListener</div>
<p> on the browser for &#8216;focus&#8217; and &#8216;blur&#8217; events. A focus event occurs when an element, such as form field or the url bar, gains focus. A blur occurs when an element loses focus.</p>
<p>Let&#8217;s say the user is typing &#8220;procrastinate less&#8221; into the google search bar. The search bar has the focus. Halfway through typing, the user decides to click on the ProcrasDonate meter in their toolbar. The search bar blurs and the meter icon gains focus.</p>
<p>Because of &#8220;bubbling,&#8221; the containing elements also gain and lose focus. Thus, a single mouse click can trigger a dozen focus and blur events.</p>
<p>We care about the last event. If the user is clicking on Firefox elements, then the last event is some element gaining focus. If the user is clicking on a different application, or alt-tabbing to a different application, the last event is a blur event, since no Firefox element is gaining focus from the one that loses it.</p>
<p>Thus, the <strong>first time</strong>* a focus or blur event occurs, a timeout is set to run in 1 second. Also, every focus and blur event sets a <strong>last event type</strong> flag to either <strong>focus</strong> or <strong>blur</strong>.</p>
<p>After 1 second, the timeout function is called. If the <strong>last event type</strong> flag is <strong>focus</strong>, then we know Firefox is the active application and we may need to start recording a visit. Otherwise, we know Firefox is not active, and we should stop recording the visit.</p>
<p>*The timeout function also resets the <strong>first time</strong> flag.</p>
<h3>User walks away from the computer and comes back</h3>
<p>This is covered by our <em>IdleBackListener</em>, which registers with Mozilla&#8217;s idleServer,
<div class="codesnip-container" >nsIIdleService</div>
<p>. This is a neat service since the notifications come for the user&#8217;s operating system. I suspect this means that pressing volume and lighting controls, in addition to standard keyboard and mouse actions, including scrolling, also triggers non-idle-ness. It might be different per operating system, and it also can add up to a 5-second delay on notifications.</p>
<p>Detecting idleness is pretty tricky. We don&#8217;t know whether the user is reading an article or taking a bathroom break, nor whether the user is watching a video or was interrupted and is now talking to a friend.</p>
<p>To make matters worse, we can&#8217;t tell what&#8217;s happening with flash. Is the movie paused or is it playing? Is it an advertisement or an hour long show?</p>
<p>We try to err on the side of caution. We register two IdleBackListeners, one for flash pages and one for no-flash pages. The no-flash idle timeout occurs after a few minutes. There&#8217;s only so long one can read a single screen of text. If nothing happens, we give the user the benefit of the doubt and stop recording the visit. When the user comes back, we start recording again.</p>
<p>If the currently viewed webpage does contain flash then we wait for the 20 minute &#8220;idle with flash&#8221; timeout to occur. We figured that 20 minutes was the length of a short TV show, and would do for a default guess. If the user is watching a full movie, then maybe he will have to move his mouse within 20 minutes anyway to keep the screensaver from interrupting.</p>
<p>We plan to make idle detection more precise eventually. The backend already supports per-website idle timeout lengths. Time yet for iteration.</p>
<h3>User puts his computer to sleep and wakes it up</h3>
<p>I hadn&#8217;t initially thought about this case. Of course, it didn&#8217;t take long to realize that if I closed my laptop with Firefox the active application, I&#8217;d return to see a sudden spike in ProcrasDonation visits.</p>
<p>At first it seemed there was no Mozilla listener for the computer going to sleep or waking up. One solution would be to set our own timer to periodically chirp. If it ever chrips and notices that its last chirp was longer ago than expected, then some kind of pause occurred, and the current visit should stop at that last chirp.</p>
<p>Fortunately for my street cred, I was able to google and forum search it up until I found something interesting about Firefox&#8217;s download manager: it pauses downloads when a computer goes to sleep, and resumes them when the computer wakes up. A code search immediately turned up this gem <a href="http://bonsai.mozilla.org/cvsview2.cgi?diff_mode=context&amp;whitespace_mode=show&amp;file=nsDownloadManager.cpp&amp;branch=&amp;root=/cvsroot&amp;subdir=mozilla/toolkit/components/downloads/src&amp;command=DIFF_FRAMESET&amp;rev1=1.172&amp;rev2=1.173">here</a>:</p>
<pre>
mObserverService-&gt;AddObserver(this, "sleep_notification", PR_FALSE);
mObserverService-&gt;AddObserver(this, "wake_notification", PR_FALSE);
</pre>
<p>That looks like C code. Futzing around with Javascript soon yielded success, which now lives in our <em>SleepWakeListener</em>:</p>
<pre>
this.observerService.addObserver(this, "sleep_notification", false);
this.observerService.addObserver(this, "wake_notification", false);
</pre>
<p>it was almost too easy. I emailed <a href="https://developer.mozilla.org/User:Sheppy">Eric Shepherd</a>, Developer Documentation Lead, to ask why these notifications were not included in the <a href="https://developer.mozilla.org/en/Observer_Notifications">Observer Notification</a> page.</p>
<p>Simple omission apparently. I&#8217;ve since added a <a href="https://developer.mozilla.org/en/Observer_Notifications#Computer_Sleep.2c_Wake">Computer Sleep, Wake</a> section to the Observer Notification wiki page.</p>
<p>(A side-benefit of having sleep and wake work is that now when I close my laptop to go to bed, a time when I often forget to update my meticulous life-tracking calendar, I can simply read the last few messages in my development logs the next morning.)</p>
<h3>User force quits Firefox (or Firefox quits unexpectedly)</h3>
<p>There may be times when Firefox&#8217;s close notifications are not called, for instance an unexpected power outage, or a memory-leak-induced forced quit.</p>
<p>The chirps don&#8217;t sound like a bad idea now, although there might be a notification for when Firefox opens after not closing correctly.</p>
<p>I&#8217;m going to have to get back to you on this one since I&#8217;m still working on it.</p>
<p>It might also be nice to show the largest visits of the past week and allow submitting them as bugs, which would also subtract them from the time tracking totals.</p>
<p>I hope that cleared some thing up. Questions and suggestions are welcome as usual.</p>
<p>Proudly ProcrasDonating,</p>
<p>Lucy.</p>
]]></content:encoded>
			<wfw:commentRss>http://proudly.procrasdonate.com/time-tracker-sleuthing/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Extension Listeners</title>
		<link>http://proudly.procrasdonate.com/extension-listeners/</link>
		<comments>http://proudly.procrasdonate.com/extension-listeners/#comments</comments>
		<pubDate>Sat, 05 Dec 2009 16:56:55 +0000</pubDate>
		<dc:creator>Lucy</dc:creator>
				<category><![CDATA[technical]]></category>

		<guid isPermaLink="false">http://proudlyprocrasdonating.wordpress.com/?p=355</guid>
		<description><![CDATA[The above diagram shows our original extension architecture. It&#8217;s not a hacked together mess by any means, but not all of the object oriented behaviors went with the right objects. Mix in some dead or duplicated code from learning how to write extensions and soon it&#8217;s hard to keep things straight.
Dan wrote this while I [...]]]></description>
			<content:encoded><![CDATA[<div id="attachment_356" class="wp-caption alignnone" style="width: 280px"><a href="/wp-content/uploads/extension_flow1.png"><img src="/wp-content/uploads/extension_flow1.png?w=300" alt="" title="extension_flow" width="270" class="size-medium wp-image-356" /></a><p class="wp-caption-text">Original extension architecture</p></div>
<p>The above diagram shows our original extension architecture. It&#8217;s not a hacked together mess by any means, but not all of the object oriented behaviors went with the right objects. Mix in some dead or duplicated code from learning how to write extensions and soon it&#8217;s hard to keep things straight.</p>
<p>Dan wrote this while I mucked around with views and the data model, so don&#8217;t think I&#8217;m complaining. It worked! Nonetheless, reading through the code sufficiently confused me so that in the diagram I listed each object&#8217;s functions, including who called what, and also who used what preferences, which made the diagram so confusing I then added big red arrows to give the big picture.</p>
<div id="attachment_405" class="wp-caption alignnone" style="width: 280px"><a href="/wp-content/uploads/extension_object_model2.png"><img src="/wp-content/uploads/extension_object_model2.png?w=300"  width="270" alt="" title="extension_object_model" class="size-medium wp-image-405" /></a><p class="wp-caption-text">Super awesome extension architecture</p></div>
<p>This diagram shows how our refactored extension architecture currently looks. Beautiful isn&#8217;t it? It&#8217;s not a true apples-to-apples comparison with the original diagram since this one contains much less information. I suppose that is also good news, since it means I&#8217;m more intimately acquainted with the code and APIs, and therefore no longer needed to spell everything out.</p>
<p>Nonetheless, the code has been greatly clarified. Part of the reason I can look at each object and know what it does is because each object does exactly what one would expect. The main improvements are:</p>
<ul>
<li>Listener objects</li>
<li>Appropriately placed fields</li>
<li>Deleted dead, duplicate code</li>
</ul>
<h3>Listener Objects</h3>
<p>Our extension has a number of listeners:</p>
<ul>
<li>InitListener</li>
<li>PageLoadListener</li>
<li>URLBarListener</li>
<li>UninstallListener</li>
<li>BlurFocusListener</li>
<li>SleepWakeListener</li>
<li>IdleBackListener (x2)</li>
</ul>
<p>In the past couple weeks I&#8217;ve added those last three listeners, plus half of the uninstall one. Even though the extension&#8217;s main object, <em>myOverlay</em>, originally contained just three listeners, the jumbling of code made it seem super complicated.</p>
<p>All of the non-view and non-lib code was shared between <em>Overlay</em> and <em>PDDB (ProcrasDonateDataBase)</em>. This included install and upgrade code, website tracking listeners and logic, database initialization and page load logic.</p>
<p><em>myOverlay</em> was itself the listener to various events, although it did instantiate a URLBarListener instance that subclassed, in a sense, from Overlay. Some listeners weren&#8217;t able to unregister properly. Some of the new listeners wanted to live in <em>PDDB</em>.</p>
<p>myOverlay and PDDB did too much. Now they delegate to happily packaged listeners.</p>
<p>Each group of listeners that make a listening concept, such as &#8220;em-action-requested&#8221; and &#8220;quit-application-granted&#8221;, from which we determine whether to uninstall the extension, is now packaged into a handy class. The constructor takes necessary state, such as access to PDDB and preferences, and automatically registers itself.</p>
<p>Additionally, I abstracted all the time tracking logic, such as <em>start_recording</em> and <em>stop_recording</em>, into a <em>TimeTracking</em> object which is passed into the constructors of the time tracking listeners.</p>
<h3>Appropriately placed object fields</h3>
<p>There&#8217;s nothing more confusing than our database ORM also doing time tracking logic and url dispatch. And why did I put the toolbar manager inside the url bar listener?</p>
<p>The new setup is clear as water. <em>Overlay</em> registers the <em>InitListener</em>. The InitListener loads everything else: all of the state (PDDB, TimeTracker, Schedule, Controller, Prefs, PD_API, ToolbarManager); and all of the listeners. <em>PDDB</em> just initializes state. The <em>Controller&#8217;s</em> URL dispatch is called from <em>PageLoadListener</em>, just as you&#8217;d expect.</p>
<p>When <em>InitListener</em>&#8217;s uninit is called, it unregisters all of its listeners.</p>
<h3>Deleted dead, duplicate code</h3>
<p>Finally, it helps when there is just one <em>Controller</em> instantiation, rather than an array of controllers and various Controller references, including a <em>PageController</em>, which is already a field of Controller. I guess this wasn&#8217;t problematic because even though our code is object oriented, there is very little instance state. Boy would that be a hard bug to track down one day&#8230;</p>
<p>While refactoring the code, I was able to remove many of the unnecessary variables and functions that come from iterative learning. In one place we used the &#8220;nsIPrefService&#8221; directly, whereas in most others we used our abstracted <em>PreferenceManager</em> object. Now we use the PreferenceManager everywhere, and not by asking for the one inside PDDB! Same goes for accessing the <em>ProcrasDonate API</em>. No longer does the view have to access it through
<div class="codesnip-container" >this.pddb.page.pd_api</div>
<p>. The view gets its own reference on instantiation, just as PageController does.</p>
<h3>Epilogue</h3>
<p>I took apart the code yesterday morning. It was a working well oiled machine by yesterday night. According to my meticulous records that meant I spent at most 9 hrs on this story.[1] I spent maybe two days prior informally thinking about the re-factoring I wanted to do (which is meticulously recorded as sleeping and walking with Bear to the park).</p>
<p>It was adding the SleepWakeListener a few days ago that tipped me over into re-factoring. After doubling the number of listeners and having to run the gamut of warning lights and not-quite-working binds, I was ready to make adding features easier and better.</p>
<p>Last night, once I completed putting things back together, I didn&#8217;t have any heinous bugs. I wasn&#8217;t expecting any, which is an unusual and awesome feeling that I&#8217;m chalking up to my excellent understanding of our code and the Mozilla API, rather than some kind of ignorant risk-taking or good fortune. Either way, I closed my laptop, brushed my teeth, and when I came back to admire the beautiful new architecture one last time before bed, ProcrasDonate verified that my last page view had only lasted 5 seconds rather than 5 minutes.</p>
<p>Proudly ProcrasDonating,</p>
<p>Lucy.</p>
<p>ps &#8211; We also still pass all of our regression tests. How to test listeners will have to be a different blog post.</p>
<p>pps &#8211; I do take meticulous records, which I&#8217;m looking forward to blogging about later. [edit: see <a href="http://proudly.procrasdonate.com/life-tracking/">Life Tracking</a>]</p>
<p>ppps &#8211; Wondering what these listeners are? Here&#8217;s a simplified explanation</p>
<p><strong>InitListener</strong> &#8211; Called when a window is loaded or unloaded</p>
<p><strong>PageLoadListener</strong> &#8211; Called when a page is loaded</p>
<p><strong>URLBarListener</strong> &#8211; Called when the url bar changes</p>
<p><strong>UninstallListener</strong> &#8211; Called when the user requests the extension is uninstalled, cancels that request, or closes Firefox, at which point a request uninstall will occur</p>
<p><strong>BlurFocusListener</strong> &#8211; Called when a window element gains or loses focus (used to track whether Firefox is the active application)</p>
<p><strong>SleepWakeListener</strong> &#8211; Called when the user&#8217;s computer goes to sleep or wakes up</p>
<p><strong>IdleBackListener</strong> (x2) &#8211; Called when the operating system reports that the user has been idle for a specified period of time (we specify two times, and thus have two listeners)</p>
]]></content:encoded>
			<wfw:commentRss>http://proudly.procrasdonate.com/extension-listeners/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Story Types</title>
		<link>http://proudly.procrasdonate.com/story-types/</link>
		<comments>http://proudly.procrasdonate.com/story-types/#comments</comments>
		<pubDate>Sat, 05 Dec 2009 08:33:53 +0000</pubDate>
		<dc:creator>Lucy</dc:creator>
				<category><![CDATA[planning]]></category>

		<guid isPermaLink="false">http://proudlyprocrasdonating.wordpress.com/?p=338</guid>
		<description><![CDATA[
This fun chart is from Pivotal Tracker. It&#8217;s a stacked line chart of the types of stories we&#8217;ve completed each iteration since we began using Pivotal Tracker.
The graph provides a colorful analysis of how we allocated our efforts: squashing bugs; bulking up the code; adding functionality.
Seeing past results is powerful. One naturally follows the trend [...]]]></description>
			<content:encoded><![CDATA[<p><a href="/wp-content/uploads/storytype_09_12_05.png"><img src="/wp-content/uploads/storytype_09_12_05.png?w=300" alt="" title="storytype_09_12_05" width="150" height="117" class="alignright size-medium wp-image-336" /></a></p>
<p>This fun chart is from Pivotal Tracker. It&#8217;s a stacked line chart of the types of stories we&#8217;ve completed each iteration since we began using Pivotal Tracker.</p>
<p>The graph provides a colorful analysis of how we allocated our efforts: squashing bugs; bulking up the code; adding functionality.</p>
<p>Seeing past results is powerful. One naturally follows the trend into the future. What breakdown do we want? Is it good or bad to spend time squashing bugs? I can&#8217;t help but feel extra motivated in bringing the red line (bugs) down to 0. I wonder if the black line (chores) is cyclic, and if so with what frequency. Can I raise the blue line (features) consistently? Will all these short term metrics trick us into making a bad decision?</p>
<p>It will be fun to find out.</p>
<p>Note that bugs and chores are always 1 &#8220;point&#8221; on the chart, since we&#8217;ve opted not to assigned points to them. Keeps us driving towards features since that&#8217;s the only way to burn down each iteration&#8217;s point-based allocation of stories. Although we can&#8217;t precisely compare story allocation to time spent, these results look like a reasonable proxy.</p>
<p>I don&#8217;t have too much more to say on this. Just wanted to share something colorful while we wait for more data to come in.</p>
<p>Proudly ProcrasDonating,</p>
<p>Lucy.</p>
]]></content:encoded>
			<wfw:commentRss>http://proudly.procrasdonate.com/story-types/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>
