<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
     xmlns:content="http://purl.org/rss/1.0/modules/content/"
     xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
     xmlns:atom="http://www.w3.org/2005/Atom"
     xmlns:dc="http://purl.org/dc/elements/1.1/"
     xmlns:wfw="http://wellformedweb.org/CommentAPI/"
     >
  <channel>
    <title>[three]Bean</title>
    <link>http://threebean.org/blog</link>
    <description>Creates a Blog</description>
    <pubDate>Tue, 14 May 2013 18:03:38 GMT</pubDate>
    <generator>Blogofile</generator>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>1</sy:updateFrequency>
    <item>
      <title>In which I avoid the inverse unicode sandwich</title>
      <link>http://threebean.org/blog/sieve</link>
      <pubDate>Fri, 22 Jun 2012 18:00:00 EDT</pubDate>
      <category><![CDATA[python]]></category>
      <category><![CDATA[toscawidgets]]></category>
      <category><![CDATA[testing]]></category>
      <category><![CDATA[turbogears]]></category>
      <guid>http://threebean.org/blog/sieve</guid>
      <description>In which I avoid the inverse unicode sandwich</description>
      <content:encoded><![CDATA[<div class="document">
<p><strong>Problem #1</strong> - I need to test tons of HTML output for correctness (because I
maintain <a class="reference external" href="http://toscawidgets.org">toscawidgets2</a>).
That output varies slightly because <tt class="docutils literal">tw2</tt> supports five different
templating languages (mako, genshi, jinja2, kajiki, and chameleon).  Using
double-equals (<tt class="docutils literal">==</tt>) just won't do it.</p>
<p><strong>Solution #1</strong> - We used <a class="reference external" href="http://pypi.python.org/pypi/strainer">strainer</a>.  It
works!</p>
<p><strong>Problem #2</strong> - Imagine porting <a class="reference external" href="http://bit.ly/O57MFF">this</a> to Python 3.  Yes,
that's right.  The encoding is sniffed by hand and then used to encode
regular expressions; these are in turn applied to parse XML.
Think &quot;<a class="reference external" href="http://bit.ly/O58xi7">inverse unicode sandwich</a> with a side of
<a class="reference external" href="http://bit.ly/O58lzf">Cthulhu</a>.&quot;</p>
<p><strong>Solution #2</strong> - I wrote <a class="reference external" href="http://pypi.python.org/pypi/sieve">sieve</a>: a baby
module child of one corner of FormEncode and another corner of strainer.  It
<a class="reference external" href="http://travis-ci.org/#!/ralphbean/sieve">works on pythons 2.6, 2.7, and 3.2</a>.  If you like, you may use it:</p>


<div class="pygments_monokai"><pre><span class="o">&gt;&gt;&gt;</span> <span class="kn">from</span> <span class="nn">sieve.operators</span> <span class="kn">import</span> <span class="n">eq_xml</span><span class="p">,</span> <span class="n">in_xml</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">a</span> <span class="o">=</span> <span class="s">&quot;&lt;foo&gt;&lt;bar&gt;Value&lt;/bar&gt;&lt;/foo&gt;&quot;</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">b</span> <span class="o">=</span> <span class="s">&quot;&quot;&quot;</span>
<span class="s">... &lt;foo&gt;</span>
<span class="s">...     &lt;bar&gt;</span>
<span class="s">...         Value</span>
<span class="s">...     &lt;/bar&gt;</span>
<span class="s">... &lt;/foo&gt;</span>
<span class="s">... &quot;&quot;&quot;</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">eq_xml</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">)</span>
<span class="bp">True</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">c</span> <span class="o">=</span> <span class="s">&quot;&lt;html&gt;&lt;body&gt;&lt;foo&gt;&lt;bar&gt;Value&lt;/bar&gt;&lt;/foo&gt;&lt;/body&gt;&lt;/html&quot;</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">in_xml</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">c</span><span class="p">)</span>  <span class="c"># &#39;needle&#39; in a &#39;haystack&#39;</span>
<span class="bp">True</span>
</pre></div>



<p>p.s. -- I looked into <a class="reference external" href="http://pypi.python.org/pypi/xmldiff">xmldiff</a>.
Awesome!</p>
</div>
]]></content:encoded>
    </item>
    <item>
      <title>Teaching Open Source, Web-Based, Game Programming at RIT</title>
      <link>http://threebean.org/blog/floss-seminar-rundown</link>
      <pubDate>Tue, 06 Mar 2012 18:00:00 EST</pubDate>
      <category><![CDATA[python]]></category>
      <category><![CDATA[teachingopensource]]></category>
      <category><![CDATA[openshift]]></category>
      <category><![CDATA[turbogears]]></category>
      <guid>http://threebean.org/blog/floss-seminar-rundown</guid>
      <description>Teaching Open Source, Web-Based, Game Programming at RIT</description>
      <content:encoded><![CDATA[<div class="document">
<p>This last <a class="reference external" href="http://www.rit.edu/emcs/admissions/bca/blog/item/academic-quarters">quarter</a> at <a class="reference external" href="http://www.rit.edu">RIT</a> I taught (for the first time) a <a class="reference external" href="http://ritfloss.rtfd.org">course on open source,
web-based, game programming</a>.  The quarter just came
to an end and I really want to brag on my students.  But before I do that,
here's a glimmer of what we covered:</p>
<blockquote>
<ul class="simple">
<li>How to use bash and vim</li>
<li>How to use git and github</li>
<li>How to submit patches to projects you know nothing about</li>
<li>What makes a good, <a class="reference external" href="http://www.amazon.com/Casual-Game-Design-Designing-Gamer/dp/0123749530">casual game</a></li>
<li>How to make <a class="reference external" href="http://www.amazon.com/Making-Isometric-Social-Real-Time-Javascript/dp/1449304753">games with HTML5</a></li>
<li>How to program in JavaScript, CoffeeScript, and Python</li>
<li>Serverside programming with <a class="reference external" href="http://turbogears.org">Turbogears2</a></li>
<li>How to get your code running on Red Hat's <a class="reference external" href="http://openshift.redhat.com">Openshift</a> cloud.</li>
<li>How to give a lightning talk(!)</li>
</ul>
</blockquote>
<p>Really cool stuff was built into the class; it was about making open source
software, so we <a class="reference external" href="http://ritfloss.readthedocs.org/en/latest/hw/fflight.html#patch-the-course-project">open sourced the syllabus</a>!
Really cool stuff happened along the way; we hit ugly problems with openshift,
so we <a class="reference external" href="https://github.com/lmacken/turbogears2-openshift-quickstart/commits/">patched the quickstarter script</a>!</p>
<p>You can endure my self-indulgent drivel yes, but you get the best picture from
reading the <a class="reference external" href="http://threebean.org/floss-planet">students' blog posts</a>
themselves.  Enough of this!  <cite>Les projets de cours</cite>!</p>
<hr class="docutils" />
<p><strong>#1</strong> - <a class="reference external" href="http://lazorz-fossrit.rhcloud.com/welcome.html">Lazorz</a> teamed up
with the <a class="reference external" href="http://www.mos.org/">Boston Museum of Science</a> to make
&quot;[An] educational game about the physics of light.  In it's current incarnation
it helps demonstrate the concepts of reflection and color filtering.  The
development team has plans to include other concepts such as refraction
and prisms in future releases.&quot;</p>
<a class="reference external image-reference" href="http://lazorz-fossrit.rhcloud.com/welcome.html"><img alt="http://lazorz-fossrit.rhcloud.com/image/lazorzscreenshot.png" src="http://lazorz-fossrit.rhcloud.com/image/lazorzscreenshot.png" /></a>
<p>They got a nasty front-end built, by hand, with javascript and HTML5 that
<strong>works on every mobile device we could test it on</strong>.  It is intuitive and fun.
They have almost-working Facebook auth and they have an almost-working
Turbogears2 backed JSON store. (really, <em>this</em> close to completion!)</p>
<hr class="docutils" />
<p><strong>#2</strong> - <a class="reference external" href="http://gold-rush.rhcloud.com">Gold Rush</a> is visually and game-ly
amazing.  If I had to pick which of the three project was going to make one
million dollars, it'd be this one.</p>
<a class="reference external image-reference" href="http://gold-rush.rhcloud.com"><img alt="http://i.imgur.com/XJCshh.jpg" src="http://i.imgur.com/XJCshh.jpg" /></a>
<p>The front-end is built in Unity (closed source, but compiles to a ton of
platforms) and the back-end JSON store is built in TurboGears2.  These guys
completely flew through development, mastering skills they'd never heard of in a
day.  Its 3D with a moving camera and an incredibly fun game.  The original game
idea belongs to team member <a class="reference external" href="http://ericheaney.wordpress.com/">Eric Heaney</a>.  It's fun to play as a normal old
card game (I've played it at a number of parties since he pitched it to
the class).</p>
<hr class="docutils" />
<p><strong>#3</strong> -- <a class="reference external" href="http://webbot-qalthos.rhcloud.com">WebBotWar</a> (the python web
robot fighting game) wins the prize for <a class="reference external" href="http://github.com/CodingRobots">master hack</a>.  They:</p>
<blockquote>
<ul class="simple">
<li>Forked <a class="reference external" href="http://code.google.com/p/pybotwar/">pybotwar</a>, ripped it's UI off
and made it export JSON.</li>
<li>Reimplemented the front-end with <a class="reference external" href="http://calebevans.me/projects/jcanvas/index.php">jCanvas</a> (and cooked up all
their own art assets!).</li>
<li>Wrote a TurboGears2 app that spins up serverside instances of their
pybotwar fork.<ul>
<li>Those instances dump their state into mongodb (or memcached, long story).</li>
<li>Their javascript client polls the TG2 app for the game state and <a class="reference external" href="http://webbot-qalthos.rhcloud.com">voilà</a>.</li>
</ul>
</li>
</ul>
</blockquote>
<p>They have (awesome) plans to:</p>
<blockquote>
<ul class="simple">
<li>Allow you to upload your own scripts (they have it working, just not secured).</li>
<li>Make a built-in script editor.</li>
<li>Tighter facebook integration (challenge your friends!)</li>
</ul>
</blockquote>
<p>BTW, it works on android and iPad.  It even works on <a class="reference external" href="http://projects.gnome.org/epiphany/">Epiphany</a> (<a class="reference external" href="https://www.destroyallsoftware.com/talks/wat">wat?</a>)  They also want you
to know that python <a class="reference external" href="http://code.google.com/p/pybox2d/">box2d</a> is
a pain in the ass.</p>
<a class="reference external image-reference" href="http://webbot-qalthos.rhcloud.com/"><img alt="https://github.com/ralphbean/WebBot/raw/c127a15b0c5f1d5683c7619676fc7aec4970e061/pywebbot-screenshot.png" src="https://github.com/ralphbean/WebBot/raw/c127a15b0c5f1d5683c7619676fc7aec4970e061/pywebbot-screenshot.png" /></a>
</div>
]]></content:encoded>
    </item>
    <item>
      <title>ToscaWidgets2 Bugsprint Report</title>
      <link>http://threebean.org/blog/bugsprint-report</link>
      <pubDate>Sun, 04 Mar 2012 15:00:00 EST</pubDate>
      <category><![CDATA[python]]></category>
      <category><![CDATA[toscawidgets]]></category>
      <category><![CDATA[pyramid]]></category>
      <category><![CDATA[turbogears]]></category>
      <guid>http://threebean.org/blog/bugsprint-report</guid>
      <description>ToscaWidgets2 Bugsprint Report</description>
      <content:encoded><![CDATA[<div class="document">
<p>Here's the rundown of the <a class="reference external" href="http://threebean.org/blog/announcing-tw2-bugsprint">pre-PyCon tw2 bugsprint</a>.  The unstoppable <a class="reference external" href="https://github.com/gregjurman">Greg
Jurman</a> and I coordinated in google+ and IRC
(with lots of ad-hoc visitors in both) and did all the damage we could do to the
<a class="reference external" href="http://bitbucket.org/paj/tw2core/issues">bug list</a>.  Our goal was to close
enough that we could justify a solid 2.0 non-beta release but we didn't quite
get there.  We settled on <a class="reference external" href="http://pypi.python.org/pypi/tw2.core/2.0rc1">2.0 release candidate 1</a>.</p>
<p><strong>tl;dr</strong> - Big progress.  We'll seal the deal on 2.0 at PyCon US next week.</p>
<p>Infrastructure:</p>
<blockquote>
<ul class="simple">
<li>tw2 sourcecode is spread out in repos all over the place.  We centralized
<em>everything</em> at <a class="reference external" href="http://github.com/toscawidgets">http://github.com/toscawidgets</a>.</li>
<li>We're using <a class="reference external" href="http://github.com/nvie/gitflow">git-flow</a> to manage
team development and releases.</li>
<li>We setup a <a class="reference external" href="http://cia.vc">http://cia.vc</a> bot in #toscawidgets/IRC, which is awesome.</li>
<li>We're in the continuous integration queue with <a class="reference external" href="https://www.shiningpanda.com/">Shining Panda</a>.</li>
</ul>
</blockquote>
<p>Tickets closed:</p>
<blockquote>
<ul class="simple">
<li><a class="reference external" href="https://bitbucket.org/paj/tw2core/issue/92">Duplicated TW Encoders</a></li>
<li><a class="reference external" href="https://bitbucket.org/paj/tw2core/issue/71">Graceful degradation when JS disabled for tw2.dynforms</a></li>
<li><a class="reference external" href="https://bitbucket.org/paj/tw2core/issue/53">HTML5 Prompt Text</a></li>
<li><a class="reference external" href="https://bitbucket.org/paj/tw2core/issue/95">Configurable 'location' for resources</a></li>
<li><a class="reference external" href="https://bitbucket.org/paj/tw2core/issue/94">Disable resource injection on a per-resource basis</a></li>
<li><a class="reference external" href="https://bitbucket.org/paj/tw2core/issue/96">Document middleware configuration values</a></li>
<li><a class="reference external" href="https://bitbucket.org/paj/tw2core/issue/77">Handling SCRIPT_NAME</a></li>
<li><a class="reference external" href="https://bitbucket.org/paj/tw2core/issue/97">Internationalization</a></li>
<li><a class="reference external" href="https://bitbucket.org/paj/tw2core/issue/69">Inline templates</a></li>
<li><a class="reference external" href="https://bitbucket.org/paj/tw2core/issue/5">Refactor resources for better compatibility with tw0.9</a></li>
<li><a class="reference external" href="https://bitbucket.org/paj/tw2core/issue/14">Automatic 'modname' for resources</a></li>
<li><a class="reference external" href="https://bitbucket.org/paj/tw2core/issue/88">Shortcuts for controller registration / Tidy up tutorial</a></li>
</ul>
</blockquote>
<p>Tickets worked on, but not complete:</p>
<blockquote>
<ul class="simple">
<li><a class="reference external" href="https://bitbucket.org/paj/tw2core/issue/31">Make onload trigger onchange</a></li>
<li><a class="reference external" href="https://bitbucket.org/paj/tw2core/issue/44">Growing grid - no label</a></li>
<li><a class="reference external" href="https://bitbucket.org/paj/tw2core/issue/7">Problems with colons as id separators</a></li>
<li><a class="reference external" href="https://bitbucket.org/paj/tw2core/issue/67">Refactor controller widgets</a></li>
<li><a class="reference external" href="https://bitbucket.org/paj/tw2core/issue/107">Consider &quot;crank&quot;</a></li>
<li><a class="reference external" href="https://bitbucket.org/paj/tw2core/issue/66">Enhanced widgetbrowser</a></li>
</ul>
</blockquote>
<p>Tickets created:</p>
<blockquote>
<ul class="simple">
<li><a class="reference external" href="https://bitbucket.org/paj/tw2core/issue/109">Refactor the entire templates system</a></li>
</ul>
</blockquote>
</div>
]]></content:encoded>
    </item>
    <item>
      <title>TW2 Bugsprint; the Final Countdown</title>
      <link>http://threebean.org/blog/the-final-countdown</link>
      <pubDate>Fri, 02 Mar 2012 11:00:00 EST</pubDate>
      <category><![CDATA[python]]></category>
      <category><![CDATA[toscawidgets]]></category>
      <category><![CDATA[pyramid]]></category>
      <category><![CDATA[turbogears]]></category>
      <guid>http://threebean.org/blog/the-final-countdown</guid>
      <description>TW2 Bugsprint; the Final Countdown</description>
      <content:encoded><![CDATA[<div class="document">
<p>Tomorrow marks the start of the <a class="reference external" href="http://threebean.org/blog/announcing-tw2-bugsprint">toscawidgets2 24 hour bugsprint</a> and I couldn't be more
stoked.  We haven't even started yet but tw2 development pushed ahead anyways.</p>
<p>We have:</p>
<blockquote>
<ul class="simple">
<li>redesigned the <a class="reference external" href="http://threebean.org/blog/new-tw2-frontpage/">tw2 homepage</a>.</li>
<li>improved tw2.jqplugins.jqgrid's <a class="reference external" href="https://github.com/ralphbean/tw2.jqplugins.jqgrid/pull/4">sqlalchemy integration</a>.</li>
<li>pumped out <a class="reference external" href="https://github.com/ralphbean/tw2.jqplugins.ui/issues/8">one</a>
or <a class="reference external" href="https://github.com/ralphbean/tw2.jqplugins.ui/issues/9">two</a>
miscellaneous fixups to tw2.jqplugins.ui.<ul>
<li>This includes <a class="reference external" href="https://github.com/ralphbean/tw2.jqplugins.ui/issues/7">fixing the
grievously broken support for certain alternate themes</a>.</li>
</ul>
</li>
<li>released <a class="reference external" href="http://pypi.python.org/pypi/tw2.jqplugins.elrte">tw2.jqplugins.elrte</a>.</li>
<li>released <a class="reference external" href="http://pypi.python.org/pypi/tw2.jqplugins.elrte">tw2.jqplugins.elfinder</a> (which is amazing).</li>
<li>released <a class="reference external" href="http://pypi.python.org/pypi/tw2.captcha">tw2.captcha</a> based on
<cite>tgcaptcha2</cite>.</li>
<li>released an alpha version of <a class="reference external" href="http://pypi.python.org/pypi/tw2.d3">tw2.d3</a>.
It's built with WebSocket support in mind.  More on that soon!</li>
<li>been excited to learn that the <a class="reference external" href="https://groups.google.com/forum/?fromgroups#!topic/turbogears/N1xh_r0Sjt4">TurboGears2 team is aiming to
standardize on tw2</a>.</li>
<li>been fielding a lot more support for tw2 in <a class="reference external" href="http://pypi.python.org/pypi/pyramid">Pyramid 1.3</a> both on the mailing list and in
<tt class="docutils literal">#toscawidgets</tt>.  There's a bug still lurking out there for
<a class="reference external" href="https://bitbucket.org/paj/tw2core/issue/108/working-with-latest-pyramid-13">windows+tw2+pyramid users</a>.
At this point, if you can reproduce it and tell us about it, that would
be helpful.</li>
<li>got our first reports of people using tw2 with <a class="reference external" href="http://pypi.python.org/pypi/bottle">bottle</a>.  It worked!</li>
</ul>
</blockquote>
<p><strong>Goal:</strong> (solidly) close as many bugs as possible and push as many libraries
from beta to release as we can.</p>
<p>Here's to winning that game.</p>
</div>
]]></content:encoded>
    </item>
    <item>
      <title>Hacking tw2 resource injection</title>
      <link>http://threebean.org/blog/2011/12/13/hacking-tw2-resource-injection/</link>
      <pubDate>Tue, 13 Dec 2011 05:44:53 EST</pubDate>
      <category><![CDATA[python]]></category>
      <category><![CDATA[toscawidgets]]></category>
      <category><![CDATA[turbogears]]></category>
      <guid>http://threebean.org/blog/2011/12/13/hacking-tw2-resource-injection/</guid>
      <description>Hacking tw2 resource injection</description>
      <content:encoded><![CDATA[
<p>Tonight, <em>VooDooNOFX</em> was asking in IRC in #turbogears how to disable
the injection of jquery.js by tw2.jquery into her/his TG2 app.  Using the
<code>inject_resources=False</code> middleware config value wouldn't cut it,
since she/he wanted tw2 to inject all <em>other</em> resources, they were
loading jQuery via google CDN beforehand and tw2's injection was clobbering
their code.</p>

<p>I came up with the following hack to <code>myapp/lib/base.py</code> which
will remove tw2.jquery.jquery_js from the list of resources tw2 would inject
into each page served by a TG2.1 app.</p>

<p>At the top of <code>myapp/lib/base.py</code> import:</p>

<div class="pygments_monokai"><pre><span class="kn">import</span> <span class="nn">tw2.core.core</span>
<span class="kn">import</span> <span class="nn">tw2.jquery</span>
</pre></div>




<p>and then replace:</p>


<div class="pygments_monokai"><pre>        <span class="k">return</span> <span class="n">TGController</span><span class="o">.</span><span class="n">__call__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">environ</span><span class="p">,</span> <span class="n">start_response</span><span class="p">)</span>
</pre></div>




<p>with the following:</p>


<div class="pygments_monokai"><pre>        <span class="n">stream</span> <span class="o">=</span> <span class="n">TGController</span><span class="o">.</span><span class="n">__call__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">environ</span><span class="p">,</span> <span class="n">start_response</span><span class="p">)</span>

        <span class="c"># Disable the injection of tw2.jquery</span>
        <span class="n">offending_link</span> <span class="o">=</span> <span class="n">tw2</span><span class="o">.</span><span class="n">jquery</span><span class="o">.</span><span class="n">jquery_js</span><span class="o">.</span><span class="n">req</span><span class="p">()</span><span class="o">.</span><span class="n">link</span>
        <span class="n">local</span> <span class="o">=</span> <span class="n">tw2</span><span class="o">.</span><span class="n">core</span><span class="o">.</span><span class="n">core</span><span class="o">.</span><span class="n">request_local</span><span class="p">()</span>
        <span class="n">local</span><span class="p">[</span><span class="s">&#39;resources&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span>
            <span class="n">r</span> <span class="k">for</span> <span class="n">r</span> <span class="ow">in</span> <span class="n">local</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">&#39;resources&#39;</span><span class="p">,</span> <span class="nb">list</span><span class="p">())</span> <span class="k">if</span> <span class="n">r</span><span class="o">.</span><span class="n">link</span> <span class="o">!=</span> <span class="n">offending_link</span>
        <span class="p">]</span>

        <span class="k">return</span> <span class="n">stream</span>
</pre></div>




<p>The two tricks to this are</p>

<p><ul><li>Simply knowing that tw2 resources register themselves with the
  'request_local' object and that during the return-phase of the WSGI pipeline,
  the tw2 middleware refers to that list when injecting
  resources</li><li>Figuring out where in a TG2 app's request flow to place the
  call to alter that object <strong>after</strong> all widgets that might
  register jquery have declared their resources but <strong>before</strong> the
  resources list is injected into the output stream.</li></ul></p>

<p>We came out of it with <a
  href="https://bitbucket.org/paj/tw2core/issue/94/jquery-resource-injection">a
  bug filed in the tw2 issue tracker</a> so we can take care of it properly in
the future.</p>
]]></content:encoded>
    </item>
    <item>
      <title>raptorizemw - Fact:  Every WSGI app is better with a raptor.</title>
      <link>http://threebean.org/blog/2011/10/03/raptorizemw-fact-every-wsgi-app-is-better-with-a-raptor/</link>
      <pubDate>Mon, 03 Oct 2011 20:35:34 EDT</pubDate>
      <category><![CDATA[python]]></category>
      <category><![CDATA[lulz]]></category>
      <category><![CDATA[pyramid]]></category>
      <category><![CDATA[turbogears]]></category>
      <guid>http://threebean.org/blog/2011/10/03/raptorizemw-fact-every-wsgi-app-is-better-with-a-raptor/</guid>
      <description>raptorizemw - Fact:  Every WSGI app is better with a raptor.</description>
      <content:encoded><![CDATA[
<p>It's done.  An over-engineered <a
  href="http://wsgi.readthedocs.org/en/latest/" target="_blank">WSGI
  middleware</a> component that adds a velociraptor to every page served.  Fact:
Every WSGI app is better with a raptor.</p>

<p>It's called <a href="http://pypi.python.org/pypi/raptorizemw">raptorizemw</a>
(pronounced "awesome") and the only way to use it is in production.</p>
]]></content:encoded>
    </item>
    <item>
      <title>TurboGears 2.1 and Foreclosures (more empty houses than homeless people)</title>
      <link>http://threebean.org/blog/2011/09/24/turbogears-2-1-and-foreclosures-more-empty-houses-than-homeless-people/</link>
      <pubDate>Sat, 24 Sep 2011 16:01:21 EDT</pubDate>
      <category><![CDATA[python]]></category>
      <category><![CDATA[politics]]></category>
      <category><![CDATA[turbogears]]></category>
      <guid>http://threebean.org/blog/2011/09/24/turbogears-2-1-and-foreclosures-more-empty-houses-than-homeless-people/</guid>
      <description>TurboGears 2.1 and Foreclosures (more empty houses than homeless people)</description>
      <content:encoded><![CDATA[
<p>I wrote an app that scrapes foreclosure data from my county of residence and
plots it six ways from Sunday in a TurboGears2.1 app.  You can find it at <a
  href="http://monroe-threebean.rhcloud.com/">http://monroe-threebean.rhcloud.com/</a>,
hosted on redhat's openshift cloud.</p>

<p>It's used by activists with <a href="http://takebackroc.rocus.org/">Take Back
  the Land, Rochester</a> and my local branch of the <a
  href="http://rochesteriso.org">ISO</a> to find upcoming evictions before they
happen and organize the neighborhoods to stop the shuttering of homes.  Get a
hundred people at the door of the house before the cops come, and no-one is
getting evicted (we've had <a
  href="http://socialistworker.org/2011/05/11/activist-takes-back-her-home">some</a>
<a
  href="http://socialistworker.org/2011/08/23/no-eviction-for-cathy-lennon">successes</a>).</p>

<p>We're living in some absurd times where banks got bailed out by the trillions
yet still get to collect on our student debt and mortgages.  Most of us are
being ruined.  If you're not, then your neighbor is.</p>

<P>If you're in Boston, check out <a></a>Vida Urbana</a> or if you're in
Chicago, check out the <a href="http://chicagoantieviction.org/">Chicago
  Anti-Eviction Campaign</a>.  Anywhere you go, check out the <a
  href="http://internationalsocialist.org">ISO</a>.</p>

<p><a href="http://github.com/ralphbean/monroe">Fork my code</a>, port it to
your home town, and start organizing!</p>
]]></content:encoded>
    </item>
    <item>
      <title>Using repoze.who.plugins.ldap in a TurboGears 2.1 app</title>
      <link>http://threebean.org/blog/2011/07/19/using-repoze-who-plugins-ldap-in-a-turbogears-2-1-app/</link>
      <pubDate>Tue, 19 Jul 2011 16:24:24 EDT</pubDate>
      <category><![CDATA[python]]></category>
      <category><![CDATA[turbogears]]></category>
      <category><![CDATA[ldap]]></category>
      <guid>http://threebean.org/blog/2011/07/19/using-repoze-who-plugins-ldap-in-a-turbogears-2-1-app/</guid>
      <description>Using repoze.who.plugins.ldap in a TurboGears 2.1 app</description>
      <content:encoded><![CDATA[

<p>Often, you will need to authenticate against <code>ldap</code> in your
webapp.  Here's how to make that happen in a freshly quickstarted TurboGears 2.1
app.</p>

<h2>Setting up your environment</h2>

<div class="pygments_monokai"><pre>mkvirtualenv --no-site-packages repoze-ldap-app
pip install tg.devtools
paster quickstart   <span class="c"># call the app repoze-ldap-app, yes to mako and auth</span>
<span class="nb">cd </span>repoze-ldap-app
python setup.py develop
pip install genshi  <span class="c"># This is a workaround.</span>
paster setup-app development.ini
paster serve development.ini  <span class="c"># To test if the basic app works.</span>
</pre></div>




<p>Point your browser at <a
  href="http://localhost:8080">http://localhost:8080</a> just to make sure
everything is cool.</p>

<h2>Setting up <code>repoze.who.plugins.ldap</code></h2>
<p><strong>Add</strong> the following line to the <code>install_requires</code>
list in <code>setup.py</code>:</p>


<div class="pygments_monokai"><pre>    <span class="s">&quot;repoze.who.plugins.ldap&quot;</span><span class="p">,</span>
</pre></div>




<p>Run <code>python setup.py develop</code> to install the newly listed repoze
plugin.</p>

<P><strong>Add</strong> the following four lines to <code>development.ini</code>
which reference an as yet unwritten secondary configuration file.  Place them
just above the <code>sqlalchemy.url=...</code> lines:</p>


<div class="pygments_monokai"><pre># Repoze.who stuff
who.config_file = %(here)s/who.ini
who.log_level = INFO
who.log_stream = stdout
</pre></div>




<p><strong>Create</strong> a new file <code>who.ini</code> with the following
contents:</p>


<div class="pygments_monokai"><pre># This file is adapted from:
# http://threebean.org/blog/2011/07/19/using-repoze-who-plugins-ldap-in-a-turbogears-2-1-app/
# which has been adapted from:
# http://static.repoze.org/whodocs/#middleware-configuration-via-config-file
# which has been adapted from:
# http://code.gustavonarea.net/repoze.who.plugins.ldap/Using.html

[plugin:friendlyform]
use = repoze.who.plugins.friendlyform:FriendlyFormPlugin
login_form_url= /login
login_handler_path = /login_handler
logout_handler_path = /logout_handler
rememberer_name = auth_tkt
post_login_url = /post_login
post_logout_url = /post_logout

[plugin:auth_tkt]
use = repoze.who.plugins.auth_tkt:make_plugin
secret = omg_this_is_so_secret_lololololol_2938485#butts

[plugin:ldap_auth]
# Here I use my own ldap_auth, since by default ldap allows affirmative
# authentication with *no password specified*.  That is lame; I override it.
use = repozeldapapp.lib.auth:ReconnectingAuthenticatorPlugin

# This is the URI of wherever you want to connect to.  I work at RIT.
ldap_connection = ldap://ldap.rit.edu

# This is the base of the &#39;distinguished names&#39; (DNs) of persons in your
# particular LDAP instance.  It will vary from server to server.
base_dn = ou=People,dc=rit,dc=edu

[plugin:ldap_attributes]
# I also do some overriding for more security in how I get attributes for
# users.
use = repozeldapapp.lib.auth:ReconnectingLDAPAttrsPlugin
ldap_connection = ldap://ldap.rit.edu

[general]
request_classifier = repoze.who.classifiers:default_request_classifier
challenge_decider = repoze.who.classifiers:default_challenge_decider

[mdproviders]
plugins =
    ldap_attributes

[identifiers]
plugins =
    friendlyform;browser
    auth_tkt

[authenticators]
plugins =
    ldap_auth

[challengers]
plugins =
    friendlyform;browser
</pre></div>




<p><strong>Create</strong> another new file
<code>repozeldapapp/lib/auth.py</code> with the following contents:</p>


<div class="pygments_monokai"><pre><span class="kn">from</span> <span class="nn">repoze.who.plugins.ldap</span> <span class="kn">import</span> <span class="p">(</span>
    <span class="n">LDAPAttributesPlugin</span><span class="p">,</span> <span class="n">LDAPAuthenticatorPlugin</span>
<span class="p">)</span>
<span class="kn">import</span> <span class="nn">ldap</span>


<span class="k">class</span> <span class="nc">URISaver</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
    <span class="sd">&quot;&quot;&quot; Saves the ldap_connection str given to repoze authn and authz &quot;&quot;&quot;</span>
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">):</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">uri</span> <span class="o">=</span> <span class="n">kw</span><span class="p">[</span><span class="s">&#39;ldap_connection&#39;</span><span class="p">]</span>
        <span class="nb">super</span><span class="p">(</span><span class="n">URISaver</span><span class="p">,</span> <span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="n">__init__</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">)</span>


<span class="k">class</span> <span class="nc">ReconnectingLDAPAttrsPlugin</span><span class="p">(</span><span class="n">LDAPAttributesPlugin</span><span class="p">,</span> <span class="n">URISaver</span><span class="p">):</span>
    <span class="sd">&quot;&quot;&quot; Gets attributes from LDAP.  Refreshes connection if stale. &quot;&quot;&quot;</span>

    <span class="k">def</span> <span class="nf">add_metadata</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">environ</span><span class="p">,</span> <span class="n">identity</span><span class="p">):</span>
        <span class="sd">&quot;&quot;&quot; Add ldap attributes to the `identity` entry. &quot;&quot;&quot;</span>

        <span class="k">try</span><span class="p">:</span>
            <span class="k">return</span> <span class="nb">super</span><span class="p">(</span><span class="n">ReconnectingLDAPAttrsPlugin</span><span class="p">,</span> <span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="n">add_metadata</span><span class="p">(</span>
                <span class="n">environ</span><span class="p">,</span> <span class="n">identity</span><span class="p">)</span>
        <span class="k">except</span> <span class="ne">Exception</span><span class="p">,</span> <span class="n">e</span><span class="p">:</span>
            <span class="k">print</span> <span class="s">&quot;FAILED TO CONNECT TO LDAP 1 : &quot;</span> <span class="o">+</span> <span class="nb">str</span><span class="p">(</span><span class="n">e</span><span class="p">)</span>
            <span class="k">print</span> <span class="s">&quot;Retrying...&quot;</span>
            <span class="bp">self</span><span class="o">.</span><span class="n">ldap_connection</span> <span class="o">=</span> <span class="n">ldap</span><span class="o">.</span><span class="n">initialize</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">uri</span><span class="p">)</span>
            <span class="k">return</span> <span class="nb">super</span><span class="p">(</span><span class="n">ReconnectingLDAPAttrsPlugin</span><span class="p">,</span> <span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="n">add_metadata</span><span class="p">(</span>
                <span class="n">environ</span><span class="p">,</span> <span class="n">identity</span><span class="p">)</span>


<span class="k">class</span> <span class="nc">ReconnectingAuthenticatorPlugin</span><span class="p">(</span><span class="n">LDAPAuthenticatorPlugin</span><span class="p">,</span> <span class="n">URISaver</span><span class="p">):</span>
    <span class="sd">&quot;&quot;&quot; Authenticates against LDAP.</span>

<span class="sd">    - Refreshes connection if stale.</span>
<span class="sd">    - Denies anonymously-authenticated users</span>

<span class="sd">    &quot;&quot;&quot;</span>

    <span class="k">def</span> <span class="nf">authenticate</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">environ</span><span class="p">,</span> <span class="n">identity</span><span class="p">):</span>
        <span class="sd">&quot;&quot;&quot; Extending the repoze.who.plugins.ldap plugin to make it much</span>
<span class="sd">        more secure. &quot;&quot;&quot;</span>

        <span class="n">res</span> <span class="o">=</span> <span class="bp">None</span>

        <span class="k">try</span><span class="p">:</span>
            <span class="c"># This is unbelievable.  Without this, ldap will</span>
            <span class="c">#   let you bind anonymously</span>
            <span class="k">if</span> <span class="ow">not</span> <span class="n">identity</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">&#39;password&#39;</span><span class="p">,</span> <span class="bp">None</span><span class="p">):</span>
                <span class="k">return</span> <span class="bp">None</span>
            <span class="k">try</span><span class="p">:</span>
                <span class="n">dn</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_get_dn</span><span class="p">(</span><span class="n">environ</span><span class="p">,</span> <span class="n">identity</span><span class="p">)</span>
            <span class="k">except</span> <span class="p">(</span><span class="ne">KeyError</span><span class="p">,</span> <span class="ne">TypeError</span><span class="p">,</span> <span class="ne">ValueError</span><span class="p">):</span>
                <span class="k">return</span> <span class="bp">None</span>

            <span class="n">res</span> <span class="o">=</span> <span class="nb">super</span><span class="p">(</span><span class="n">ReconnectingAuthenticatorPlugin</span><span class="p">,</span> <span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="n">authenticate</span><span class="p">(</span>
                <span class="n">environ</span><span class="p">,</span> <span class="n">identity</span><span class="p">)</span>

            <span class="c"># Sanity check here (for the same reason as the above check)</span>
            <span class="k">if</span> <span class="s">&quot;dn:</span><span class="si">%s</span><span class="s">&quot;</span> <span class="o">%</span> <span class="n">dn</span> <span class="o">!=</span> <span class="bp">self</span><span class="o">.</span><span class="n">ldap_connection</span><span class="o">.</span><span class="n">whoami_s</span><span class="p">():</span>
                <span class="k">return</span> <span class="bp">None</span>

        <span class="k">except</span> <span class="n">ldap</span><span class="o">.</span><span class="n">LDAPError</span><span class="p">,</span> <span class="n">e</span><span class="p">:</span>
            <span class="k">print</span> <span class="s">&quot;FAILED TO CONNECT TO LDAP 2 : &quot;</span> <span class="o">+</span> <span class="nb">str</span><span class="p">(</span><span class="n">e</span><span class="p">)</span>
            <span class="k">print</span> <span class="s">&quot;Retrying...&quot;</span>
            <span class="bp">self</span><span class="o">.</span><span class="n">ldap_connection</span> <span class="o">=</span> <span class="n">ldap</span><span class="o">.</span><span class="n">initialize</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">uri</span><span class="p">)</span>

        <span class="k">return</span> <span class="n">res</span>
</pre></div>




<p>Finally, do two things to
<code>repozeldapapp/config/middleware.py</code>.</p>

<P><strong>Edit</strong> it and at the top of the file add:</p>


<div class="pygments_monokai"><pre><span class="kn">from</span> <span class="nn">repoze.who.config</span> <span class="kn">import</span> <span class="n">make_middleware_with_config</span>
</pre></div>




<p><strong>Add</strong> the following inside the <code>make_app(...)</code>
function, just below the comment line about Wrap your base TurboGears 2..., like
so:</p>


<div class="pygments_monokai"><pre>    <span class="c"># Wrap your base TurboGears 2 application with custom middleware here</span>
    <span class="n">app</span> <span class="o">=</span> <span class="n">make_middleware_with_config</span><span class="p">(</span>
        <span class="n">app</span><span class="p">,</span> <span class="n">global_conf</span><span class="p">,</span>
        <span class="n">app_conf</span><span class="p">[</span><span class="s">&#39;who.config_file&#39;</span><span class="p">],</span>
        <span class="n">app_conf</span><span class="p">[</span><span class="s">&#39;who.log_stream&#39;</span><span class="p">],</span>
        <span class="n">app_conf</span><span class="p">[</span><span class="s">&#39;who.log_level&#39;</span><span class="p">])</span>
</pre></div>




<h2>Give it a test</h2>
<p>Restart the <code>paster</code> server and reload <a
  href="http://localhost:8080">http://localhost:8080</a>.  Try logging in as a
user in your ldap instance and you should be all gravy.</p>

<a href="http://www.flickr.com/photos/deathbeforedisco/3608101114"><img alt="You should be all gravy." height="192" src="http://farm4.static.flickr.com/3393/3608101114_22a355e9a5_b.jpg" title="You should be all gravy." width="256" /></a>
]]></content:encoded>
    </item>
    <item>
      <title>Tutorial -- melting your face off with tw2 and TurboGears2.1</title>
      <link>http://threebean.org/blog/2011/04/30/tutorial-melting-your-face-off-with-tw2-and-turbogears2-1/</link>
      <pubDate>Sat, 30 Apr 2011 06:39:36 EDT</pubDate>
      <category><![CDATA[python]]></category>
      <category><![CDATA[toscawidgets]]></category>
      <category><![CDATA[turbogears]]></category>
      <guid>http://threebean.org/blog/2011/04/30/tutorial-melting-your-face-off-with-tw2-and-turbogears2-1/</guid>
      <description>Tutorial -- melting your face off with tw2 and TurboGears2.1</description>
      <content:encoded><![CDATA[
<div class="wp-caption alignright" id="attachment_269" style="width: 310px;"><a
    href="http://threebean.files.wordpress.com/2011/04/crazyfort.jpg"><img
    alt="" class="size-medium wp-image-269" height="225"
    src="http://threebean.files.wordpress.com/2011/04/crazyfort.jpg?w=300&#038;h=225"
    title="crazyfort" width="300" /></a><p
  class="wp-caption-text"><div><span>Maunsell Fort tower</span> (<a
      href="http://www.geograph.org.uk/profile/850"
      rel="cc:attributionurl">Hywel Williams</a>) / <a
      href="http://creativecommons.org/licenses/by-sa/2.0/" rel="license">CC
      BY-SA 2.0</a></div></div>


<p><strong><em>Get the source:</em></strong> if you don't want to read through
this, you can get the entire source for this tutorial <a
  href="https://github.com/ralphbean/tw2-facemelt-tg2.1" target="_blank">here on
  my github account</a>.</p>

---

<p>Today it's an epic data widgets tutorial!  I've been working on a lot of
stuff recently, including <a
  href="http://groups.google.com/group/toscawidgets-discuss/browse_thread/thread/e13af6f62464e54e"
  target="_blank">trying to pull some of the more quiet tw2 developers back
  together</a>; I hope that this fits into that scheme and to at some point link
directly to this tutorial from the <code>tw2</code> documentation.</p>

<p>This tutorial won't introduce you to any of <code>tw2.core</code>
fundamentals directly, but it <strong>will</strong> show off some of the
<em>flashier</em> and <em>data-driven</em> widgets.  If it gets you real hot,
check out some of my other tw2+TurboGears2.1 tutorials <a
  href="http://threebean.org/blog/2011/03/06/sqlalchemy-the-javascript-infovis-toolkit-jit/"
  target="_blank">here</a> and <a
  href="http://threebean.org/blog/2010/10/24/python-wsgi-protovis-barcamproc-fall-2010/"
  target="_blank">here</a> and my tw2+Pyramid tutorial <a
  href="http://threebean.org/blog/2011/03/07/sqlaradialgraph-in-a-pyramid-app/"
  target="_blank">here</a>, but enough of that, let's cut to the chase:</p>

<h2>Agenda</h2>
<p>We're going to build an TurboGears2.1 application from start to finish that
logs the IP of every request made of it and displays those hits with a couple of
fancy-ass <code>tw2</code> widgets.  Here's what it's going to take:</p>

<ul>
<li>Getting TurboGears2.1 installed and running</li>
<li>Setting up a data backend</li>
<li>"Automatically" listing db entries with <code>tw2.jqplugins.jqgrid:SQLAjqGridWidget</code></li>
<li>Plotting server history with <code>tw2.jqplugins.jqplot:JQPlotWidget</code></li>
<li>Making the layout look like <a href="http://google.com/ig" target="_blank">http://google.com/ig</a> with <code>tw2.jqplugins.portlets</code></li>
</ul>

<h2>1.  Getting TurboGears2.1 installed and running</h2>
<p><strong>Install</strong> (if you haven't already) <a
  href="http://www.doughellmann.com/projects/virtualenvwrapper/"
  target="_blank">virtualenvwrapper</a>.  It's awesome and you should use it
always.</p>

<p>Next, <strong>Open</strong> up your favorite terminal and do the
following:</p>

<div class="pygments_monokai"><pre>% mkdir tw2-facemelt-tg2.1 &amp;amp;&amp;amp; cd tw2-facemelt-tg2.1
% mkvirtualenv --no-site-packages --distribute tw2-facemelt-tg2.1
% pip install --use-mirrors tg.devtools
% paster quickstart
        Enter project name: tw2-facemelt-tg2.1
        Enter package name [tw2facemelttg21]:
        Would you prefer mako templates? (yes/[no]): yes
        Do you need authentication and authorization in this project? ([yes]/no): yes
% cd tw2-facemelt-tg2.1
</pre></div>




<p>At this point we need to modify the freshly quickstarted TurboGears 2.1
project.  There's a bug there!  We said want to use <code>mako</code> templates,
so TG2.1 isn't configured to install <code>genshi</code> but it
<strong>does</strong> include references to the
<code>tgext.admin.controller:AdminController</code> which references
<code>genshi</code> but doesn't list its dependency and therefore doesn't
install it.  That's fine, we'll just remove the references to the
<code>AdminController</code> since we won't be using it, anyways.</p>

<p><strong>Remove</strong> the following three lines from
<code>tw2facemelttg21/controllers/root.py</code>:</p>


<div class="pygments_monokai"><pre><span class="kn">from</span> <span class="nn">tgext.admin.tgadminconfig</span> <span class="kn">import</span> <span class="n">TGAdminConfig</span>
<span class="kn">from</span> <span class="nn">tgext.admin.controller</span> <span class="kn">import</span> <span class="n">AdminController</span>

<span class="o">...</span>

    <span class="n">admin</span> <span class="o">=</span> <span class="n">AdminController</span><span class="p">(</span><span class="n">model</span><span class="p">,</span> <span class="n">DBSession</span><span class="p">,</span> <span class="n">config_type</span><span class="o">=</span><span class="n">TGAdminConfig</span><span class="p">)</span>
</pre></div>




<p>While we're at it, let's turn on the <code>tw2</code> WSGI middleware.</p>

<p>Edit <code>tw2facemelttg21/config/app_cfg.py</code> and <strong>add</strong>
the following two lines at the bottom:</p>


<div class="pygments_monokai"><pre><span class="n">base_config</span><span class="o">.</span><span class="n">use_toscawidgets</span> <span class="o">=</span> <span class="bp">False</span>
<span class="n">base_config</span><span class="o">.</span><span class="n">use_toscawidgets2</span> <span class="o">=</span> <span class="bp">True</span>
</pre></div>




<p>It should be noted that toscawidgets1 (setup by TG2.1 by default) and
toscawidgets2 can peacefully coexist in a WSGI app but I'm a <code>tw2</code>
purist.  After all, <a href="https://github.com/ralphbean/tw2-benchmark"
  target="_blank">tw2 is faster</a>.</p>

<p>At this point you should be able to run your TG2.1 quickstarted app.
<strong>Run</strong> the following:</p>


<div class="pygments_monokai"><pre>% pip install --use-mirrors -e .
% paster setup-app development.ini
% paster serve development.ini
</pre></div>




<p>And <strong>visit</strong> <a href="http://localhost:8080"
  target="_blank">http://localhost:8080</a> to verify that all this worked.</p>

<h2>2.  Setting up a data backend</h2>

<p><strong>Create</strong> a file at
<code>tw2facemelttg21/models/bloglog.py</code> with the following contents:</p>


<div class="pygments_monokai"><pre><span class="c"># -*- coding: utf-8 -*-</span>
<span class="sd">&quot;&quot;&quot; Logs of Bob Loblaw&#39;s Law Blog &quot;&quot;&quot;</span>

<span class="kn">from</span> <span class="nn">sqlalchemy</span> <span class="kn">import</span> <span class="o">*</span>
<span class="kn">from</span> <span class="nn">sqlalchemy.orm</span> <span class="kn">import</span> <span class="n">mapper</span><span class="p">,</span> <span class="n">relation</span>
<span class="kn">from</span> <span class="nn">sqlalchemy</span> <span class="kn">import</span> <span class="n">Table</span><span class="p">,</span> <span class="n">ForeignKey</span><span class="p">,</span> <span class="n">Column</span>
<span class="kn">from</span> <span class="nn">sqlalchemy.types</span> <span class="kn">import</span> <span class="n">Integer</span><span class="p">,</span> <span class="n">Unicode</span>
<span class="c">#from sqlalchemy.orm import relation, backref</span>

<span class="kn">from</span> <span class="nn">datetime</span> <span class="kn">import</span> <span class="n">datetime</span>

<span class="kn">from</span> <span class="nn">tw2facemelttg21.model</span> <span class="kn">import</span> <span class="n">DeclarativeBase</span><span class="p">,</span> <span class="n">metadata</span><span class="p">,</span> <span class="n">DBSession</span>


<span class="k">class</span> <span class="nc">ServerHit</span><span class="p">(</span><span class="n">DeclarativeBase</span><span class="p">):</span>
    <span class="n">__tablename__</span> <span class="o">=</span> <span class="s">&#39;server_hit&#39;</span>

    <span class="nb">id</span> <span class="o">=</span> <span class="n">Column</span><span class="p">(</span><span class="n">Integer</span><span class="p">,</span> <span class="n">primary_key</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
    <span class="n">timestamp</span> <span class="o">=</span> <span class="n">Column</span><span class="p">(</span><span class="n">DateTime</span><span class="p">,</span> <span class="n">nullable</span><span class="o">=</span><span class="bp">False</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">)</span>

    <span class="n">remote_addr</span> <span class="o">=</span> <span class="n">Column</span><span class="p">(</span><span class="n">Unicode</span><span class="p">(</span><span class="mi">15</span><span class="p">),</span> <span class="n">nullable</span><span class="o">=</span><span class="bp">False</span><span class="p">)</span>

    <span class="n">path_info</span> <span class="o">=</span> <span class="n">Column</span><span class="p">(</span><span class="n">Unicode</span><span class="p">(</span><span class="mi">1024</span><span class="p">),</span> <span class="n">nullable</span><span class="o">=</span><span class="bp">False</span><span class="p">)</span>
    <span class="n">query_string</span> <span class="o">=</span> <span class="n">Column</span><span class="p">(</span><span class="n">Unicode</span><span class="p">(</span><span class="mi">1024</span><span class="p">),</span> <span class="n">nullable</span><span class="o">=</span><span class="bp">False</span><span class="p">)</span>
</pre></div>




<p><strong>Edit</strong> the module-level file
<code>tw2facemelttg21/models/__init__.py</code> and <strong>uncomment</strong>
the following line:</p>


<div class="pygments_monokai"><pre><span class="n">DeclarativeBase</span><span class="o">.</span><span class="n">query</span> <span class="o">=</span> <span class="n">DBSession</span><span class="o">.</span><span class="n">query_property</span><span class="p">()</span>
</pre></div>




<p><strong>Add</strong> the following line to the very bottom of <em>the same
  file</em> (<code>tw2facemelttg21/models/__init__.py</code>):</p>

<div class="pygments_monokai"><pre><span class="kn">from</span> <span class="nn">bloglog</span> <span class="kn">import</span> <span class="n">ServerHit</span>
</pre></div>




<p>The data model should be good to go now, but let's add one little piece of
code --- a hook --- that will populate the <code>server_hit</code> table as the
app runs.</p>

<p><strong>Edit</strong> <code>tw2facemelttg21/lib/base.py</code> and add the
following seven-line chunk just inside the <code>__call__(...)</code> method of
your <code>BaseController</code> </p>


<div class="pygments_monokai"><pre>    <span class="k">def</span> <span class="nf">__call__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">environ</span><span class="p">,</span> <span class="n">start_response</span><span class="p">):</span>
        <span class="sd">&quot;&quot;&quot;Invoke the Controller&quot;&quot;&quot;</span>
        <span class="c"># TGController.__call__ dispatches to the Controller method</span>
        <span class="c"># the request is routed to. This routing information is</span>
        <span class="c"># available in environ[&#39;pylons.routes_dict&#39;]</span>

        <span class="n">entry</span> <span class="o">=</span> <span class="n">model</span><span class="o">.</span><span class="n">ServerHit</span><span class="p">(</span>
            <span class="n">remote_addr</span><span class="o">=</span><span class="n">environ</span><span class="p">[</span><span class="s">&#39;REMOTE_ADDR&#39;</span><span class="p">],</span>
            <span class="n">path_info</span><span class="o">=</span><span class="n">environ</span><span class="p">[</span><span class="s">&#39;PATH_INFO&#39;</span><span class="p">],</span>
            <span class="n">query_string</span><span class="o">=</span><span class="n">environ</span><span class="p">[</span><span class="s">&#39;QUERY_STRING&#39;</span><span class="p">],</span>
        <span class="p">)</span>
        <span class="n">model</span><span class="o">.</span><span class="n">DBSession</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">entry</span><span class="p">)</span>
        <span class="n">model</span><span class="o">.</span><span class="n">DBSession</span><span class="o">.</span><span class="n">flush</span><span class="p">()</span>

        <span class="n">request</span><span class="o">.</span><span class="n">identity</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">environ</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">&#39;repoze.who.identity&#39;</span><span class="p">)</span>
        <span class="n">tmpl_context</span><span class="o">.</span><span class="n">identity</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">identity</span>
        <span class="k">return</span> <span class="n">TGController</span><span class="o">.</span><span class="n">__call__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">environ</span><span class="p">,</span> <span class="n">start_response</span><span class="p">)</span>
</pre></div>





<p>Now <strong>blow away</strong> and <strong>recreate</strong> your old sqlite
database by typing the following into your trusty terminal:</p>


<div class="pygments_monokai"><pre>% rm devdata.db
% paster setup-app development.ini
% paster serve development.ini
</pre></div>




<p>Pray for no bugs as you <strong>revisit</strong> <a
  href="http://localhost:8080" target="_blank">http://localhost:8080</a>.
Reload the page a few times and just double-check that your database has entries
in it now by running <code>sqlite3 devdata.db</code>.  Issue the command
<code>select * from server_hit;</code> and you should see all your page requests
listed.</p>

<p>Cool?  Cool.</p>

<p>(<em><strong>Note to the brave</strong></em>:  If we wanted to be
<strong>really</strong> awesome, we would write WSGI <em>middleware</em> to do
our request-logging.)</p>

<h2>3.  "Automatically" listing db entries with <code>tw2.jqplugins.jqgrid:SQLAjqGridWidget</code></h2>

<p><strong>Create</strong> a new file <code>tw2facemelttg21/widgets.py</code>
with the following content:</p>


<div class="pygments_monokai"><pre><span class="kn">import</span> <span class="nn">tw2facemelttg21.model</span> <span class="kn">as</span> <span class="nn">model</span>
<span class="kn">from</span> <span class="nn">tw2.jqplugins.jqgrid</span> <span class="kn">import</span> <span class="n">SQLAjqGridWidget</span>

<span class="k">class</span> <span class="nc">LogGrid</span><span class="p">(</span><span class="n">SQLAjqGridWidget</span><span class="p">):</span>
    <span class="nb">id</span> <span class="o">=</span> <span class="s">&#39;awesome-loggrid&#39;</span>
    <span class="n">entity</span> <span class="o">=</span> <span class="n">model</span><span class="o">.</span><span class="n">ServerHit</span>
    <span class="n">excluded_columns</span> <span class="o">=</span> <span class="p">[</span><span class="s">&#39;id&#39;</span><span class="p">]</span>
    <span class="n">datetime_format</span> <span class="o">=</span> <span class="s">&quot;</span><span class="si">%x</span><span class="s"> </span><span class="si">%X</span><span class="s">&quot;</span>

    <span class="n">prmFilter</span> <span class="o">=</span> <span class="p">{</span><span class="s">&#39;stringResult&#39;</span><span class="p">:</span> <span class="bp">True</span><span class="p">,</span> <span class="s">&#39;searchOnEnter&#39;</span><span class="p">:</span> <span class="bp">False</span><span class="p">}</span>

    <span class="n">options</span> <span class="o">=</span> <span class="p">{</span>
        <span class="s">&#39;pager&#39;</span><span class="p">:</span> <span class="s">&#39;awesome-loggrid_pager&#39;</span><span class="p">,</span>
        <span class="s">&#39;url&#39;</span><span class="p">:</span> <span class="s">&#39;/jqgrid/&#39;</span><span class="p">,</span>
        <span class="s">&#39;rowNum&#39;</span><span class="p">:</span><span class="mi">15</span><span class="p">,</span>
        <span class="s">&#39;rowList&#39;</span><span class="p">:[</span><span class="mi">15</span><span class="p">,</span><span class="mi">150</span><span class="p">,</span> <span class="mi">1500</span><span class="p">],</span>
        <span class="s">&#39;viewrecords&#39;</span><span class="p">:</span><span class="bp">True</span><span class="p">,</span>
        <span class="s">&#39;imgpath&#39;</span><span class="p">:</span> <span class="s">&#39;scripts/jqGrid/themes/green/images&#39;</span><span class="p">,</span>
        <span class="s">&#39;shrinkToFit&#39;</span><span class="p">:</span> <span class="bp">True</span><span class="p">,</span>
        <span class="s">&#39;height&#39;</span><span class="p">:</span> <span class="s">&#39;auto&#39;</span><span class="p">,</span>
    <span class="p">}</span>
</pre></div>




<p><strong>Pull</strong> the <code>LogGrid</code> widget into your controller by
importing it at the top of <code>tw2facemelttg21/controllers/root.py</code>
with:</p>

<div class="pygments_monokai"><pre><span class="kn">from</span> <span class="nn">tw2facemelttg21.widgets</span> <span class="kn">import</span> <span class="n">LogGrid</span>
</pre></div>




<p><strong>Modify</strong> the <code>index</code> method of your
<code>RootController</code> in <code>tw2facemelttg21/controllers/root.py</code>
so that it looks like:</p>


<div class="pygments_monokai"><pre><span class="nd">@expose</span><span class="p">(</span><span class="s">&#39;tw2facemelttg21.templates.index&#39;</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">index</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
    <span class="sd">&quot;&quot;&quot;Handle the front-page.&quot;&quot;&quot;</span>
    <span class="k">return</span> <span class="nb">dict</span><span class="p">(</span><span class="n">page</span><span class="o">=</span><span class="s">&#39;index&#39;</span><span class="p">,</span> <span class="n">gridwidget</span><span class="o">=</span><span class="n">LogGrid</span><span class="p">)</span>
</pre></div>




<p>This will make the LogGrid available in your index template under the name
<code>gridwidget</code>.  We still need to display it there.</p>

<p><strong>Edit</strong> <code>tw2facemelttg21/templates/index.mak</code> and
wipe out <em>all</em> of the content.  Replace it with <em>only</em> the
following:</p>


<div class="pygments_monokai"><pre><span class="err">&lt;</span>%inherit file=&quot;local:templates.master&quot;/&gt;
${gridwidget.display() | n}
</pre></div>




<p>Cool.</p>

<p>The <code>SQLAjqGridWidget</code> has a <code>request(...)</code> method that
does all of its magic (interrogating your <code>sqlalchemy</code> model for its
properties and values).  We still need to wire up our TurboGears app to forward
the <em>ajax<->json</em> requests to the right place.<p>

<p>To do this, <strong>add</strong> another method to your
<code>RootController</code> back in
<code>tw2facemelttg21/controllers/root.py</code> that looks like this:</p>


<div class="pygments_monokai"><pre><span class="nd">@expose</span><span class="p">(</span><span class="s">&#39;json&#39;</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">jqgrid</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
    <span class="k">return</span> <span class="n">LogGrid</span><span class="o">.</span><span class="n">request</span><span class="p">(</span><span class="n">request</span><span class="p">)</span><span class="o">.</span><span class="n">body</span>
</pre></div>




<p>Lastly, <strong>edit</strong> your <code>setup.py</code> file and
<strong>add</strong> <em>all</em> the new widget dependencies we'll need (not
just the <code>jqgrid</code>), like so:</p>


<div class="pygments_monokai"><pre>    <span class="n">install_requires</span><span class="o">=</span><span class="p">[</span>
        <span class="s">&quot;TurboGears2 &gt;= 2.1&quot;</span><span class="p">,</span>
        <span class="s">&quot;Mako&quot;</span><span class="p">,</span>
        <span class="s">&quot;zope.sqlalchemy &gt;= 0.4&quot;</span><span class="p">,</span>
        <span class="s">&quot;repoze.tm2 &gt;= 1.0a5&quot;</span><span class="p">,</span>
        <span class="s">&quot;repoze.what-quickstart&quot;</span><span class="p">,</span>
        <span class="s">&quot;repoze.what &gt;= 1.0.8&quot;</span><span class="p">,</span>
        <span class="s">&quot;repoze.what-quickstart&quot;</span><span class="p">,</span>
        <span class="s">&quot;repoze.who-friendlyform &gt;= 1.0.4&quot;</span><span class="p">,</span>
        <span class="s">&quot;repoze.what-pylons &gt;= 1.0&quot;</span><span class="p">,</span>
        <span class="s">&quot;repoze.what.plugins.sql&quot;</span><span class="p">,</span>
        <span class="s">&quot;repoze.who == 1.0.18&quot;</span><span class="p">,</span>
        <span class="s">&quot;tgext.admin &gt;= 0.3.9&quot;</span><span class="p">,</span>
        <span class="s">&quot;tw.forms&quot;</span><span class="p">,</span>
        <span class="s">&quot;tw2.jqplugins.jqgrid&quot;</span><span class="p">,</span>
        <span class="s">&quot;tw2.jqplugins.jqplot&quot;</span><span class="p">,</span>
        <span class="s">&quot;tw2.jqplugins.portlets&quot;</span><span class="p">,</span>
        <span class="p">],</span>
    <span class="n">setup_requires</span><span class="o">=</span><span class="p">[</span><span class="s">&quot;PasteScript &gt;= 1.7&quot;</span><span class="p">],</span>
</pre></div>




<p><strong>Install</strong> the newly listed dependencies by running:</p>


<div class="pygments_monokai"><pre>% python setup.py develop
% paster serve development.ini
</pre></div>




<p>...and revisit <a href="http://localhost:8080"
  target="_blank">http://localhost:8080</a>.</p>

<p>At this point, your app should look something like the following:</p>

<a href="http://threebean.files.wordpress.com/2011/04/screenshot1.png"><img alt="" class="size-medium wp-image-292" height="180" src="http://threebean.files.wordpress.com/2011/04/screenshot1.png?w=300" title="screenshot1" width="300" /></a>

<p>We should really (probably) do things as fancy as we can, and make use of
jquery-ui's themes.</p>

<p><strong>Edit</strong> <code>tw2facemelttg21/lib/base.py</code> do the
following two things:</p>

<p><strong>Add</strong> this <code>import</code> statement to the top of the
file.</p>

<div class="pygments_monokai"><pre><span class="kn">from</span> <span class="nn">tw2.jqplugins.ui</span> <span class="kn">import</span> <span class="n">set_ui_theme_name</span>
</pre></div>




<p>And <strong>invoke</strong> it from <em>inside</em> the
<code>__call__(...)</code> method of your <code>BaseController</code> like
so:</p>


<div class="pygments_monokai"><pre>    <span class="k">def</span> <span class="nf">__call__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">environ</span><span class="p">,</span> <span class="n">start_response</span><span class="p">):</span>
        <span class="sd">&quot;&quot;&quot;Invoke the Controller&quot;&quot;&quot;</span>
        <span class="c"># TGController.__call__ dispatches to the Controller method</span>
        <span class="c"># the request is routed to. This routing information is</span>
        <span class="c"># available in environ[&#39;pylons.routes_dict&#39;]</span>

        <span class="n">set_ui_theme_name</span><span class="p">(</span><span class="s">&#39;hot-sneaks&#39;</span><span class="p">)</span>

        <span class="n">entry</span> <span class="o">=</span> <span class="n">model</span><span class="o">.</span><span class="n">ServerHit</span><span class="p">(</span>
            <span class="n">remote_addr</span><span class="o">=</span><span class="n">environ</span><span class="p">[</span><span class="s">&#39;REMOTE_ADDR&#39;</span><span class="p">],</span>
            <span class="n">path_info</span><span class="o">=</span><span class="n">environ</span><span class="p">[</span><span class="s">&#39;PATH_INFO&#39;</span><span class="p">],</span>
            <span class="n">query_string</span><span class="o">=</span><span class="n">environ</span><span class="p">[</span><span class="s">&#39;QUERY_STRING&#39;</span><span class="p">],</span>
        <span class="p">)</span>
        <span class="n">model</span><span class="o">.</span><span class="n">DBSession</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">entry</span><span class="p">)</span>

        <span class="n">request</span><span class="o">.</span><span class="n">identity</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">environ</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">&#39;repoze.who.identity&#39;</span><span class="p">)</span>
        <span class="n">tmpl_context</span><span class="o">.</span><span class="n">identity</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">identity</span>
        <span class="k">return</span> <span class="n">TGController</span><span class="o">.</span><span class="n">__call__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">environ</span><span class="p">,</span> <span class="n">start_response</span><span class="p">)</span>
</pre></div>


 

<p>Nice!  But <code>hot-sneaks</code> isn't the only available theme -- you can
see a list of all of them <a
  href="https://github.com/ralphbean/tw2.jqplugins.ui/blob/master/tw2/jqplugins/ui/base.py#L37"
  target="_blank">right here</a>.</p>

<a href="http://threebean.files.wordpress.com/2011/04/screenshot2.png"><img alt="" class="size-medium wp-image-295" height="180" src="http://threebean.files.wordpress.com/2011/04/screenshot2.png?w=300" title="screenshot2" width="300" /></a>

<h2>4.  Plotting server history with <code>tw2.jqplugins.jqplot:JQPlotWidget</code></h2>

<p>This is going to be awesome.</p>

<p><strong>Add</strong> a new widget definition to
<code>tw2facemelttg21/widgets.py</code>:</p>


<div class="pygments_monokai"><pre><span class="kn">from</span> <span class="nn">tw2.jqplugins.jqplot</span> <span class="kn">import</span> <span class="n">JQPlotWidget</span>
<span class="kn">from</span> <span class="nn">tw2.jqplugins.jqplot.base</span> <span class="kn">import</span> <span class="n">categoryAxisRenderer_js</span><span class="p">,</span> <span class="n">barRenderer_js</span>
<span class="kn">from</span> <span class="nn">tw2.core</span> <span class="kn">import</span> <span class="n">JSSymbol</span>

<span class="k">class</span> <span class="nc">LogPlot</span><span class="p">(</span><span class="n">JQPlotWidget</span><span class="p">):</span>
    <span class="nb">id</span> <span class="o">=</span> <span class="s">&#39;awesome-logplot&#39;</span>
    <span class="n">interval</span> <span class="o">=</span> <span class="mi">2000</span>
    <span class="n">resources</span> <span class="o">=</span> <span class="n">JQPlotWidget</span><span class="o">.</span><span class="n">resources</span> <span class="o">+</span> <span class="p">[</span>
        <span class="n">categoryAxisRenderer_js</span><span class="p">,</span>
        <span class="n">barRenderer_js</span><span class="p">,</span>
    <span class="p">]</span>

    <span class="n">options</span> <span class="o">=</span> <span class="p">{</span>
        <span class="s">&#39;seriesDefaults&#39;</span> <span class="p">:</span> <span class="p">{</span>
            <span class="s">&#39;renderer&#39;</span><span class="p">:</span> <span class="n">JSSymbol</span><span class="p">(</span><span class="s">&#39;$.jqplot.BarRenderer&#39;</span><span class="p">),</span>
            <span class="s">&#39;rendererOptions&#39;</span><span class="p">:</span> <span class="p">{</span> <span class="s">&#39;barPadding&#39;</span><span class="p">:</span> <span class="mi">4</span><span class="p">,</span> <span class="s">&#39;barMargin&#39;</span><span class="p">:</span> <span class="mi">10</span> <span class="p">}</span>
        <span class="p">},</span>
        <span class="s">&#39;axes&#39;</span> <span class="p">:</span> <span class="p">{</span>
            <span class="s">&#39;xaxis&#39;</span><span class="p">:</span> <span class="p">{</span>
                <span class="s">&#39;renderer&#39;</span><span class="p">:</span> <span class="n">JSSymbol</span><span class="p">(</span><span class="n">src</span><span class="o">=</span><span class="s">&quot;$.jqplot.CategoryAxisRenderer&quot;</span><span class="p">),</span>
            <span class="p">},</span>
            <span class="s">&#39;yaxis&#39;</span><span class="p">:</span> <span class="p">{</span><span class="s">&#39;min&#39;</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="p">},</span>
        <span class="p">}</span>
    <span class="p">}</span>
</pre></div>



<p>Now we're going to go to town on our <code>RootController</code>.  We need
to:</p>
<ul>
<li>Make the new widget available in our template</li>
<li>Produce data for it</li>
<li>Render it in the template</li>
</ul>

<p>First, <strong>add</strong> the following imports to the top of
<code>tw2facemelttg21/controllers/root.py</code>:</p>


<div class="pygments_monokai"><pre><span class="kn">from</span> <span class="nn">tw2facemelttg21.models</span> <span class="kn">import</span> <span class="n">ServerHit</span>
<span class="kn">from</span> <span class="nn">tw2facemelttg21.widgets</span> <span class="kn">import</span> <span class="n">LogPlot</span>

<span class="kn">import</span> <span class="nn">sqlalchemy</span>
<span class="kn">import</span> <span class="nn">datetime</span>
<span class="kn">import</span> <span class="nn">time</span>
</pre></div>




<p>We're also going to need this little <code>recursive_update</code> utility to
merge the <code>options<code> <code>dict</code>s.  Just add it at the top of
    <code>tw2facemelttg21/controllers/root.py</code> as a <em>function</em>.  It
    should <strong>not</strong> be a method of <code>RootController</code>.</p>


<div class="pygments_monokai"><pre><span class="k">def</span> <span class="nf">recursive_update</span><span class="p">(</span><span class="n">d1</span><span class="p">,</span> <span class="n">d2</span><span class="p">):</span>
    <span class="sd">&quot;&quot;&quot; Little helper function that does what d1.update(d2) does,</span>
<span class="sd">    but works nice and recursively with dicts of dicts of dicts.</span>

<span class="sd">    It&#39;s not necessarily very efficient.</span>
<span class="sd">    &quot;&quot;&quot;</span>

    <span class="k">for</span> <span class="n">k</span> <span class="ow">in</span> <span class="n">d1</span><span class="o">.</span><span class="n">keys</span><span class="p">():</span>
        <span class="k">if</span> <span class="n">k</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">d2</span><span class="p">:</span>
            <span class="k">continue</span>

        <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">d1</span><span class="p">[</span><span class="n">k</span><span class="p">],</span> <span class="nb">dict</span><span class="p">)</span> <span class="ow">and</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">d2</span><span class="p">[</span><span class="n">k</span><span class="p">],</span> <span class="nb">dict</span><span class="p">):</span>
            <span class="n">d1</span><span class="p">[</span><span class="n">k</span><span class="p">]</span> <span class="o">=</span> <span class="n">recursive_update</span><span class="p">(</span><span class="n">d1</span><span class="p">[</span><span class="n">k</span><span class="p">],</span> <span class="n">d2</span><span class="p">[</span><span class="n">k</span><span class="p">])</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="n">d1</span><span class="p">[</span><span class="n">k</span><span class="p">]</span> <span class="o">=</span> <span class="n">d2</span><span class="p">[</span><span class="n">k</span><span class="p">]</span>

    <span class="k">for</span> <span class="n">k</span> <span class="ow">in</span> <span class="n">d2</span><span class="o">.</span><span class="n">keys</span><span class="p">():</span>
        <span class="k">if</span> <span class="n">k</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">d1</span><span class="p">:</span>
            <span class="n">d1</span><span class="p">[</span><span class="n">k</span><span class="p">]</span> <span class="o">=</span> <span class="n">d2</span><span class="p">[</span><span class="n">k</span><span class="p">]</span>

    <span class="k">return</span> <span class="n">d1</span>
</pre></div>



<p><strong>Add</strong> a new method to the <code>RootController</code> class
that looks like the following.  This will do all of the heavy lifty ---
producing the data for the jqplot.</p>


<div class="pygments_monokai"><pre>    <span class="k">def</span> <span class="nf">jqplot</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">days</span><span class="o">=</span><span class="mi">1</span><span class="o">/</span><span class="p">(</span><span class="mf">24.0</span><span class="p">)):</span>
        <span class="n">n_buckets</span> <span class="o">=</span> <span class="mi">15</span>
        <span class="n">now</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span>
        <span class="n">then</span> <span class="o">=</span> <span class="n">now</span> <span class="o">-</span> <span class="n">datetime</span><span class="o">.</span><span class="n">timedelta</span><span class="p">(</span><span class="n">days</span><span class="p">)</span>
        <span class="n">delta</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">timedelta</span><span class="p">(</span><span class="n">days</span><span class="p">)</span> <span class="o">/</span> <span class="n">n_buckets</span>

        <span class="n">entries</span> <span class="o">=</span> <span class="n">ServerHit</span><span class="o">.</span><span class="n">query</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="n">ServerHit</span><span class="o">.</span><span class="n">timestamp</span><span class="o">&gt;</span><span class="n">then</span><span class="p">)</span><span class="o">.</span><span class="n">all</span><span class="p">()</span>

        <span class="n">t_bounds</span> <span class="o">=</span> <span class="p">[(</span><span class="n">then</span><span class="o">+</span><span class="n">delta</span><span class="o">*</span><span class="n">i</span><span class="p">,</span> <span class="n">then</span><span class="o">+</span><span class="n">delta</span><span class="o">*</span><span class="p">(</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span><span class="p">))</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">n_buckets</span><span class="p">)]</span>

        <span class="c"># Accumulate into buckets!  This is how I like to do it.</span>
        <span class="n">buckets</span> <span class="o">=</span> <span class="nb">dict</span><span class="p">([(</span><span class="n">lower</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span> <span class="k">for</span> <span class="n">lower</span><span class="p">,</span> <span class="n">upper</span> <span class="ow">in</span> <span class="n">t_bounds</span><span class="p">])</span>
        <span class="k">for</span> <span class="n">entry</span> <span class="ow">in</span> <span class="n">entries</span><span class="p">:</span>
            <span class="k">for</span> <span class="n">lower</span><span class="p">,</span> <span class="n">upper</span> <span class="ow">in</span> <span class="n">t_bounds</span><span class="p">:</span>
                <span class="k">if</span> <span class="n">entry</span><span class="o">.</span><span class="n">timestamp</span> <span class="o">&gt;=</span> <span class="n">lower</span> <span class="ow">and</span> <span class="n">entry</span><span class="o">.</span><span class="n">timestamp</span> <span class="o">&lt;</span> <span class="n">upper</span><span class="p">:</span>
                    <span class="n">buckets</span><span class="p">[</span><span class="n">lower</span><span class="p">]</span> <span class="o">+=</span> <span class="mi">1</span>

        <span class="c"># Only one series for now.. but you could do other stuff!</span>
        <span class="n">series</span> <span class="o">=</span> <span class="p">[</span><span class="n">buckets</span><span class="p">[</span><span class="n">lower</span><span class="p">]</span> <span class="k">for</span> <span class="n">lower</span><span class="p">,</span> <span class="n">upper</span> <span class="ow">in</span> <span class="n">t_bounds</span><span class="p">]</span>
        <span class="n">data</span> <span class="o">=</span> <span class="p">[</span>
            <span class="n">series</span><span class="p">,</span>
            <span class="c"># You could add another series here...</span>
        <span class="p">]</span>

        <span class="n">options</span> <span class="o">=</span> <span class="p">{</span> <span class="s">&#39;axes&#39;</span> <span class="p">:</span> <span class="p">{</span> <span class="s">&#39;xaxis&#39;</span><span class="p">:</span> <span class="p">{</span>
            <span class="s">&#39;ticks&#39;</span><span class="p">:</span> <span class="p">[</span><span class="n">u</span><span class="o">.</span><span class="n">strftime</span><span class="p">(</span><span class="s">&quot;%I:%M:%S&quot;</span><span class="p">)</span> <span class="k">for</span> <span class="n">l</span><span class="p">,</span> <span class="n">u</span> <span class="ow">in</span> <span class="n">t_bounds</span><span class="p">],</span>
        <span class="p">}}}</span>

        <span class="k">return</span> <span class="nb">dict</span><span class="p">(</span><span class="n">data</span><span class="o">=</span><span class="n">data</span><span class="p">,</span> <span class="n">options</span><span class="o">=</span><span class="n">options</span><span class="p">)</span>
</pre></div>




<p><strong>Rewrite</strong> the <code>index(...)</code> method of your
<code>RootController</code> to look like the following:</p>


<div class="pygments_monokai"><pre>    <span class="nd">@expose</span><span class="p">(</span><span class="s">&#39;tw2facemelttg21.templates.index&#39;</span><span class="p">)</span>
    <span class="k">def</span> <span class="nf">index</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="sd">&quot;&quot;&quot;Handle the front-page.&quot;&quot;&quot;</span>
        <span class="n">jqplot_params</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">jqplot</span><span class="p">()</span>
        <span class="n">plotwidget</span> <span class="o">=</span> <span class="n">LogPlot</span><span class="p">(</span><span class="n">data</span><span class="o">=</span><span class="n">jqplot_params</span><span class="p">[</span><span class="s">&#39;data&#39;</span><span class="p">])</span>
        <span class="n">plotwidget</span><span class="o">.</span><span class="n">options</span> <span class="o">=</span> <span class="n">recursive_update</span><span class="p">(</span>
            <span class="n">plotwidget</span><span class="o">.</span><span class="n">options</span><span class="p">,</span> <span class="n">jqplot_params</span><span class="p">[</span><span class="s">&#39;options&#39;</span><span class="p">])</span>
        <span class="k">return</span> <span class="nb">dict</span><span class="p">(</span><span class="n">page</span><span class="o">=</span><span class="s">&#39;index&#39;</span><span class="p">,</span> <span class="n">gridwidget</span><span class="o">=</span><span class="n">LogGrid</span><span class="p">,</span> <span class="n">plotwidget</span><span class="o">=</span><span class="n">plotwidget</span><span class="p">)</span>
</pre></div>




<p>Now the jqplot widget should pull its data from the (perhaps poorly named)
<code>jqplot</code> method of your <code>RootController</code> and should merge
new <code>options</code> nicely with the predefined ones.  It should also be
available in your <code>index</code> template under the name
<code>plotwidget</code>.  Let's use it there!</p>

<P><strong>Edit</strong> <code>tw2facemelttg21/templates/index.mak</code> and
<strong>add</strong> the following line:</p>


<div class="pygments_monokai"><pre><span class="err">&lt;</span>%inherit file=&quot;local:templates.master&quot;/&gt;
${plotwidget.display() | n}
${gridwidget.display() | n}
</pre></div>



<p>Revisit <a href="http://localhost:8080/"
  target="_blank">http://localhost:8080/</a> and you should get something like
this.</p>

<a href="http://threebean.files.wordpress.com/2011/04/screenshot3.png"><img alt="" class="size-medium wp-image-302" height="180" src="http://threebean.files.wordpress.com/2011/04/screenshot3.png?w=300" title="screenshot3" width="300" /></a>

<h2>5.  Making the layout look like <a href="http://google.com/ig" target="_blank">http://google.com/ig</a> with <code>tw2.jqplugins.portlets</code></h2>

<p><strong>Add</strong> the following import to the top of
<code>tw2facemelttg21/controllers/root.py</code>:</p>


<div class="pygments_monokai"><pre><span class="kn">import</span> <span class="nn">tw2.jqplugins.portlets</span> <span class="kn">as</span> <span class="nn">p</span>
</pre></div>




<p><strong>Rewrite</strong> the <code>index(...)</code> method of your
<code>RootController</code> to look like this:</p>


<div class="pygments_monokai"><pre>    <span class="nd">@expose</span><span class="p">(</span><span class="s">&#39;tw2facemelttg21.templates.index&#39;</span><span class="p">)</span>
    <span class="k">def</span> <span class="nf">index</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="sd">&quot;&quot;&quot;Handle the front-page.&quot;&quot;&quot;</span>
        <span class="n">jqplot_params</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">jqplot</span><span class="p">()</span>
        <span class="n">plotwidget</span> <span class="o">=</span> <span class="n">LogPlot</span><span class="p">(</span><span class="n">data</span><span class="o">=</span><span class="n">jqplot_params</span><span class="p">[</span><span class="s">&#39;data&#39;</span><span class="p">])</span>
        <span class="n">plotwidget</span><span class="o">.</span><span class="n">options</span> <span class="o">=</span> <span class="n">recursive_update</span><span class="p">(</span>
            <span class="n">plotwidget</span><span class="o">.</span><span class="n">options</span><span class="p">,</span> <span class="n">jqplot_params</span><span class="p">[</span><span class="s">&#39;options&#39;</span><span class="p">])</span>

        <span class="n">colwidth</span> <span class="o">=</span> <span class="s">&#39;50%&#39;</span>
        <span class="k">class</span> <span class="nc">LayoutWidget</span><span class="p">(</span><span class="n">p</span><span class="o">.</span><span class="n">ColumnLayout</span><span class="p">):</span>
            <span class="nb">id</span> <span class="o">=</span> <span class="s">&#39;awesome-layout&#39;</span>
            <span class="k">class</span> <span class="nc">col1</span><span class="p">(</span><span class="n">p</span><span class="o">.</span><span class="n">Column</span><span class="p">):</span>
                <span class="n">width</span> <span class="o">=</span> <span class="n">colwidth</span>
                <span class="k">class</span> <span class="nc">por1</span><span class="p">(</span><span class="n">p</span><span class="o">.</span><span class="n">Portlet</span><span class="p">):</span>
                    <span class="n">title</span> <span class="o">=</span> <span class="s">&#39;DB Entries of Server Hits&#39;</span>
                    <span class="n">widget</span> <span class="o">=</span> <span class="n">LogGrid</span>

            <span class="k">class</span> <span class="nc">col2</span><span class="p">(</span><span class="n">p</span><span class="o">.</span><span class="n">Column</span><span class="p">):</span>
                <span class="n">width</span> <span class="o">=</span> <span class="n">colwidth</span>
                <span class="k">class</span> <span class="nc">por2</span><span class="p">(</span><span class="n">p</span><span class="o">.</span><span class="n">Portlet</span><span class="p">):</span>
                    <span class="n">title</span> <span class="o">=</span> <span class="s">&#39;Hits over the last hour&#39;</span>
                    <span class="n">widget</span> <span class="o">=</span> <span class="n">plotwidget</span>

        <span class="k">return</span> <span class="nb">dict</span><span class="p">(</span><span class="n">page</span><span class="o">=</span><span class="s">&#39;index&#39;</span><span class="p">,</span> <span class="n">layoutwidget</span><span class="o">=</span><span class="n">LayoutWidget</span><span class="p">)</span>
</pre></div>




<p>Now that only the <code>layoutwidget</code> is available to your
<code>index</code> template, you'll need to <strong>rewrite</strong> your
<code>tw2facemelttg21/templates/index.mak</code>.</p>


<div class="pygments_monokai"><pre><span class="err">&lt;</span>%inherit file=&quot;local:templates.master&quot;/&gt;
${layoutwidget.display() | n}
</pre></div>




<p>To simplify all the styling, you'll also need to clear out all the clutter
from the TG2.1 quickstart install.  <strong>Rewrite</strong> (for the first
time) <code>tw2facemelttg21/templates/master.mak</code> to look like this:</p>



<div class="pygments_monokai"><pre><span class="cp">&lt;!DOCTYPE html PUBLIC &quot;-//W3C//DTD XHTML 1.0 Transitional//EN&quot;</span>
<span class="cp">                 &quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&quot;&gt;</span>
<span class="nt">&lt;html&gt;</span>
<span class="nt">&lt;head&gt;&lt;/head&gt;</span>
<span class="nt">&lt;body&gt;</span>${self.body()}<span class="nt">&lt;/body&gt;</span>
<span class="nt">&lt;/html&gt;</span>
</pre></div>




<p><strong>Rerun</strong> your paster server and you should see something like
this.</p>

<a href="http://threebean.files.wordpress.com/2011/04/screenshot4.png"><img
  alt="" class="size-medium wp-image-307" height="180"
  src="http://threebean.files.wordpress.com/2011/04/screenshot4.png?w=300"
  title="screenshot4" width="300" /></a>

<p>The portlet windows are movable and collapsible and they <a
  href="http://threebean.org/blog/2011/04/25/jquery-ui-portlets-with-saved-position-and-state-via-cookies/"
  target="_blank">automatically retain their state</a> between page loads (with
jquery.cookie.js).</p>

---

<p>I hope you enjoyed the tutorial.  Once again, you can get the entire source
<a href="https://github.com/ralphbean/tw2-facemelt-tg2.1" target="_blank">here
  on my github account</a>.  If you have any comments, feedback, or questions
--- post 'em!</p>
]]></content:encoded>
    </item>
    <item>
      <title>sqlalchemy + the JavaScript InfoVis Toolkit (jit)</title>
      <link>http://threebean.org/blog/2011/03/06/sqlalchemy-the-javascript-infovis-toolkit-jit/</link>
      <pubDate>Sun, 06 Mar 2011 05:27:24 EST</pubDate>
      <category><![CDATA[python]]></category>
      <category><![CDATA[toscawidgets]]></category>
      <category><![CDATA[turbogears]]></category>
      <guid>http://threebean.org/blog/2011/03/06/sqlalchemy-the-javascript-infovis-toolkit-jit/</guid>
      <description>sqlalchemy + the JavaScript InfoVis Toolkit (jit)</description>
      <content:encoded><![CDATA[
<p>I've been working on some new <a
  href="http://toscawidgets.org/">ToscaWidgets2</a> widgetry.  Today's post is a
tutorial on how to use <a
  href="http://craftsman.rc.rit.edu/module?module=tw2.jit#SQLARadialGraph">tw2.jit.SQLARadialGraph</a>
with the <a href="http://turbogears.org/2.1/docs/">Turbogears 2.1</a> framework.
You can check out the entire source tree for this tutorial from <a
  href="https://github.com/ralphbean/tw2.jit-tg2.1-demo/tree/master/tw2jittg21demo">my
  github page</a>.</p>

<p>I really like <a
  href="http://craftsman.rc.rit.edu/module?module=tw2.jit#SQLARadialGraph">tw2.jit.SQLARadialGraph</a>
and have been getting overly-excited about it:  foaming at the mouth,
rambling...  I've tried to cut all that out of this post and just give you the
details on how to get started using it and Turbogears 2.1.  In short,
<code>SQLARadialGraph</code> itself is a python web component that produces an
interactive graph of your database via javascript and asynchronous requests for
JSON data.  Most all of its real rendering work is accomplished with the awesome
<a href="http://thejit.org">JavaScript InfoVis Toolkit</a> by Nicolas Garcia
Belmonte.</p>

<p>For this post, first we'll configure Turbogears 2.1 to use Toscawidgets 2
middleware.  Second we'll modify our new app's bootstrapping process to insert
some random data into its development sqlite database.  Third, we'll configure
and display an instance of <code>tw2.jit.SQLARadialGraph</code> to visualize the
data we inserted.</p>

<h1>1.  Setting up Turbogears </h1>
<p>Start with a fresh TG 2.1 quickstarted app by running <code>$ paster
  quickstart tw2.jit-tg2.1-demo</code>.  It will ask you some questions -- for
this tutorial we are using <em>mako templates</em> and we <em>are</em> using
authentication.</p>

<h3> virtualenv </h3>
<p>Jump into your newly quickstarted app's directory and run <code>$ virtualenv
  --no-site-packages virtualenv</code> and <code>$ source
  virtualenv/bin/activate</code> to cordon off your app's dependencies from your
system-wide python site-packages directory.</p>

<h3>Configure TG 2.1 for Toscawidgets 2</h3>
<p><strong>Add</strong> the following three lines to the
<code>install_requires</code> argument in your <code>setup.py</code> file.</p>


<div class="pygments_monokai"><pre>        <span class="s">&quot;repoze.what-pylons&quot;</span><span class="p">,</span>
        <span class="s">&quot;tg.devtools&quot;</span><span class="p">,</span>
        <span class="s">&quot;tw2.jit&quot;</span><span class="p">,</span>
</pre></div>




<p>A fresh Turbogears 2.1 quickstarted app depends on Toscawidgets 1 by default.
We'll be using Toscawidgets 2 so we need to remove all the old references as
well as tell TG2.1 to use the tw2 middleware instead of tw1.</p>

<p><strong>Remove</strong> the following three lines from
<code>tw2jittg21demo/controllers/root.py</code>:</p>


<div class="pygments_monokai"><pre><span class="kn">from</span> <span class="nn">tgext.admin.tgadminconfig</span> <span class="kn">import</span> <span class="n">TGAdminConfig</span>
<span class="kn">from</span> <span class="nn">tgext.admin.controller</span> <span class="kn">import</span> <span class="n">AdminController</span>
 <span class="o">...</span> <span class="o">&lt;</span> <span class="n">snip</span> <span class="o">&gt;</span> <span class="o">...</span>
    <span class="n">admin</span> <span class="o">=</span> <span class="n">AdminController</span><span class="p">(</span><span class="n">model</span><span class="p">,</span> <span class="n">DBSession</span><span class="p">,</span> <span class="n">config_type</span><span class="o">=</span><span class="n">TGAdminConfig</span><span class="p">)</span>
</pre></div>




<p><strong>Remove</strong> the following one line from
<code>tw2jittg21demo/lib/base.py</code>:</p>


<div class="pygments_monokai"><pre><span class="kn">from</span> <span class="nn">tw.api</span> <span class="kn">import</span> <span class="n">WidgetBunch</span>
</pre></div>




<p><strong>Add</strong> the following two lines to the bottom of
<code>tw2jittg21demo/config/app_cfg.py</code>:</p>


<div class="pygments_monokai"><pre><span class="n">base_config</span><span class="o">.</span><span class="n">use_toscawidgets</span> <span class="o">=</span> <span class="bp">False</span>
<span class="n">base_config</span><span class="o">.</span><span class="n">use_toscawidgets2</span> <span class="o">=</span> <span class="bp">True</span>
</pre></div>




<p>You will also need to enable the <em>query</em> property on all models.</p>

<p><strong>Uncomment</strong> the following line in
<code>tw2jittg21demo/model/__init__.py</code>:</p>


<div class="pygments_monokai"><pre><span class="n">DeclarativeBase</span><span class="o">.</span><span class="n">query</span> <span class="o">=</span> <span class="n">DBSession</span><span class="o">.</span><span class="n">query_property</span><span class="p">()</span>
</pre></div>




<p><strong>Test</strong> it all with the following commands.</p>

<div class="pygments_monokai"><pre>$ python setup.py develop
$ paster setup-app development.ini
$ paster serve --reload development.ini
</pre></div>




<p>And point your browser at <a
  href="http://localhost:8080">http://localhost:8080</a>.  If all has gone well,
you should see the default index page for your newly quickstarted TG2.1
web-app.</p>

<h1>2.  Add some `interesting` data</h1>
<p>We'll use the existing models defined in <code>tw2jittg21demo/model/</code>
to make this tutorial shorter and smoother.</p>

<p>Edit <code>tw2jittg21demo/websetup/bootstrap.py</code> (which is the code
that gets run when you issue <code>$ paster setup-app development.ini</code>)
and <strong>add</strong> the following two new function definitions just above
the definition of the <code>bootstrap</code> function.</p>


<div class="pygments_monokai"><pre><span class="kn">from</span> <span class="nn">random</span> <span class="kn">import</span> <span class="n">choice</span><span class="p">,</span> <span class="n">randint</span>

<span class="k">def</span> <span class="nf">add_random_users</span><span class="p">():</span>
    <span class="sd">&quot;&quot;&quot; Add 9 random users &quot;&quot;&quot;</span>
    <span class="kn">import</span> <span class="nn">string</span>
    <span class="n">chars</span> <span class="o">=</span> <span class="n">string</span><span class="o">.</span><span class="n">letters</span>
    <span class="k">for</span> <span class="n">first</span> <span class="ow">in</span> <span class="p">[</span><span class="s">u&#39;Sally&#39;</span><span class="p">,</span> <span class="s">u&#39;John&#39;</span><span class="p">,</span> <span class="s">u&#39;Tim&#39;</span><span class="p">]:</span>
        <span class="k">for</span> <span class="n">last</span> <span class="ow">in</span> <span class="p">[</span><span class="s">u&#39;Anderson&#39;</span><span class="p">,</span> <span class="s">u&#39;Flanderson&#39;</span><span class="p">,</span> <span class="s">u&#39;Block&#39;</span><span class="p">]:</span>
            <span class="n">user</span> <span class="o">=</span> <span class="n">model</span><span class="o">.</span><span class="n">User</span><span class="p">()</span>
            <span class="n">user</span><span class="o">.</span><span class="n">user_name</span> <span class="o">=</span> <span class="nb">unicode</span><span class="p">((</span><span class="n">first</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">+</span> <span class="n">last</span><span class="p">)</span><span class="o">.</span><span class="n">lower</span><span class="p">())</span>
            <span class="n">user</span><span class="o">.</span><span class="n">display_name</span> <span class="o">=</span> <span class="s">u&#39;</span><span class="si">%s</span><span class="s"> </span><span class="si">%s</span><span class="s">&#39;</span> <span class="o">%</span> <span class="p">(</span><span class="n">first</span><span class="p">,</span> <span class="n">last</span><span class="p">)</span>
            <span class="n">user</span><span class="o">.</span><span class="n">email_address</span> <span class="o">=</span> <span class="s">u&#39;</span><span class="si">%s</span><span class="s">@socialistworker.org&#39;</span> <span class="o">%</span> <span class="n">user</span><span class="o">.</span><span class="n">user_name</span>
            <span class="n">user</span><span class="o">.</span><span class="n">password</span> <span class="o">=</span> <span class="s">u&#39;&#39;</span><span class="o">.</span><span class="n">join</span><span class="p">([</span><span class="n">choice</span><span class="p">(</span><span class="n">chars</span><span class="p">)</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">12</span><span class="p">)])</span>
            <span class="n">model</span><span class="o">.</span><span class="n">DBSession</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>

    <span class="n">model</span><span class="o">.</span><span class="n">DBSession</span><span class="o">.</span><span class="n">flush</span><span class="p">()</span>

<span class="k">def</span> <span class="nf">add_random_groups</span><span class="p">():</span>
    <span class="sd">&quot;&quot;&quot; Generate a number of random groups and add users to them &quot;&quot;&quot;</span>
    <span class="k">for</span> <span class="n">name</span> <span class="ow">in</span> <span class="p">[</span><span class="s">&#39;developer&#39;</span><span class="p">,</span> <span class="s">&#39;system admin&#39;</span><span class="p">,</span> <span class="s">&#39;shmeveloper&#39;</span><span class="p">,</span> <span class="s">&#39;crispin gladbin&#39;</span><span class="p">]:</span>
        <span class="n">group</span> <span class="o">=</span> <span class="n">model</span><span class="o">.</span><span class="n">Group</span><span class="p">()</span>
        <span class="n">group</span><span class="o">.</span><span class="n">group_name</span> <span class="o">=</span> <span class="n">name</span>
        <span class="n">group</span><span class="o">.</span><span class="n">display_name</span> <span class="o">=</span> <span class="p">(</span><span class="s">u&quot;</span><span class="si">%s</span><span class="s">s group&quot;</span> <span class="o">%</span> <span class="n">name</span><span class="p">)</span><span class="o">.</span><span class="n">title</span><span class="p">()</span>
        <span class="n">model</span><span class="o">.</span><span class="n">DBSession</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">group</span><span class="p">)</span>

        <span class="n">all_users</span> <span class="o">=</span> <span class="n">model</span><span class="o">.</span><span class="n">User</span><span class="o">.</span><span class="n">query</span><span class="o">.</span><span class="n">all</span><span class="p">()</span>
        <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">randint</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">all_users</span><span class="p">)</span><span class="o">-</span><span class="mi">2</span><span class="p">)):</span>
            <span class="n">user</span> <span class="o">=</span> <span class="n">choice</span><span class="p">(</span><span class="n">all_users</span><span class="p">)</span>
            <span class="k">while</span> <span class="n">user</span> <span class="ow">in</span> <span class="n">group</span><span class="o">.</span><span class="n">users</span><span class="p">:</span>
                <span class="n">user</span> <span class="o">=</span> <span class="n">choice</span><span class="p">(</span><span class="n">all_users</span><span class="p">)</span>
            
            <span class="n">group</span><span class="o">.</span><span class="n">users</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>

    <span class="n">model</span><span class="o">.</span><span class="n">DBSession</span><span class="o">.</span><span class="n">flush</span><span class="p">()</span>
</pre></div>




<p>These new functions still need to be <em>called</em> during the boostrap
process, so <strong>add</strong> two invocation lines just above the
<code>transaction.commit()</code> line at the end of the <code>boostrap</code>
function.</p>


<div class="pygments_monokai"><pre>        <span class="n">model</span><span class="o">.</span><span class="n">DBSession</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">u1</span><span class="p">)</span>
        <span class="n">model</span><span class="o">.</span><span class="n">DBSession</span><span class="o">.</span><span class="n">flush</span><span class="p">()</span>

        <span class="n">add_random_users</span><span class="p">()</span>
        <span class="n">add_random_groups</span><span class="p">()</span>

        <span class="n">transaction</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
    <span class="k">except</span> <span class="n">IntegrityError</span><span class="p">:</span>
        <span class="k">print</span> <span class="s">&#39;Warning, there was a problem adding your auth data, it may have already been added:&#39;</span>
</pre></div>




<p><strong>Blow away and re-create</strong> your database with the following
commands:</p>

<div class="pygments_monokai"><pre>$ rm devdata.db
$ paster setup-app development.ini
</pre></div>




<p>Run <code>$ paster serve --reload development.ini</code> and point your
browser at <a href="http://localhost:8080">http://localhost:8080</a> again to
make sure nothing is broken.</p>

<h1>3.  Visualize the database with <em>tw2.jit.SQLARadialGraph</em></h1>

<h3>Make the widget available in your root controller</h3>

<p><strong>Create</strong> a module <code>tw2jittg21demo/widgets.py</code> with
the following content:</p>


<div class="pygments_monokai"><pre><span class="kn">from</span> <span class="nn">tw2jittg21demo</span> <span class="kn">import</span> <span class="n">model</span>
<span class="kn">from</span> <span class="nn">tw2.jit</span> <span class="kn">import</span> <span class="n">SQLARadialGraph</span>

<span class="k">def</span> <span class="nf">makeUserGraph</span><span class="p">():</span>
    <span class="k">class</span> <span class="nc">UserGraph</span><span class="p">(</span><span class="n">SQLARadialGraph</span><span class="p">):</span>
        <span class="nb">id</span> <span class="o">=</span> <span class="s">&#39;whatever&#39;</span>
        <span class="n">base_url</span> <span class="o">=</span> <span class="s">&#39;/jit_data&#39;</span>
        <span class="n">entities</span> <span class="o">=</span> <span class="p">[</span><span class="n">model</span><span class="o">.</span><span class="n">User</span><span class="p">,</span> <span class="n">model</span><span class="o">.</span><span class="n">Group</span><span class="p">,</span> <span class="n">model</span><span class="o">.</span><span class="n">Permission</span><span class="p">]</span>
        <span class="n">excluded_columns</span> <span class="o">=</span> <span class="p">[</span><span class="s">&#39;_password&#39;</span><span class="p">,</span> <span class="s">&#39;password&#39;</span><span class="p">,</span>
                            <span class="s">&#39;user_id&#39;</span><span class="p">,</span> <span class="s">&#39;group_id&#39;</span><span class="p">,</span> <span class="s">&#39;permission_id&#39;</span><span class="p">]</span>
        <span class="n">width</span> <span class="o">=</span> <span class="s">&#39;920&#39;</span>
        <span class="n">height</span> <span class="o">=</span> <span class="s">&#39;525&#39;</span>
        <span class="n">rootObject</span> <span class="o">=</span> <span class="n">model</span><span class="o">.</span><span class="n">User</span><span class="o">.</span><span class="n">query</span><span class="o">.</span><span class="n">first</span><span class="p">()</span>
    
    <span class="k">return</span> <span class="n">UserGraph</span>
</pre></div>




<p>Modify your root controller in
<code>tw2jittg21demo/controllers/root.py</code> to make use of the new
widget.</p>

<p><strong>Add</strong> the following import above the
<code>RootController</code> definition:</p>


<div class="pygments_monokai"><pre><span class="kn">from</span> <span class="nn">tw2jittg21demo.widgets</span> <span class="kn">import</span> <span class="n">makeUserGraph</span>
</pre></div>




<p><strong>Modify</strong> the <code>index(self)</code> method so that the
return statement looks like:</p>


<div class="pygments_monokai"><pre>        <span class="k">return</span> <span class="nb">dict</span><span class="p">(</span><span class="n">page</span><span class="o">=</span><span class="s">&#39;index&#39;</span><span class="p">,</span> <span class="n">widget</span><span class="o">=</span><span class="n">makeUserGraph</span><span class="p">())</span>
</pre></div>




<p>And <strong>add</strong> a new method to serve json data to the jit
widget.</p>


<div class="pygments_monokai"><pre>    <span class="nd">@expose</span><span class="p">(</span><span class="s">&#39;json&#39;</span><span class="p">)</span>
    <span class="k">def</span> <span class="nf">jit_data</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">):</span>
        <span class="sd">&quot;&quot;&quot; Serve data from the tw2.jit built-in controller &quot;&quot;&quot;</span>
        <span class="k">return</span> <span class="n">makeUserGraph</span><span class="p">()</span><span class="o">.</span><span class="n">request</span><span class="p">(</span><span class="n">request</span><span class="p">)</span><span class="o">.</span><span class="n">body</span>
</pre></div>


 

<h3>Display the widget in your root template</h3>

<p><strong>Replace</strong> <em>all</em> of the contents of
<code>tw2jittg21demo/templates/index.mak</code> with the following two
lines:</p>


<div class="pygments_monokai"><pre><span class="err">&lt;</span>%inherit file=&quot;local:templates.master&quot;/&gt;
${widget.display() | n }
</pre></div>




<h3>Give it a spin</h3>

<p>Point your browser once again at <a
  href="http://localhost:8080">http://localhost:8080</a> and you should be
greeted by something like the following:</p>

<a href="http://threebean.files.wordpress.com/2011/03/screenshot1.png"><img alt="Screenshot of the tw2.jit.SQLARadialGraph in action" class="size-medium wp-image-112" height="180" src="http://threebean.files.wordpress.com/2011/03/screenshot1.png?w=300" title="screenshot1" width="300" /></a>

<h3>Getting fancy</h3>

<p><code>tw2.jit.SQLARadialGraph</code> can make use of various method defined
on the sqlalchemy entities of which its been made aware.  One is the
<code>__unicode__(self)</code> method which is already present on our models
from the TG2.1 quickstart process.  Another is <code>__jit_data__(self)</code>
which must return a json-ifiable python dict.</p>

<p>In the future, we expect to support more attribute, but at the time of this
writing the only attribute that is actually used on the client side is the value
of an <code>hover_html</code> key if it is present at all.</p>

<p><strong>Add</strong> the following two methods to your
<code>tw2jittg21demo.model.User</code> class which is defined in
<code>tw2jittg21demo/model/auth.py</code>:</p>


<div class="pygments_monokai"><pre>    <span class="nd">@property</span>
    <span class="k">def</span> <span class="nf">gravatar_url</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="sd">&quot;&quot;&quot; Return a link to the gravatar image for this email addy &quot;&quot;&quot;</span>
        <span class="kn">import</span> <span class="nn">hashlib</span>
        <span class="n">hsh</span> <span class="o">=</span> <span class="n">hashlib</span><span class="o">.</span><span class="n">md5</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">email_address</span><span class="p">)</span><span class="o">.</span><span class="n">hexdigest</span><span class="p">()</span>
        <span class="n">base</span> <span class="o">=</span> <span class="s">&quot;http://www.gravatar.com/avatar/{hsh}?d=monsterid&quot;</span>
        <span class="k">return</span> <span class="n">base</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">hsh</span><span class="o">=</span><span class="n">hsh</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">__jit_data__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="sd">&quot;&quot;&quot; &#39;hover_html&#39; is the only supported key at present &quot;&quot;&quot;</span>
        <span class="k">return</span> <span class="p">{</span>
            <span class="s">&#39;hover_html&#39;</span> <span class="p">:</span> <span class="s">&quot;&quot;&quot;</span>
<span class="s">            &lt;h2&gt;{display_name}&lt;/h2&gt;</span>
<span class="s">            &lt;img src=&quot;{gravatar_url}&quot; /&gt;</span>
<span class="s">            &lt;ul&gt;</span>
<span class="s">                &lt;li&gt;{user_name}&lt;/li&gt;</span>
<span class="s">                &lt;li&gt;{created}&lt;/li&gt;</span>
<span class="s">            &lt;/ul&gt;</span>
<span class="s">            &quot;&quot;&quot;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">gravatar_url</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">gravatar_url</span><span class="p">,</span> <span class="o">**</span><span class="bp">self</span><span class="o">.</span><span class="n">__dict__</span><span class="p">)</span>
        <span class="p">}</span>
</pre></div>




<p><strong>Restart</strong> your webapp and <strong>re-visit</strong> <a
  href="http://localhost:8080">http://localhost:8080</a>.  You should see fancy
pop-ups now when you mouseover any entity for which a <code>__jit_data__</code>
method returns a dict containing a <em>hover_html</em> key.</p>

<a href="http://threebean.files.wordpress.com/2011/03/screenshot2.png"><img alt="sqlalchemy entities can provide tw2.jit.SQLARadialGraph with content to be displayed on mouseover" class="size-medium wp-image-114" height="180" src="http://threebean.files.wordpress.com/2011/03/screenshot2.png?w=300" title="screenshot2" width="300" /></a>

<h3>Style</h3>

<p>Lastly, you may want to re-style the jit graph to fit your page.
<strong>Modify</strong> <code>tw2jittg21demo/widgets.py</code> by adding the
following import statement at the top:</p>


<div class="pygments_monokai"><pre><span class="kn">from</span> <span class="nn">tw2.core</span> <span class="kn">import</span> <span class="n">JSSymbol</span>
</pre></div>




<p>and by <strong>adding</strong> the following extra specifications to the
<code>UserGraph</code> class:</p>


<div class="pygments_monokai"><pre>        <span class="c"># Try to match colors to the TG banner</span>
        <span class="n">backgroundcolor</span> <span class="o">=</span> <span class="s">&#39;#FFFFFF&#39;</span>
        <span class="n">background</span> <span class="o">=</span> <span class="p">{</span> <span class="s">&#39;CanvasStyles&#39;</span><span class="p">:</span> <span class="p">{</span> <span class="s">&#39;strokeStyle&#39;</span> <span class="p">:</span> <span class="s">&#39;#FFFFFF&#39;</span> <span class="p">}</span> <span class="p">}</span>
        <span class="n">Node</span> <span class="o">=</span> <span class="p">{</span> <span class="s">&#39;color&#39;</span> <span class="p">:</span> <span class="s">&#39;#ffcb2f&#39;</span> <span class="p">}</span>
        <span class="n">Edge</span> <span class="o">=</span> <span class="p">{</span> <span class="s">&#39;color&#39;</span> <span class="p">:</span> <span class="s">&#39;#307e8a&#39;</span><span class="p">,</span> <span class="s">&#39;lineWidth&#39;</span><span class="p">:</span><span class="mf">1.5</span><span class="p">,</span> <span class="p">}</span>

        <span class="c"># Override the label style</span>
        <span class="n">onPlaceLabel</span> <span class="o">=</span> <span class="n">JSSymbol</span><span class="p">(</span><span class="n">src</span><span class="o">=</span><span class="s">&quot;&quot;&quot;</span>
<span class="s">            (function(domElement, node){</span>
<span class="s">                domElement.style.display = &quot;none&quot;;</span>
<span class="s">                domElement.innerHTML = node.name;</span>
<span class="s">                domElement.style.display = &quot;&quot;;</span>
<span class="s">                var left = parseInt(domElement.style.left);</span>
<span class="s">                domElement.style.width = &#39;120px&#39;;</span>
<span class="s">                domElement.style.height = &#39;&#39;;</span>
<span class="s">                var w = domElement.offsetWidth;</span>
<span class="s">                domElement.style.left = (left - w /2) + &#39;px&#39;;</span>

<span class="s">                domElement.style.cursor = &#39;pointer&#39;;</span>
<span class="s">                if ( node._depth &lt;= 1 )</span>
<span class="s">                    domElement.style.color = &#39;black&#39;;</span>
<span class="s">                else</span>
<span class="s">                    domElement.style.color = &#39;grey&#39;;</span>
<span class="s">            })&quot;&quot;&quot;</span><span class="p">)</span>
</pre></div>




<p>Once again <strong>restart</strong> your webapp and <strong>reload</strong>
the page to get the following:</p>

<a href="http://threebean.files.wordpress.com/2011/03/screenshot3.png"><img alt="tw2.jit.SQLARadialGraph styled to fit more nicely with the TG2.1 default appearance" class="size-medium wp-image-115" height="180" src="http://threebean.files.wordpress.com/2011/03/screenshot3.png?w=300" title="screenshot3" width="300" /></a>

<h1>Outties</h1>

<p>I hope this post was helpful and got you interested enough to check out the
code and improve on it.  Comments, questions, and patches are all
appreciated.</p>
]]></content:encoded>
    </item>
  </channel>
</rss>
