tag:blogger.com,1999:blog-63306816316290467242024-03-08T14:27:20.269-08:00Jim Fultonj1mhttp://www.blogger.com/profile/15967224335563051939noreply@blogger.comBlogger4125tag:blogger.com,1999:blog-6330681631629046724.post-21656276879565791302011-06-06T04:54:00.000-07:002011-06-06T04:54:20.623-07:00<div class="document" id="dojo-animation-documented"><h1 class="title">A basic look at dojo.Animation</h1><br />
Recently, I needed to animate rotation of an HTML button. I use the Dojo javascript library, which has some frameworks for animation. Unfortunately, the core framework, <tt class="docutils literal">dojo.Animation</tt> isn't well documented. For example, if you look at:<br />
<blockquote><a class="reference external" href="http://dojotoolkit.org/reference-guide/quickstart/Animation.html">http://dojotoolkit.org/reference-guide/quickstart/Animation.html</a></blockquote>The documentation and examples are written in terms of higher-level APIs. It turns out that the base animation API is quite simple. Basically, all you need to do, at a minimum, is specify starting and ending values and set up some event handlers. Here's some example code to rotate a node:<br />
<br />
<pre class="literal-block">new dojo.Animation({
curve: [0, 360],
onAnimate: function (v) {
node.style[transform] = 'rotate(' + v + 'deg)';
}
}).play();
</pre><br />
In the example, I used a variable, <tt class="docutils literal">transform</tt> for the style name. This is necessary because different browsers use different names for the style property that provides for rotation. A full working example can be found at<br />
<blockquote><a class="reference external" href="http://jimfulton.info/demos/dojo-animated-rotate.html">http://jimfulton.info/demos/dojo-animated-rotate.html</a></blockquote>There are various additional properties you can provide to the <tt class="docutils literal">Animation</tt> constructor to control things like duration, frame rate and repeats. You can also provide handlers for beginning, and end of animation. The later is especially important because animation is asynchronous and you often want to perform some action when an animation is done. The API documentation covers these pretty well:<br />
<blockquote><a class="reference external" href="http://dojotoolkit.org/api/1.6/dojo/Animation">http://dojotoolkit.org/api/1.6/dojo/Animation</a></blockquote>The dojo helper functions provide some convenience. For example, to fade a node in:<br />
<br />
<span class="Apple-style-span" style="font-family: monospace; white-space: pre;">dojo.fadeIn({node: node})</span><br />
<br />
which is simpler than:<br />
<br />
<pre class="literal-block">new dojo.Animation({
curve: [0, 1],
onAnimate: function (v) { node.style.opacity = v; }
}).play()
</pre><br />
but it's nice to know that <tt class="docutils literal">fadeIn</tt> is just a short hand for some pretty simple code.<br />
<br />
This is a good example of a low-level API that provides a lot of value, but that gets obscured by higher-level APIs that provide convenience in common cases. </div>j1mhttp://www.blogger.com/profile/15967224335563051939noreply@blogger.com1tag:blogger.com,1999:blog-6330681631629046724.post-10702511350988772992011-03-19T08:47:00.000-07:002011-03-21T05:08:58.169-07:00An API that tries too hardThe purpose of this post is to make a point about the dangers of<br />
excessive automation<br />
<br />
Let me emphasize: <b>the purpose of this post if not to criticize <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">ConfigParser</span> in particular</b>, but to make a point about the API over design. <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">ConfigParser</span> is an old project and if it was redesigned today, would hopefully look very different.<br />
<br />
<br />
The Python 2 standard library contains a <tt class="docutils literal">ConfigParser</tt> module that<br />
parses "ini"-style files. (In Python 3, it was renamed to<br />
<tt class="docutils literal">configparser</tt>.) Ini files model information as named<br />
sections containing named options. They're typically used in a<br />
schema-less manner, meaning there's no schema for defining what<br />
sections, options, or option values are valid. I've found this format<br />
to be powerful enough to handle lots of configuration problems and<br />
less cumbersome than more sophisticated mechanisms based on XML or<br />
other formats requiring schemas.<br />
<br />
I apologize in advance to the maintainers of configparser. I appreciate<br />
and value your effort, really, but ...<br />
<br />
The <tt class="docutils literal">ConfigParser</tt> module is a good example of a module that tries too<br />
hard to help and, in so doing, makes a simple problem complicated.<br />
<br />
When I parse an INI file, all I want is a function that takes a<br />
string and returns a dictionary of dictionaries. (An ordered<br />
dictionary of ordered dictionaries is a bonus.) <tt class="docutils literal">ConfigParsser</tt> has<br />
this functionality embedded in a relatively short private function<br />
burried in layers of unhelpful and non-pythonic APIs.<br />
<br />
<tt class="docutils literal">ConfigParser</tt> provides a bad variable-interpolation syntax that's<br />
an attractive nuisance. Because this mechanaism was used by<br />
<tt class="docutils literal">PasteDeploy</tt>, <span class="docutils literal" style="font-family: 'Courier New', Courier, monospace;"><span class="pre">%</span></span><tt class="docutils literal"><span class="pre">s</span> in </tt><span class="docutils literal" style="font-family: 'Courier New', Courier, monospace;">PasteDeploy</span>'s configuration files have<br />
to be escaped and the APIs defined by <tt class="docutils literal">PasteDeploy</tt> have an awkward<br />
"global configuration" parameter that exists soley to accept a<br />
<tt class="docutils literal">ConfigParser</tt>'s default section.<br />
<br />
<tt class="docutils literal">ConfigParser</tt> provides a policy of case-folding option names that<br />
you have to go out of your way to disable.<br />
<br />
<tt class="docutils literal">ConfigParser</tt> provides a policy of trimming leading and traling<br />
spaces from option values that can't be overridden.<br />
<br />
I've used <tt class="docutils literal">ConfigParser</tt> in the <tt class="docutils literal">zc.buildout</tt> project for some<br />
time. The trimming of leading spaces in configuration values is a<br />
headache. I'm currently working on a port of <tt class="docutils literal">zc.buildout</tt> to<br />
Python 3 and found that Python 3's <tt class="docutils literal">configparser</tt> wasn't backward<br />
compatible with <tt class="docutils literal">ConfigParser</tt> and I was forced to copy the function<br />
at the heart of <tt class="docutils literal">ConfigParser</tt>. This function is straightforward and<br />
expressed in ~70 lines of code, not counting comments, docstrings and<br />
some exception classes. The function would be even simpler if it<br />
wasn't saddled with some legacy syntax support. (Because I used<br />
<tt class="docutils literal">ConfigParser</tt>, I'm saddled with that legacy too.) My code is now<br />
simpler as a result of using this function and I'll be able to adjust<br />
the text trimming policy easily later (after the Python 3 port is<br />
done).<br />
<br />
In an effort to help me, <tt class="docutils literal">ConfigParser</tt> did things for me that<br />
ultimately got in my way. Policies like variable interpolation, case<br />
folding or string trimming can easily be done after initial parsing.<br />
By coupling these policies with parsing, users are either stuck with<br />
the policy decisions, or have to work around them. A simple function that<br />
simply parsed a string and returned an ordered dictionary of ordered<br />
dictionaries would have been far more helpful. People might<br />
appreciate higher-level functions that provide some of these policies,<br />
but these should have been provided as optional conveniences along<br />
with the simpler function.j1mhttp://www.blogger.com/profile/15967224335563051939noreply@blogger.com0tag:blogger.com,1999:blog-6330681631629046724.post-16700953760434666722011-02-13T09:02:00.000-08:002011-02-13T09:02:32.380-08:00Health 2 code-a-thon in DC Feb 12, 2011<a class="reference external" href="http://health2challenge.org/code-a-thon/washington-dc/">http://health2challenge.org/code-a-thon/washington-dc/</a><br />
<br />
Someone sent a link about this to the DC Python Meetup Group<br />
(<a class="reference external" href="http://meetup.zpugdc.org/">http://meetup.zpugdc.org/</a>) a few weeks ago. It looked like fun and a<br />
way to learn about a new domain, so I signed up. I'm not aware if any<br />
other Python folks were there. I didn't bump into any.<br />
<br />
I didn't really know what to expect. I knew pretty close to nothing<br />
about the field. I wondered what technology would be used. It wasn't<br />
clear how teams would be assembled.<br />
<br />
A major motivation of this event was to leverage a growing collection<br />
of health-related databases:<br />
<blockquote><a class="reference external" href="http://health2challenge.org/code-a-thon/data-resources/">http://health2challenge.org/code-a-thon/data-resources/</a></blockquote>The event was fun, if a bit chaotic. It was hard to find an<br />
appropriate team and contribute. I gather some teams had formed ahead<br />
of time, but as an outsider, there didn't seem to be any way to get<br />
hooked up ahead of time.<br />
<br />
I spent some time brainstorming with one loose team that was<br />
interested in raising awareness at the community level of the economic<br />
impact on a community of health issues. There were some ideas thrown<br />
around that didn't seem very realistic. The "public" aren't likely to<br />
visit dedicated health policy sites or even play health policy games.<br />
<br />
I suggested that a good way to reach people in communities might be<br />
through their community newspapers and web sites. The idea was to<br />
develop database-based content in the form of mini applications,<br />
possibly augmented by prose written my health professionals that could<br />
be leveraged by community newspapers. Making this database-based<br />
meant that the content could be relevant to the local community.<br />
<br />
This idea was well received. This was a pleasant surprise, since it's<br />
actually kinda close to my day job.<br />
<br />
I worked for a while on a prototype application that would provide a<br />
small bit of content of the form:<br />
<blockquote>The hospital readmission rates in MYCOMMUNITY are X.<br />
This compares to a rate of Y in MYSTATE and Z nationally.<br />
To find out more, see <a class="reference external" href="http://services.healthindicators.gov/">http://services.healthindicators.gov</a>.</blockquote>where obviously MYCOMMUNITY and MYSTATE are community specific and X,<br />
Y and Z are provided by a health database. We used data from<br />
<a class="reference external" href="http://services.healthindicators.gov/">http://services.healthindicators.gov</a>. The idea is that this blurb<br />
would be published as an app that community newspapers could use to<br />
create content. The specific blurb was just a proof of concept.<br />
<br />
The database provides SOAP and REST interfaces. I ended up using<br />
<i>suds</i>, <a class="reference external" href="http://pypi.python.org/pypi/suds">http://pypi.python.org/pypi/suds</a> to access the SOAP<br />
interface. This was <i>really</i> easy:<br />
<pre class="literal-block">from suds.client import Client
url = 'http://services.healthindicators.gov/v1/SOAP.svc?wsdl'
client = Client(url)
</pre>To get a list of all of the methods:<br />
<pre class="literal-block">print client
</pre>To call a method:<br />
<pre class="literal-block">client.service.SomeMethod()
</pre>(All of the methods in this API have camel-case names with initial<br />
upper case letters.)<br />
<br />
Of course, since this is Python, I could do all of this interactively!<br />
(I say this for the benefit of Health 2.0 readers who read this.)<br />
I was exploring the API in a few minutes. Nice!<br />
<br />
For some reason, the API breaks most requests into pages. Each<br />
request has three parts:<br />
<dl class="docutils"><dt>foo(some_args, page)</dt>
<dd><div class="first">Get some data.</div><div class="last">For example: <tt class="docutils literal">GetLocales</tt>, <tt class="docutils literal">GetIndicatorsByLocaleID</tt>, <tt class="docutils literal">GetGenders</tt>.</div></dd>
<dt>fooCount(some_args)</dt>
<dd><div class="first">Get the result count</div>For example: <tt class="docutils literal">GetLocalesCount</tt>, <tt class="docutils literal">GetIndicatorsByLocaleIDCount</tt>, <tt class="docutils literal">GetGendersCount</tt>.
<div class="last">(In case you're wondering, <tt class="docutils literal">client.service.GetGendersCount()</tt> returns 2.)</div></dd>
<dt>fooPageCount(some_args)</dt>
<dd><div class="first">Get the result count</div><div class="last">For example: <tt class="docutils literal">GetLocalesPageCount</tt>, <tt class="docutils literal">GetIndicatorsByLocaleIDPageCount</tt>, <tt class="docutils literal">GetGendersPageCount</tt>.</div></dd> </dl>I ended up creating a helper function:<br />
<pre class="literal-block">def paged(client, name, *args):
r = []
service = client.service
for page in range(1, getattr(service, name+'PageCount')(*args)+1):
r.extend(getattr(service, name)(*(args+(page, )))[0])
return r
</pre>(If you're paying close attention, you might be wondering about the<br />
<tt class="docutils literal">[0]</tt> in the code above. For some reason, each "page" of data was<br />
returned by suds as a sequence object with one item containing a<br />
list of the actual data. I don't know if this is a quirk of the API or<br />
of suds.)<br />
<br />
This allowed me, for example, to get all locales with:<br />
<pre class="literal-block">locales = paged(client, 'GetLocales')
</pre>to deal with the paged data.<br />
<br />
As is to be expected, the database is challenging. Data are not<br />
uniformly available. Some data are available down to the county<br />
level, but other data isn't. For example, hospital readmission rates<br />
are available at the level of "Health Referral Region", which is<br />
typically (always?) much larger than a county. Different localities<br />
have different amounts of data. Prince William County has on the<br />
order of 300 health indicators available, while DC has around 10,000.<br />
<br />
Speaking of "indicators", as with any domain, this one has confusing<br />
jargon. There were "indicator descriptions", like "Acute Hospital<br />
Readmission Rate" and "indicators", like "the value in Arlington is<br />
17%". As it was explained to me, the indicator descriptions are the<br />
questions and the indicators are the answers. The answers are<br />
qualified and adjusted in various ways, probably based on whatever<br />
studies they came out of. I suspect that there will be lots of naive<br />
and misleading uses of this data. I hope these automated<br />
applications get some careful review by domain experts.<br />
<br />
Using the database affectively requires either familiarity<br />
with the data, or the ability to quickly browse. The SOAP interface<br />
to the database is pretty slow and doesn't provide very targeted<br />
queries. For example, there's no way to request one type of indicator<br />
for a locale. You can pick an indicator, and get data for all locales,<br />
or pick a locale and get all indicators for it. Getting all of the<br />
indicators for DC took several minutes. They're working on their<br />
search capabilities, so I'm sure this will improve over time.<br />
<br />
These sorts of databases will be used for a variety of<br />
applications and run-time use of the databases will likely prove to be<br />
impractical. Taking snapshots is unattractive, as data will<br />
be out of data. Probably, a download model with update<br />
subscriptions would be a better way to go. In other words,<br />
applications might be well served by downloading a database and either<br />
polling for updates or getting updates sent to them.<br />
<br />
We decided to bail on our prototype because we didn't feel the data<br />
was local enough. This was a mistake! We should have finished the<br />
prototype. The actual data didn't matter. The presentation of the<br />
prototype would have been a good time to discuss the issues. Dang.<br />
<br />
I wandered over to another team that was working with the same<br />
database. They were working on a system for looking at local policy<br />
decisions based on county government databases and connecting these to<br />
outcomes via the health indicators database. I think this is a cool<br />
idea and they were led by a domain expert who had a pretty definite<br />
idea of what he was trying to accomplish. I'm pretty sure that this<br />
will lead to success.<br />
<br />
I was hoping to provide some help because I has gained <i>some</i><br />
familiarity with the database. Unfortunately, they were bogged down<br />
accessing the database using some Java-based SOAP<br />
interface. Gaaaa. Their Java programmer was obviously good, but he was<br />
still using Java. Most of the developers were just sitting around<br />
waiting for the Java programmer. I tried to explain some of the issues<br />
with the data, but the Java programmer was just too busy hacking<br />
Java. I ended up learning the Google chart API so I could help them<br />
eventually display the data.<br />
<br />
I eventually got bored and left early. I wish, in hindsight, I'd<br />
finished the prototype I was working on. Hopefully, this blog will be<br />
useful and make up for this a little bit. :)<br />
<br />
I wouldn't mind doing this again, especially if I could hook up with a<br />
team ahead of time. I'd even be willing to finish that prototype if<br />
there was interest. I can't spend too much time on this though, as I<br />
have to many other interesting projects.j1mhttp://www.blogger.com/profile/15967224335563051939noreply@blogger.com0tag:blogger.com,1999:blog-6330681631629046724.post-23816335191452978362011-01-29T15:44:00.000-08:002011-01-30T07:25:09.590-08:00A little web application for keeping an eye on thingsThere are a lot of things I'd keep an eye on, if it was easier,<br />including:<br /><ul class="simple"><li>Nagios problem summaries</li><li>Various plots showing metrics for our hosted applications</li><li>finance.google.com</li><li>social network feeds</li><li>...</li></ul>I could keep web pages open, but that takes too much space.<br />What I want is the equivalent of an electronic photo frame for web<br />pages.<br />I decided to throw something together today that would do this for me:<br /><blockquote><a class="reference external" href="http://www.riversnake.com/webframe">http://www.riversnake.com/webframe</a></blockquote>This is a purely client side application that collects URLs and<br />cycles through them, staying on each one for a minute at a time. You<br />can add, list and remove URLs. You can stop and start cycling, move<br />forward and back, and select URLs to display.<br />There are a few interesting things to note:<br /><dl class="docutils"><dt>Web Storage</dt><dd>The localStorage facility provided by modern browsers is used to store the list of URLs. The storage is keyed on the URL, so you can keep multiple lists of URLs by adding query strings (or copying the application to other URLs).</dd><dt>Layout</dt><dd><div class="first">The application has a row of controls across the top and an iframe that takes up the rest of the page. Getting the iframe to fill the remainder of the page, and getting the URL bar at the top to fill the middle of the control bar was a bit tricky. Using CSS percentage based sizes wasn't acceptable because I didn't want to scale all components equally when resizing a page (or when zooming in or out).</div>The most common technique seems to be to use javascript handlers for page resizes to resize the page contents. Dojo, which I used for the control widgets has some mechanisms to do this, but I wanted to see if I could manage it totally with CSS. <div class="last">I ended up using a combination of absolute and fixed positioning expressed in terms of ems. See the CSS styles in the HTML for more information.</div></dd><dt>Unicode Fun</dt><dd>I was too lazy to go scrounge up images to use for the player controls, so I ended up using unicode text that got me close enough. :)</dd> </dl>j1mhttp://www.blogger.com/profile/15967224335563051939noreply@blogger.com0