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

<channel>
	<title>Psychic Origami &#187; Python</title>
	<atom:link href="http://www.psychicorigami.com/category/python/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.psychicorigami.com</link>
	<description>folding with my brain</description>
	<lastBuildDate>Wed, 03 Aug 2011 19:13:30 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>A Little Sparql</title>
		<link>http://www.psychicorigami.com/2011/06/05/a-little-sparql/</link>
		<comments>http://www.psychicorigami.com/2011/06/05/a-little-sparql/#comments</comments>
		<pubDate>Sun, 05 Jun 2011 14:31:41 +0000</pubDate>
		<dc:creator>john</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">http://www.psychicorigami.com/?p=432</guid>
		<description><![CDATA[Quite often I&#8217;ll get distracted with an idea/piece of technology and mull it over for quite some time. Most of the time I play through these ideas in my head and don&#8217;t do anything with them. It&#8217;s often just an exercise in learning and understanding. I&#8217;ll read up on something and then try to think [...]]]></description>
			<content:encoded><![CDATA[<p>Quite often I&#8217;ll get distracted with an idea/piece of technology and mull it over for quite some time.  Most of the time I play through these ideas in my head and don&#8217;t do anything with them.  It&#8217;s often just an exercise in learning and understanding.  I&#8217;ll read up on something and then try to think about how it works, what I could use it for and so forth.</p>
<p>Occasionally though I&#8217;ll feel compelled to actually write some code to test out the idea.  It&#8217;s good to prototype things.  It&#8217;s particularly handy for testing out new tech without the constraints of an existing system.</p>
<p>The latest idea that&#8217;s been running around my head has been <a href="http://en.wikipedia.org/wiki/Triplestore">triplestores</a>.  I&#8217;ve still yet to need to use one, but wanted to get a better understanding of how they work and what they can be used for.  Having spent a large number of years in the SQL database world, it seems like a good idea to try out some alternatives &#8211; if only to make sure that I&#8217;m not shoe-horning an SQL database into something purely because I know how they work.</p>
<p>So rather than do the sane thing and download a triplestore and start playing around with it, I decided to write my own.  Obviously I don&#8217;t have unlimited time so it had to be simple.  As of such I decided an in-memory store would still let me learn a fair bit, without being too onerous.  Over the past few weeks I&#8217;ve been working on this triplestore and it&#8217;s now at a point where it&#8217;s largely functional:</p>
<p><a href="https://github.com/lilspikey/mini-sparql">https://github.com/lilspikey/mini-sparql</a></p>
<p>To be honest writing this has been almost more of an exercise &#8211; a <a href="http://codekata.pragprog.com/">coding kata</a>.</p>
<p>A few key points of the implementation:</p>
<ul>
<li><a href="http://pypi.python.org/pypi/pyparsing/">PyParsing</a> for the parsing <a href="http://www.w3.org/TR/rdf-sparql-query/">SPARQL</a></li>
<li>Lots of generator functions to make evaluation lazy</li>
<li>Interactive prompt for running SPARQL queries</li>
<li>Load triple data from file/stdin (as triples of data in simplified turtle format)</li>
</ul>
<p>As the data is all stored in memory it&#8217;s no good as a persistence store, but for quickly inspecting some triple data it works pretty well.  Simple point the minisparql.py script at a file containing triple data and away you go:</p>
<pre><code>
$ python minisparql.py birds.ttl
sparql> SELECT ?name WHERE { ?id name ?name . ?id color red }
name
'Robin'
</code></pre>
<h3>PyParsing</h3>
<p>PyParsing is a great project for writing recursive descent parsers.  It makes use of operator overloading to allow you to (mostly) write readable grammars.  You can also specify what objects you want to be returned from parsing &#8211; so it&#8217;s quite easy to parse some code and get back a list of objects that can then execute the code.</p>
<p>It also includes quite a few extra batteries.  One of the most powerful is <a href="http://packages.python.org/pyparsing/pyparsing.pyparsing-module.html#operatorPrecedence">operatorPrecedence</a>, which makes it a piece of cake to create the classic arithmetic style expressions we all know and love.  For example, the operatorPrecedence call in minisparql looks like:</p>
<pre><code>
expr=Forward()
# code removed for clarity
expr << operatorPrecedence(baseExpr,[
        (oneOf('!'), 1, opAssoc.RIGHT, _unaryOpAction),
        (oneOf('+ -'), 1, opAssoc.RIGHT, _unaryOpAction),
        (oneOf('* /'), 2, opAssoc.LEFT, _binOpAction),
        (oneOf('+ -'), 2, opAssoc.LEFT, _binOpAction),
        (oneOf('<= >= < >'), 2, opAssoc.LEFT, _binOpAction),
        (oneOf('= !='), 2, opAssoc.LEFT, _binOpAction),
        ('&#038;&#038;', 2, opAssoc.LEFT, _binOpAction),
        ('||', 2, opAssoc.LEFT, _binOpAction),
    ])
</code></pre>
<p>This handles grouping the various operators appropriately.  So rather than parsing:</p>
<pre><code>5 * 2 + 3 + 2 * 4</code></pre>
<p>And getting just a list of the individual elements you instead get a properly nested parse tree, that is equivalent to:</p>
<pre><code>((5 * 2) + 3) + (2 * 4)</code></pre>
<p>Where each binary operator is only operating on two other expressions (just as you would expect under classic C operator precedence).</p>
<p>This meant that implementing <a href="http://www.w3.org/TR/rdf-sparql-query/#tests">FILTER expressions</a> was pretty straightforward.</p>
<h3>Generators</h3>
<p><a href="http://www.python.org/dev/peps/pep-0255/">Generator functions</a> are one of my favourite features in Python.  I was completely blown away by David Beazley&#8217;s <a href="http://www.dabeaz.com/generators/">Generator Tricks for Systems Programmers</a>.  He starts off with fairly simple examples of generators and shows how they can be combined, much like piped commands in Unix, to create extremely powerful constructs.  Therefore it was obvious to me that generators were a good fit for minisparql.</p>
<p>The code for the regular, optional and union joins became pretty straightforward using generators.  For example the union join looked like:</p>
<pre><code>
    def match(self, solution):
        for m in self.pattern1.match(solution):
            yield m
        for m in self.pattern2.match(solution):
            yield m
</code></pre>
<p>This simply returns the result of one query, followed by the results of another.  The great thing about this is that if we only evaluate the first few values we might not even need to evaluate the second query.  The better aspect though is that you can start getting results straight away.  A more classical approach would entail building up a list of results:</p>
<pre><code>
    def match(self, solution):
        matches = []
        for m in self.pattern1.match(solution):
            matches.append(m)
        for m in self.pattern2.match(solution):
            matches.append(m)
        return matches
</code></pre>
<p>This code would always execute both queries &#8211; even if it wasn&#8217;t strictly necessary.  To my eye the generator code is much clearer as well.  <code>yield</code> stands out very well, in much the same way as <code>return</code> does.  It&#8217;s certainly much clearer that <code>matches.append(m)</code> which does not immediately make it clear that data is being returned/yielded from the function.</p>
<p>Anyway, so here&#8217;s my silly little bit of exploration.  I&#8217;ve learnt a fair bit and got to play with a some different ideas.</p>
<p>If I was to take this any further I&#8217;d probably want to add some sort of disk-based backend.  That way it might be useful for small/medium-sized SPARQL/RDF datasets for simple apps.  Much like <a href="http://www.sqlite.org/">SQLite</a> for SQL databases or <a href="http://pypi.python.org/pypi/Whoosh/">Whoosh</a> for full-text search.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.psychicorigami.com/2011/06/05/a-little-sparql/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Django User Profiles and select_related optimisation</title>
		<link>http://www.psychicorigami.com/2011/02/12/django-user-profiles-and-select_related-optimisation/</link>
		<comments>http://www.psychicorigami.com/2011/02/12/django-user-profiles-and-select_related-optimisation/#comments</comments>
		<pubDate>Sat, 12 Feb 2011 19:01:03 +0000</pubDate>
		<dc:creator>john</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Django]]></category>
		<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">http://www.psychicorigami.com/?p=416</guid>
		<description><![CDATA[If you want to associate extra information with a user account in Django you need to create a separate &#8220;User Profile&#8221; model. To get the profile for a given User object one simply calls get_profile. This is a nice easy way of handling things and helps keep the User model in Django simple. However it [...]]]></description>
			<content:encoded><![CDATA[<p>If you want to associate extra information with a user account in Django you need to create <a href="http://docs.djangoproject.com/en/dev/topics/auth/#storing-additional-information-about-users">a separate &#8220;User Profile&#8221; model</a>.  To get the profile for a given User object one simply calls <a href="http://docs.djangoproject.com/en/dev/topics/auth/#django.contrib.auth.models.User.get_profile">get_profile</a>.  This is a nice easy way of handling things and helps keep the User model in Django simple.  However it does have a downside &#8211; fetching the user and user profile requires two database queries.  That&#8217;s not so bad when we&#8217;re selecting just one user and profile, but when we are displaying a list of users we&#8217;d end up doing one query for the users and another n-queries for the profiles &#8211; the classic <a href="http://www.pbell.com/index.cfm/2006/9/17/Understanding-the-n1-query-problem">n+1 query problem</a>!</p>
<p>To reduce the number of queries to just one we can use <a href="http://docs.djangoproject.com/en/dev/ref/models/querysets/#select-related">select_related</a>.  Prior to Django 1.2 <code>select_related</code> did not support the reverse direction of a <code>OneToOneField</code>, so we couldn&#8217;t actually use it with the user and user profile models.  Luckily that&#8217;s no longer a problem.</p>
<p>If we are clever we can setup the user profile so it stills works with <code>get_profile</code> and does not create an extra query.</p>
<p><code>get_profile</code> looks like this:</p>
<pre><code>
def get_profile(self):
        if not hasattr(self, '_profile_cache'):
            # ...
            # query to get profile and store it in _profile_cache on instance
            # so we don't need to look it up again later
            # ...
        return self._profile_cache
</code></pre>
<p>So if <code>select_related</code> stores the profile object in <code>_profile_cache</code> then <code>get_profile</code> will not need to do any more querying.</p>
<p>To do this we&#8217;d define the user profile model like this:</p>
<pre><code>
class UserProfile(models.Model):
    user = models.OneToOneField(User, related_name='_profile_cache')
    # ...
    # other model fields
    # ...
</code></pre>
<p>The key thing is that we have set <code>related_name</code> on the <code>OneToOneField</code> to be <code>'_profile_cache'</code>.  The <code>related_name</code> defines the attribute on the *other* model (User in this case) and is what we need to refer to when we use <code>select_related</code>.</p>
<p>Querying for all user instances and user profiles at the same time would look like:</p>
<pre><code>
User.objects.selected_related('_profile_cache')
</code></pre>
<p>The only downside is that this does change the behaviour of <code>get_profile</code> slightly.  Previously if no profile existed for a given user a <code>DoesNotExist</code> exception is raised.  With this approach <code>get_profile</code> instead returns <code>None</code>.  So you&#8217;re code will need to handle this.  On the upside repeated calls to <code>get_profile</code>, when no profile exists, won&#8217;t re-query the database.</p>
<p>The other minor problem is that we are relying on the name of a private attribute on the User model (that little pesky underscore indicates it&#8217;s not officially for public consumption).  Theoretically the attribute name could change in a future version of Django.  To mitigate against the name changing I&#8217;d personally just store the name in a variable or on the profile model as a class attribute and reference that whenever you need it, so at least there&#8217;s only one place to make any change.  Apart from that this only requires a minor modification to your user profile model and the use of <code>select_related</code>, so it&#8217;s not a massively invasive optimisation.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.psychicorigami.com/2011/02/12/django-user-profiles-and-select_related-optimisation/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>&#8220;Ultimate&#8221; Arduino Doorbell &#8211; part 2 (Software)</title>
		<link>http://www.psychicorigami.com/2010/10/16/ultimate-arduino-doorbell-part-2-software/</link>
		<comments>http://www.psychicorigami.com/2010/10/16/ultimate-arduino-doorbell-part-2-software/#comments</comments>
		<pubDate>Sat, 16 Oct 2010 18:45:00 +0000</pubDate>
		<dc:creator>john</dc:creator>
				<category><![CDATA[Application]]></category>
		<category><![CDATA[Arduino]]></category>
		<category><![CDATA[chumby]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">http://www.psychicorigami.com/?p=380</guid>
		<description><![CDATA[As mentioned in the previous post about my arduino doorbell I wanted to get the doorbell and my Chumby talking. As the Chumby runs Linux, is on the network and is able to run a recent version of Python (2.6) it seemed like it would be pretty easy to get it to send Growl notifications [...]]]></description>
			<content:encoded><![CDATA[<p>As mentioned in the <a href="http://www.psychicorigami.com/2010/09/06/ultimate-arduino-doorbell-part-1-hardware/">previous post about my arduino doorbell</a> I wanted to get the doorbell and my <a href="http://www.chumby.com/">Chumby</a> talking.</p>
<p>As the Chumby runs Linux, is on the network and is able to run a recent version of Python (2.6) it seemed like it would be pretty easy to get it to send <a href="http://growl.info/">Growl</a> notifications to the local network.</p>
<p><center><img src="http://www.psychicorigami.com/wp-content/uploads/2010/10/growl-doorbell.png" alt="" title="growl doorbell notification" width="421" height="117" class="alignnone size-full wp-image-381" /></center></p>
<p>This did indeed prove fairly easy and involved three main activities:</p>
<ul>
<li>Listening to the serial port for the doorbell to ring</li>
<li>Using <a href="http://www.apple.com/support/bonjour/">Bonjour (formally Rendezvous)</a>/<a href="http://www.zeroconf.org/">Zeroconf</a> to find computers on the network</li>
<li>Sending network Growl notifications to those computers</li>
</ul>
<p>Getting <a href="http://www.psychicorigami.com/2010/06/26/chumby-to-arduino-communication-using-pyserial/">Python and PySerial running on the Chumby</a> is pretty easy.  The Chumby simply listens for the doorbell to send the string &#8216;DING DONG&#8217; and can then react as needed.</p>
<pre><code>
def listen_on_serial_port(port, network):
    ser = serial.Serial(port, 9600, timeout=1)
    try:
        while True:
            line = ser.readline()
            if line is not None:
                line = line.strip()
            if line == 'DING DONG':
                network.send_notification()
    finally:
        if ser:
            ser.close()
</code></pre>
<p><code>port</code> is the string representing the serial port (e.g. <code>/dev/ttyUSB0</code>). <code>network</code> is an object that handles the network notifications (see below).</p>
<p>The network object (an instance of <code>Network</code>) has two jobs.  Firstly to find computers on the network (using PicoRendezvous &#8211; now called <a href="http://www.taoofmac.com/projects/picobonjour">PicoBonjour</a>) and secondly to send network growl notifications to those computers.</p>
<p>A background thread periodically calls <code>Network.find</code>, which uses multicast DNS (Bonjour/Zeroconf) to find the IP addresses of computers to notify:</p>
<pre><code>
class Network(object):
    title = 'Ding-Dong'
    description = 'Someone is at the door'
    password = None

    def __init__(self):
        self.growl_ips = []
        self.gntp_ips = []

    def _rendezvous(self, service):
        pr = PicoRendezvous()
        pr.replies = []
        return pr.query(service)

    def find(self):
        self.growl_ips = self._rendezvous('_growl._tcp.local.')
        self.gntp_ips  = self._rendezvous('_gntp._tcp.local.')
        def start(self):
        import threading
        t = threading.Thread(target=self._run)
        t.setDaemon(True)
        t.start()

    def _run(self):
        while True:
            self.find()
            time.sleep(30.0)
</code></pre>
<p><code>_growl._tcp.local.</code> are computers that can handle Growl UDP packets and <code>_gntp._tcp.local.</code> those that can handle the newer <a href="http://www.growlforwindows.com/gfw/help/gntp.aspx">GNTP protocol</a>.  Currently the former will be Mac OS X computers (running Growl) and the latter Windows computers (running <a href="http://www.growlforwindows.com/">Growl for Windows</a>).</p>
<p>I had to <a href="http://github.com/lilspikey/doorbell/commit/33bbfa13cbd81691b5dcac2a48b84aea38a4c0f0">tweak PicoRendezvous slightly</a> to work round a bug on the Chumby version of Python, where <code>socket.gethostname</code> was returning the string <code>'(None)'</code>, but otherwise this worked ok.</p>
<p>When the doorbell activates <a href="http://www.taoofmac.com/projects/netgrowl">netgrowl</a> is used to send Growl UDP packets to the IP addresses in <code>growl_ips</code> and the <a href="http://github.com/kfdm/gntp">Python gntp library</a> to notify those IP addresses in <code>gntp_ips</code> (but not those duplicated in <code>growl_ips</code>).  For some reason my Macbook was appearing in both lists of IP addresses, so I made sure that the <code>growl_ips</code> took precedence.</p>
<pre><code>
    def send_growl_notification(self):
        growl_ips = self.growl_ips

        reg = GrowlRegistrationPacket(password=self.password)
        reg.addNotification()

        notify = GrowlNotificationPacket(title=self.title,
                    description=self.description,
                    sticky=True, password=self.password)
        for ip in growl_ips:
            addr = (ip, GROWL_UDP_PORT)
            s = socket(AF_INET, SOCK_DGRAM)
            s.sendto(reg.payload(), addr)
            s.sendto(notify.payload(), addr)

    def send_gntp_notification(self):
        growl_ips = self.growl_ips
        gntp_ips  = self.gntp_ips

        # don't send to gntp if we can use growl
        gntp_ips = [ip for ip in gntp_ips if (ip not in growl_ips)]

        for ip in gntp_ips:
            growl = GrowlNotifier(
                applicationName = 'Doorbell',
                notifications = ['doorbell'],
                defaultNotifications = ['doorbell'],
                hostname = ip,
                password = self.password,
            )
            result = growl.register()
            if not result:
                continue
            result = growl.notify(
                noteType = 'doorbell',
                title = self.title,
                description = self.description,
                sticky = True,
            )

    def send_notification(self):
        '''
        send notification over the network
        '''
        self.send_growl_notification()
        self.send_gntp_notification()
</code></pre>
<p>The code is pretty simple and will need some more tweaking.  The notification has failed for some reason a couple of times, but does usually seem to work.  I&#8217;ll need to start logging the doorbell activity to see what&#8217;s going on, but I suspect the Bonjour/Zeroconf is sometimes finding no computers.  If so I&#8217;ll want to keep the previous found addresses hanging around for a little longer &#8211; just in case it&#8217;s a temporary glitch.</p>
<p>You can see the full code in my <a href="http://github.com/lilspikey/doorbell">doorbell repository</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.psychicorigami.com/2010/10/16/ultimate-arduino-doorbell-part-2-software/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>watch-cp.py</title>
		<link>http://www.psychicorigami.com/2010/07/06/watch-cp-py/</link>
		<comments>http://www.psychicorigami.com/2010/07/06/watch-cp-py/#comments</comments>
		<pubDate>Tue, 06 Jul 2010 20:53:42 +0000</pubDate>
		<dc:creator>john</dc:creator>
				<category><![CDATA[Application]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">http://www.psychicorigami.com/?p=315</guid>
		<description><![CDATA[During software development it&#8217;s key to minimize the time between editing code and seeing the results of a change. During web-development the constant tweaking of CSS/HTML/Javascript etc means you&#8217;re always reloading the browser to see changes. At work I do a lot of Java web development, which normally involves compiling code, packaging into a .war [...]]]></description>
			<content:encoded><![CDATA[<p>During software development it&#8217;s key to minimize the time between editing code and seeing the results of a change.  During web-development the constant tweaking of CSS/HTML/Javascript etc means you&#8217;re always reloading the browser to see changes.</p>
<p>At work I do a lot of Java web development, which normally involves compiling code, packaging into a .war file and deploying it to <a href="http://tomcat.apache.org/">Tomcat</a> (running locally).  I make use of the <a href="http://mojo.codehaus.org/tomcat-maven-plugin/">maven tomcat plugin</a>, so it&#8217;s just a case of calling <code>maven tomcat:redeploy</code>.  However it still takes tens of seconds (or more if there are tests to run).  For quick tweaks of css it&#8217;s nice to be able to short-circuit this process.</p>
<p>Tomcat unpacks the .war file to another directory after the app has been deployed.  All the .jsp pages, css and javascript files can be edited in this directory and changes can be seen immediately.  However getting into the habit of editing these files within this directory is usually a bad idea, as the files will get overwritten at the next deployment.</p>
<p>We&#8217;ve been using <a href="http://compass-style.org/">compass</a> lately for css and it has a handy command:</p>
<pre>
<code>
    compass watch
</code>
</pre>
<p>That monitors .sass files for changes, then re-compiles them to css as they change, so you can see changes quickly (without needing to manually run compass each time).</p>
<p>So I thought I could do something similar for the editable files within the war file.  So I created <a href="http://gist.github.com/379197">watch-cp.py</a>.  It simply monitors a set of files and/or directories for changes and copies over files that have changed to a source file or directory.  To provide a bit of feedback it prints out when it spots a changed file, but beyond that it&#8217;s pretty brute-force in it&#8217;s approach.</p>
<p><code>watch-cp.py</code> works in a very similar way to the <code>cp</code> command, but without as many options.  For example:</p>
<pre>
<code>
    # recursively copy files from src/main/webapp to tomcat
    watch-cp.py -r src/main/webapp/* ~/tomcat/webapps/mywebapp
</code>
</pre>
<p>This is great as it means I can edit my sass files, have them compiled to css by compass, then copied over to tomcat without needing to intervene.  It takes a second sometimes, but it&#8217;s fast enough for the most part.</p>
<p>Feel free to fork the <a href="http://gist.github.com/379197">gist of watch-cp.py</a> on github.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.psychicorigami.com/2010/07/06/watch-cp-py/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>iPhone Brighton Buses webapp/javascript front-end</title>
		<link>http://www.psychicorigami.com/2010/07/04/iphone-brighton-buses-webappjavascript-front-end/</link>
		<comments>http://www.psychicorigami.com/2010/07/04/iphone-brighton-buses-webappjavascript-front-end/#comments</comments>
		<pubDate>Sun, 04 Jul 2010 19:23:37 +0000</pubDate>
		<dc:creator>john</dc:creator>
				<category><![CDATA[Application]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">http://www.psychicorigami.com/?p=285</guid>
		<description><![CDATA[Here in Brighton a good number of the bus stops have electronic boards, that tell you when the next buses are due. The data for these boards is available online and even provides data for stops that don&#8217;t have the electronic boards. In fact there&#8217;s an free iPhone app available, so you can get the [...]]]></description>
			<content:encoded><![CDATA[<p>Here in Brighton a good number of the bus stops have electronic boards, that tell you when the next buses are due.  The data for these boards is <a href="http://buses.co.uk/">available online</a> and even provides data for stops that don&#8217;t have the electronic boards.  In fact there&#8217;s an <a href="http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=323501858&#038;mt=8">free iPhone app</a> available, so you can get the data easily on the move.</p>
<p><center><br />
<a href="http://www.flickr.com/photos/lilspikey/4743549060/" title="Brighton Station (Stop B) by lilspikey, on Flickr"><img src="http://farm5.static.flickr.com/4123/4743549060_97a885c3cc.jpg" width="500" height="375" alt="Brighton Station (Stop B)"></a><br />
</center></p>
<p>After playing around with <a href="http://www.psychicorigami.com/2009/07/10/an-iphone-friendly-local-storage-backed-offline-todo-list-webapp/">iPhone javascript apps</a>, I thought it would be interesting to try and create a javascript app for the bus times.  I had some specific ideas about how I wanted the app to work.  I was also keen on trying out some more &#8220;HTML 5&#8243; tech.  Plus I do like things to be (at least in principle) cross-platform, so this has a chance of working ok on <a href="http://www.android.com/">Android</a> phones (though I&#8217;ve not tested it on one).</p>
<p>There are still a few rough edges, but you can try out the app yourself at:</p>
<ul>
<li><a href="http://buses.psychicorigami.com/">http://buses.psychicorigami.com/</a></li>
</ul>
<p>It will prompt you to add a stop.  If you type in the text field, you&#8217;ll get a list of stops that match.  If your browser supports geo-location, you&#8217;ll also see a &#8220;Find nearby&#8221; button, which will list the nearest few stops.</p>
<p>I had the pleasure of demoing this app at the <a href="http://asyncjs.com/showntell/">Async Javascript show&#8217;n'tell</a> last month.  I quickly outlined how it worked, which involves the following:</p>
<ul>
<li><a href="http://developer.apple.com/safari/library/documentation/iphone/conceptual/safarijsdatabaseguide/OfflineApplicationCache/OfflineApplicationCache.html">Offline App Cache</a> &#8211; to make startup quick (key when you don&#8217;t want to miss your bus)</li>
<li><a href="https://developer.mozilla.org/en/dom/storage">Client-side Local Storage</a> &#8211; to record which bus stops you add</li>
<li><a href="http://www.w3.org/TR/geolocation-API/">Geo-location</a> &#8211; so you can find nearby stops</li>
<li><a href="http://jquery.com/">JQuery</a> &#8211; for front end</li>
<li><a href="http://bottle.paws.de/">Bottle.py</a> &#8211; for back end</li>
<li><a href="http://www.crummy.com/software/BeautifulSoup/">BeautifulSoup</a> &#8211; for scraping bus times</li>
</ul>
<h3>Getting the data</h3>
<p>The buses.co.uk site has an API, which I used to scrape all of the bus stops in Brighton.  I chose to scrape the update data from the website itself though, as I couldn&#8217;t get the API to give me the combined services for each stop (so you can see when the next 7 and 81 are coming for example). There are typically two stops for each name &#8211; one either side of the road.  The updates are for both directions, so I actually merged the stops together &#8211; which makes searching easier.</p>
<p>All of the stop data was stored in a single <a href="http://www.sqlite.org/">SQLite</a> database &#8211; mostly for convenience.  The database is used in a read-only method in the actual running app.  I&#8217;m sure I could use something a bit better, particularly when it comes to the distance queries.  Currently, as there are a relatively small number of stops I&#8217;m using a brute force method to find the nearest stops, but I&#8217;m sure with the use of a proper <a href="http://en.wikipedia.org/wiki/Spatial_index">spatial index</a> this could be done a lot more efficiently.  If this was scaled up to work for all stops in the UK, then that would be a necessity.</p>
<h3>Geo-location</h3>
<p>Initially the geo-location was pretty straightforward &#8211; I simply used the <code> getCurrentPosition</code> function and I got a latitude and longitude back.  Testing in Firefox and Safari on the mac gave fairly reasonable results.  However on the iPhone itself I started to notice it wasn&#8217;t very accurate.  Sometimes it was out by several 100 metres, meaning that stops I was standing next to were sometimes not showing up at all!  I had noticed, in the past, that the built-in map app sometimes has a bit of &#8220;lag&#8221; in getting an accurate position.  So I switched to using <code> watchPosition</code> and polling for the position for several seconds.  This worked pretty well, as the initial result would only be vaguely in the right place and then after a few seconds the position would become more accurate and the listing of nearby stops would start to look more sensible.</p>
<h3>Look and feel</h3>
<p>Originally I&#8217;d planned to mimic the look and feel of the iPhone&#8217;s built-in weather app.  The app is built around a similar concept &#8211; you search for bus stops and add them to a list of stops that you can flick through.  I tried to get a nice animated sliding action working, but kept on running into trouble on the device itself.  In Safari and Firefox the animation all worked ok, but I suspect there&#8217;s an issue on the iPhone when you are dragging an area that limits redrawing.  In the end I had to ditch the flip/swipe gesture too &#8211; interfering with the touch events again seemed to cause some rendering issues on occasion.  So instead simply clicking on the left or right of the timetable moves between pages.  It&#8217;s a slightly less complicated way of doing things, so there&#8217;s less that can go wrong really.</p>
<p><center><br />
<a href="http://www.flickr.com/photos/lilspikey/2720488882/" title="Old Brighton No. 5 Bus by lilspikey, on Flickr"><img src="http://farm4.static.flickr.com/3255/2720488882_d425545223.jpg" width="500" height="375" alt="Old Brighton No. 5 Bus"></a><br />
</center></p>
<h3>Deployment</h3>
<p>As this was a nice simple app (on the back-end at least), I decided to take a little time to create a <a href="http://docs.fabfile.org/">Fabric</a> file to control deployment.  The app is hosted on Webfaction, where I have already setup key-based login for ssh.  The app is deployed using <a href="http://code.google.com/p/modwsgi/">mod_wsgi</a> with a daemon process, so that one merely needs to copy over the new code, then &#8220;touch&#8221; the .wsgi file restart the daemon process.  Not exactly tricky to do, but making it a one-button process saves making any mistakes.</p>
<p>To trigger the deployment I run <a href="http://github.com/lilspikey/buses/blob/master/fabfile.py">my fab file</a> like this:</p>
<pre>
<code>
fab --user=lilspikey --hosts=lilspikey.webfactional.com deploy:webapps/buses/app/
</code>
</pre>
<p>I actually have that in a shell script, that isn&#8217;t checked into git, so that the deployment specific username, host name and remote directory aren&#8217;t checked into source control.  If I wasn&#8217;t using key-based login, I&#8217;d need to specify a password and I definitely wouldn&#8217;t want to check that into git!</p>
<h3>Source code</h3>
<p>You can get the source code on github in my <a href="http://github.com/lilspikey/buses">buses repository</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.psychicorigami.com/2010/07/04/iphone-brighton-buses-webappjavascript-front-end/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Chumby to Arduino communication using PySerial</title>
		<link>http://www.psychicorigami.com/2010/06/26/chumby-to-arduino-communication-using-pyserial/</link>
		<comments>http://www.psychicorigami.com/2010/06/26/chumby-to-arduino-communication-using-pyserial/#comments</comments>
		<pubDate>Sat, 26 Jun 2010 20:52:03 +0000</pubDate>
		<dc:creator>john</dc:creator>
				<category><![CDATA[Arduino]]></category>
		<category><![CDATA[chumby]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">http://www.psychicorigami.com/?p=278</guid>
		<description><![CDATA[So here are a few notes on getting a Chumby to talk to an Arduino using PySerial (and Python). It&#8217;s pretty easy, but I&#8217;ll document it to make it obvious. As the Chumby is sometimes a bit slow with a few of these steps, it&#8217;s good to know it will work in the end. First [...]]]></description>
			<content:encoded><![CDATA[<p>So here are a few notes on getting a <a href="http://www.chumby.com/">Chumby</a> to talk to an <a href="http://www.arduino.cc/">Arduino</a> using <a href="http://pyserial.sourceforge.net/">PySerial</a> (and <a href="http://www.python.org/">Python</a>).  It&#8217;s pretty easy, but I&#8217;ll document it to make it obvious.  As the Chumby is sometimes a bit slow with a few of these steps, it&#8217;s good to know it will work in the end.</p>
<p>First you&#8217;ll want <a href="http://wiki.chumby.com/mediawiki/index.php/Python">Python on the Chumby</a>.  At the time the latest version already compiled for the Chumby is Python 2.6, so it&#8217;s pretty up-to-date.</p>
<p>Once you&#8217;ve got Python installed and on a USB stick you&#8217;ll also want to download PySerial &#8211; I picked the latest version <a href="http://sourceforge.net/projects/pyserial/files/pyserial/2.5-rc2/pyserial-2.5-rc2.tar.gz/download">PySerial-2.5-rc2</a>.</p>
<p>With PySerial expanded and on the USB stick (alongside Python) you&#8217;ll want to put the USB stick in the Chumby and connect to it <a href="http://wiki.chumby.com/mediawiki/index.php/Chumby_tricks#Hidden_screen_in_Control_Panel">via SSH</a>.</p>
<p>Change to the directory for the USB stick (e.g. <code>cd /mnt/usb</code>).</p>
<p>You should see (at least) two directories, one for Python and one for PySerial:</p>
<pre>
<code>
chumby:/mnt/usb-EC5C-3D0A# ls -l
drwxr-xr-x    6 root     root         4096 Jun 26 21:15 pyserial-2.5-rc2
drwxrwxrwx    4 root     root         4096 Jan 10 13:51 python2.6-chumby
</code>
</pre>
<p>You&#8217;re first instinct may be to try and install PySerial via the usual call to <code>python setup.py install</code>.  However this appears not to work.</p>
<p>All is not lost though &#8211; just manually copy the relevant directory (serial) from PySerial to the python site-packages directory, e.g.:</p>
<pre>
<code>
cp -r pyserial-2.5-rc2/serial python2.6-chumby/lib/python2.6/site-packages/
</code>
</pre>
<p>Know to check that&#8217;s worked open a python prompt and try to import the serial library:</p>
<pre>
<code>
chumby:/mnt/usb-EC5C-3D0A# python2.6-chumby/bin/python
Python 2.6.2 (r262:71600, May 23 2009, 22:28:43)
[GCC 4.3.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import serial
>>>
</code>
</pre>
<p>If you don&#8217;t see any errors (as above) then everything has installed ok.</p>
<p>Next step is to try out connecting an Arduino.</p>
<p>So first off you&#8217;ll want to ensure the Arduino has a program that can read from the serial port &#8211; to show that everything is working ok.  In my case I picked my <a href="http://www.psychicorigami.com/2010/05/26/my-first-arduino-project-morse-code/">Morse Code program</a>.  This will read bytes from the serial port and toggle the built-in LED (on pin 13), so it&#8217;s handy for verifying the serial port is working.</p>
<p>So now you have a program loaded on the Arduino, connect it to the Chumby.  The Arduino should get enough power from the Chumby to start up ok.</p>
<p>You need to work out which serial port the Arduino is using on the Chumby.  List the tty&#8217;s in /dev and pick the most likely looking one (should have USB in it&#8217;s name):</p>
<pre>
<code>
chumby:/mnt/usb-EC5C-3D0A# ls /dev/tty*
/dev/tty      /dev/ttyS00   /dev/ttyS02   /dev/ttyS1    /dev/ttyS3
/dev/ttyS0    /dev/ttyS01   /dev/ttyS03   /dev/ttyS2    /dev/ttyUSB0
</code>
</pre>
<p>On my Chumby the serial port was <code>/dev/ttyUSB0</code>.</p>
<p>Now open up a python prompt again and try talking to the Arduino.  You&#8217;ll need to configure the baud-rate of the serial port to match the program on the Chumby.  In my case this was 9600.</p>
<pre>
<code>

chumby:/mnt/usb-EC5C-3D0A# python2.6-chumby/bin/python
Python 2.6.2 (r262:71600, May 23 2009, 22:28:43)
[GCC 4.3.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import serial
>>> ser = serial.Serial('/dev/ttyUSB0', 9600, timeout=1)
>>> ser.write('sos')
3
</code>
</pre>
<p>If you are using the same morse code program you should see the LED blink out &#8216;DOT-DOT-DOT DASH-DASH-DASH DOT-DOT-DOT&#8217; &#8211; confirming that the serial port works.</p>
<p>This is really quite handy, as the Chumby is a small low-power Linux server.  Coupled with Python this means it&#8217;s really easy to get any Arduino based project online, without needing a &#8220;normal&#8221; computer constantly running.  Not quite as self-contained as using an <a href="http://www.arduino.cc/en/Main/ArduinoEthernetShield">Ethernet Shield</a>, but the Chumby is fairly small, so it and an Arduino will easily sit on a window ledge (for example).</p>
]]></content:encoded>
			<wfw:commentRss>http://www.psychicorigami.com/2010/06/26/chumby-to-arduino-communication-using-pyserial/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Chumby Podcast Client Source Code</title>
		<link>http://www.psychicorigami.com/2010/01/09/chumby-podcast-client-source-code/</link>
		<comments>http://www.psychicorigami.com/2010/01/09/chumby-podcast-client-source-code/#comments</comments>
		<pubDate>Sat, 09 Jan 2010 18:46:54 +0000</pubDate>
		<dc:creator>john</dc:creator>
				<category><![CDATA[Application]]></category>
		<category><![CDATA[chumby]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">http://www.psychicorigami.com/?p=250</guid>
		<description><![CDATA[Ok so I blogged about a podcatcher/podcast client for the Chumby quite a while ago (August 2008 to be precise). At the time I said I&#8217;d tidy things up and release the source code etc. Well that didn&#8217;t quite happen, but I figured I might as well release the source code. So seeing as I&#8217;ve [...]]]></description>
			<content:encoded><![CDATA[<p>Ok so I blogged about a <a href="http://www.psychicorigami.com/2008/08/23/chumby-podcatcher/">podcatcher/podcast client for the Chumby</a> quite a while ago (August 2008 to be precise).  At the time I said I&#8217;d tidy things up and release the source code etc.  Well that didn&#8217;t quite happen, but I figured I might as well release the source code.  So seeing as I&#8217;ve been using <a href="http://git-scm.com/">git</a> a lot lately and <a href="https://github.com/">github</a> is easy to use I thought that&#8217;d I&#8217;d best just put online what I had.</p>
<p>So you can now find the hybrid Python, Javascript and Flash <a href="http://www.chumby.com/">Chumby</a> postcast client I dubbed <a href="http://github.com/lilspikey/chumbycast">chumbycast, on github</a>.  I&#8217;m providing the code just for the curious &#8211; I&#8217;ve had at least one request for it, but I&#8217;m not really going to detail too much of how it works. Though some of this is outlined in the original post where I mentioned the project.</p>
<p>I am tempted to revive this project a bit, but probably by trying a different approach.  I mostly gave up on this as I was creating a UI on the Chumby in Flash and my Flash skills aren&#8217;t exactly great, plus I was trying to use <a href="http://www.mtasc.org/">mtasc</a> which hampered things further.  Which is not to belittle mtasc &#8211; it&#8217;s just with limited time and no previous Flash experience I wasn&#8217;t exactly helping myself by not using some nice friendly Flash IDE.</p>
<p>I&#8217;ve since realised that if I ditched the Flash UI I could probably get quite a bit done.  The Python httpserver side of things was pretty easy.  So if I focussed on that I would then have a few of ways of providing a UI by hijacking existing functionality on the Chumby.</p>
<ul>
<li>The Chumby runs a <a href="http://wiki.chumby.com/mediawiki/index.php/Chumby_as_an_iPod_server">httpserver on port 8080 when an ipod is plugged in</a>, which the UI uses to access playlists etc.  I could mimic this approach and effectively make the podcast client look like an iPod as far as the Chumby&#8217;s UI was concerned.  By plugging in a USB stick loaded up with the podcast client everything would behave the same as if you had plugged in an iPod.</li>
<li>It&#8217;s possible to <a href="http://wiki.chumby.com/mediawiki/index.php/Chumby_and_music">create playlist files and edit the &#8220;My Streams&#8221;</a> section programmatically.  Each podcast subscribed to would create a matching pls or m3u file and be added to the &#8220;My Streams&#8221; section.</li>
<li>Create a javascript/web UI for controlling the playback and subscriptions to podcasts and other podcast management tasks (like removing old files, manually downloading older episodes etc) from another computer.  Possibly adding bonjour/zero-conf support so the Chumby can be browsed to easily</li>
</ul>
<p>I would need to see whether those first two could be made to work for my purpose, but it would make sense to just use the existing UI on the Chumby &#8211; rather than creating a new one.  The existing chumbycast code already provides a javascript/web UI for controlling the playback on the Chumby.  This was originally done so I could remote control the chumby, but also so that I could easily create and debug an API for the Flash UI to use.</p>
<p>The other major missing feature is allowing the podcasts to be paused part way through.  The current client does not support this, as the audio files are streamed and not downloaded.  So that would be the first change to make.  Now that I&#8217;ve dug out the code maybe I&#8217;ll be inspired to play around some more.  I&#8217;ve also been listening to a lot more podcasts (thanks to <a href="http://huffduffer.com/">Huffduffer</a>) so it might happen yet.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.psychicorigami.com/2010/01/09/chumby-podcast-client-source-code/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Django Batch Select</title>
		<link>http://www.psychicorigami.com/2009/11/23/django-batch-select/</link>
		<comments>http://www.psychicorigami.com/2009/11/23/django-batch-select/#comments</comments>
		<pubDate>Mon, 23 Nov 2009 10:00:00 +0000</pubDate>
		<dc:creator>john</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Django]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[SQL]]></category>

		<guid isPermaLink="false">http://www.psychicorigami.com/?p=228</guid>
		<description><![CDATA[So quite a while ago I wrote about avoiding the n+1 query problem with SQLObject. I&#8217;ve since been using Django a lot more. In particular at my day job I&#8217;ve been improving a Django site we&#8217;ve inherited. This particular site suffered from a few too many SQL queries and needed speeding up. Some of the [...]]]></description>
			<content:encoded><![CDATA[<p>So quite a while ago I wrote about <a href="http://www.psychicorigami.com/2007/12/16/using-raw-sql-with-sqlobject-and-keeping-the-object-y-goodness/">avoiding the n+1 query problem with SQLObject</a>.  I&#8217;ve since been using <a href="http://www.djangoproject.com/">Django</a> a lot more.  In particular at <a href="http://www.sensibledevelopment.com/">my day job</a> I&#8217;ve been improving a Django site we&#8217;ve inherited.  This particular site suffered from a few too many SQL queries and needed speeding up.  Some of the issues related to the classic n+1 problem.  i.e. one initial query triggering a further n queries (where n is the number of results in the first query).</p>
<p>A liberal dose of <a href="http://docs.djangoproject.com/en/dev/ref/models/querysets/#id4">select_related</a> helped.  However that was only useful in the cases where a ForeignKey needed to be pre-fetched.</p>
<p>In the case however there was a page that was selecting a set of objects that had tags.  The tags for each object were being displayed along side a link to the main object.  Given that the initial query returned over three hundred objects, this meant the page was performing another three hundred (plus) queries to fetch the individual tags for each object!  Now we could cache the page and that&#8217;s was indeed what was being done.  The trouble however was when the cache expired.  It also made things painful when developing &#8211; as I&#8217;d typically want to disable caching whilst I&#8217;m making changes to pages frequently.</p>
<p>I came up with a specific solution for this project &#8211; to perform the initial query, then a second query to fetch *all* of the other tags in one go.  The results of the second query could then be stitched into the original results, to make them available in a handy manor within the page&#8217;s template.</p>
<p>I took the original idea and made it re-usable and am now releasing that code as <a href="http://github.com/lilspikey/django-batch-select">Django Batch Select</a>.  There are examples of how to use it over at <a href="http://github.com/lilspikey/django-batch-select">github</a>, but it works a bit like this:</p>
<pre><code>
&gt;&gt;&gt; entries = Entry.objects.batch_select('tags').all()
&gt;&gt;&gt; entry = entries[0]
&gt;&gt;&gt; print entry.tags_all
[&lt;Tag: Tag object&gt;]
</code></pre>
<p>It&#8217;s a very early release &#8211; after the result of only a few hours coding, so use with care.  It does have 100% test code coverage at the moment and I&#8217;m reasonable confident about it working.  Please try it out and let me know whether it works for you.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.psychicorigami.com/2009/11/23/django-batch-select/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Django simple admin ImageField thumbnail</title>
		<link>http://www.psychicorigami.com/2009/06/20/django-simple-admin-imagefield-thumbnail/</link>
		<comments>http://www.psychicorigami.com/2009/06/20/django-simple-admin-imagefield-thumbnail/#comments</comments>
		<pubDate>Sat, 20 Jun 2009 11:11:00 +0000</pubDate>
		<dc:creator>john</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Django]]></category>
		<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">http://www.psychicorigami.com/?p=168</guid>
		<description><![CDATA[The Django admin site is one of the best features of Django. It really lets you just get on with building your app, without having to worry too much about how you&#8217;ll administer your site. The defaults are generally pretty good, but it&#8217;s often the case that you&#8217;ll want to tweak and change it (particularly [...]]]></description>
			<content:encoded><![CDATA[<p>The <a href="http://www.djangoproject.com/">Django</a> admin site is one of the best features of Django.  It really lets you just get on with building your app, without having to worry too much about how you&#8217;ll administer your site.</p>
<p>The defaults are generally pretty good, but it&#8217;s often the case that you&#8217;ll want to tweak and change it (particularly when you have clients involved).  Luckily it&#8217;s pretty easy to customize.</p>
<p>One common change that many people will want to do is to display a thumbnail of an uploaded image in the admin.  The default image field (when an image has been uploaded) in the admin looks like:</p>
<p><img class="alignnone size-full wp-image-169" title="image_field1" src="http://www.psychicorigami.com/wp-content/uploads/2009/06/image_field1.jpg" alt="image_field1" width="331" height="44" /></p>
<p>and we want to change it to look like this:</p>
<p><img class="alignnone size-full wp-image-170" title="image_field2" src="http://www.psychicorigami.com/wp-content/uploads/2009/06/image_field2.jpg" alt="image_field2" width="257" height="77" /></p>
<p>In my case I had images that I knew would be fairly small so I didn&#8217;t need to use any auto-resizing or anything like that.  I found <a href="http://www.djangosnippets.org/snippets/934/">this snippet</a> for doing the same task, but decided to simplify it a fair bit and ended up with:</p>
<pre><code>
from django.contrib.admin.widgets import AdminFileWidget
from django.utils.translation import ugettext as _
from django.utils.safestring import mark_safe

class AdminImageWidget(AdminFileWidget):
    def render(self, name, value, attrs=None):
        output = []
        if value and getattr(value, "url", None):
            image_url = value.url
            file_name=str(value)
            output.append(u' &lt;a href="%s" target="_blank"&gt;&lt;img src="%s" alt="%s" /&gt;&lt;/a&gt; %s ' % \
                (image_url, image_url, file_name, _('Change:')))
        output.append(super(AdminFileWidget, self).render(name, value, attrs))
        return mark_safe(u''.join(output))
</code></pre>
<p>Then to use it I simply override <code>formfield_for_dbfield</code> to return a field that uses that widget for the field I&#8217;m interested in:</p>
<pre><code>
class MyAdmin(admin.ModelAdmin):
    def formfield_for_dbfield(self, db_field, **kwargs):
        if db_field.name == 'profile_image':
            request = kwargs.pop("request", None)
            kwargs['widget'] = AdminImageWidget
            return db_field.formfield(**kwargs)
        return super(MyAdmin,self).formfield_for_dbfield(db_field, **kwargs)
</code></pre>
<p>It&#8217;s a fairly straightforward alteration to how the <code>ImageField</code> widget renders in the admin, but it is often a big help to be able to actually see the image in question.</p>
<p>UPDATED: Feb 24th, 2010.  Based on Jeremy&#8217;s comment below and having upgraded to Django 1.1 I&#8217;ve modified this example to remove the &#8220;request&#8221; param from kwargs before passing it db_field.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.psychicorigami.com/2009/06/20/django-simple-admin-imagefield-thumbnail/feed/</wfw:commentRss>
		<slash:comments>12</slash:comments>
		</item>
		<item>
		<title>Introducing NoteComb</title>
		<link>http://www.psychicorigami.com/2009/05/18/introducing-notecomb/</link>
		<comments>http://www.psychicorigami.com/2009/05/18/introducing-notecomb/#comments</comments>
		<pubDate>Mon, 18 May 2009 21:25:50 +0000</pubDate>
		<dc:creator>john</dc:creator>
				<category><![CDATA[Application]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">http://www.psychicorigami.com/?p=167</guid>
		<description><![CDATA[I&#8217;ve been writing an application for my other half in recent months to help her organise observations of the children in her class. I&#8217;ve actually written two versions of the app. The first was dubbed &#8220;Observertron&#8221; and in retrospect was overly complex. After a bit of thought I was able to vastly simplify the app, [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve been writing an application for my other half in recent months to help her organise observations of the children in her class.  I&#8217;ve actually written two versions of the app.  The first was dubbed &#8220;Observertron&#8221; and in retrospect was overly complex.  After a bit of thought I was able to vastly simplify the app, whilst also making it more useful for other purposes beyond making observations.</p>
<p>The app is called <a href="http://www.psychicorigami.com/notecomb/">NoteComb</a> and is written in <a href="http://python.org/">Python</a> using <a href="http://www.wxpython.org/">wxPython</a>.  It&#8217;s essentially a specialised text-editor.  The core feature is a <a href="http://www.gnu.org/software/grep/">grep</a>-like search functionality coupled with the ability to edit text in-place during a search.  Lines that don&#8217;t contain the search terms get hidden, leaving you free to edit the remaining lines as you want:</p>
<p><center><br />
<a href="http://www.psychicorigami.com/notecomb/#demo"><img src="http://www.psychicorigami.com/notecomb/images/screenshot1.jpg" /></a><br />
</center></p>
<p>I&#8217;ve decided to take the time and try to package it up &#8220;properly&#8221;.  So NoteComb is available as Mac and Windows apps, complete with icons etc.  In fact to &#8220;regular&#8221; users it should appear that NoteComb is an app like any other &#8211; the fact it&#8217;s written using Python is largely incidental.</p>
<p>This packaging works pretty well overall and is pretty seamless once the app is downloaded, but it does tend to make for rather large apps.  The Windows version when packaged as an installer runs in at about 4Mb, which isn&#8217;t too crazy, but the OS X app packed into a compressed DMG file weighs in at 15Mb!  At the end of the day the app does include Python + wxPython + various libraries, so it&#8217;s not a surprise really, but I guess I was kind of hoping that I might develop in Python and get everything for free&#8230;</p>
<p>This is an early version of NoteComb (version 0.2.1), but it&#8217;s core functionality is there.  Most of the extra work I&#8217;ve done has been on adding those little extra bits that aren&#8217;t core to the app, but are generally just expected (e.g. remembering previous window positions, copy/paste, undo/redo etc).</p>
<p>So feel free to download <a href="http://www.psychicorigami.com/notecomb/#download">NoteComb</a> and give it a go.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.psychicorigami.com/2009/05/18/introducing-notecomb/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

