<?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>The Fluther Blog &#187; How&#8217;d they do that?</title>
	<atom:link href="http://blog.fluther.com/category/howd-they-do-that/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.fluther.com</link>
	<description>Tapping and Collecting</description>
	<lastBuildDate>Mon, 16 Jan 2012 13:15:58 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.3</generator>
		<item>
		<title>How’d they do that? Breaking the server.</title>
		<link>http://blog.fluther.com/howd-they-do-that-breaking-the-server/</link>
		<comments>http://blog.fluther.com/howd-they-do-that-breaking-the-server/#comments</comments>
		<pubDate>Fri, 05 Dec 2008 00:46:15 +0000</pubDate>
		<dc:creator>Andrew</dc:creator>
				<category><![CDATA[Fluther]]></category>
		<category><![CDATA[How'd they do that?]]></category>
		<category><![CDATA[News]]></category>
		<category><![CDATA[Technical]]></category>

		<guid isPermaLink="false">http://blog.fluther.com/blog/?p=227</guid>
		<description><![CDATA[Some of you may have noticed the hour that Fluther was down this afternoon. I thought I’d give you a little insight into what we’ve&#8230;]]></description>
			<content:encoded><![CDATA[<p>Some of you may have noticed the hour that Fluther was down this afternoon. I thought I’d give you a little insight into what we’ve been I’ve been up to while Bacio fiddles the bits on the server.</p>
<p>Not wanting anyone to accuse us of not running with the cool kids, I decided to implement a STOMP <a href="http://morethanseven.net/2008/09/14/using-python-and-stompserver-get-started-message-q/" target="_blank">messaging queue</a> system* for Fluther in November. Now, whenever a question gets asked we can offload the matching algorithm to a different machine. The result? You get a much snappier Fluther.</p>
<p>I’ve also rewritten our twisted-based notify server to use the message queue as well, so watch out for better emails when there’s activity on your questions.</p>
<p>Here’s how this afternoon went down. Many of these steps are run automatically using our custom capistrano scripts:</p>
<ul>
<li>Post an MOTD that the site will go down.</li>
<li>Remove one of our webservers from behind perlbal, our load balancer. We use this to test our production environment later.</li>
<li>Take the site down. (Disable the fluther apache site, enable the maintenance apache site)</li>
<li>Using capistrano, update all of our servers with the latest messaging code (which we call lemur)</li>
<li>Update all the servers with the latest fluther code</li>
<li>Migrate the database (we’ve just switched over from using a home-brewed capistrano migration system to <a href="http://south.aeracode.org/" target="_blank">south</a>, it’s nifty!)</li>
<li>Update the new notifier codebase (we call it hydra)</li>
<li>Restart all of our processes: apache, lighttpd, memcached, lemur, and hydra</li>
<li>Check the logs to make sure that everything is running.</li>
<li>Use our web server that we’ve taken out of the load balancer to run a few spot tests.</li>
<li>Reenable the website</li>
<li>Add our web server back into the load balancing pool.</li>
</ul>
<p>In practice, there were a few snags (mostly dependency issues with the new codebase), but overall everything is running very smoothly now.</p>
<p> </p>
<p>* The messaging system is a customized version of <a href="http://djangopeople.net/brosner/">Brian Rosner</a>’s engine library and resembles starling — it’s pluggable for different queue types, provides a nice interface for deferring functions, and has sample clients that consume messages off the queue. Let me know if this interests you!</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.fluther.com/howd-they-do-that-breaking-the-server/feed/</wfw:commentRss>
		<slash:comments>15</slash:comments>
		</item>
		<item>
		<title>django + vim</title>
		<link>http://blog.fluther.com/django-vim/</link>
		<comments>http://blog.fluther.com/django-vim/#comments</comments>
		<pubDate>Fri, 17 Oct 2008 20:59:53 +0000</pubDate>
		<dc:creator>Andrew</dc:creator>
				<category><![CDATA[How'd they do that?]]></category>
		<category><![CDATA[Technical]]></category>
		<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blog.fluther.com/blog/?p=213</guid>
		<description><![CDATA[I’ve finally weaned myself off Komodo, since the vim bindings had just enough tiny errors to annoy me, and I was never satisfied with how&#8230;]]></description>
			<content:encoded><![CDATA[<p>I’ve finally weaned myself off Komodo, since the vim bindings had just enough tiny errors to annoy me, and I was never satisfied with how long it took to find and open a file inside the project.</p>
<p>The one thing I was really missing in vim, though, was solid code-completion feature. After much haranguing, I finally figured out how to enable omnicomplete with django.</p>
<h3 id="Downloadandinstallvim">Download and install vim for OS X</h3>
<ul>
<li>By far the easiest way to get a version of vim that has python compiled is through <a class="ext-link" href="http://www.macports.org/"><span class="icon">macports</span></a>.</p>
<pre class="wiki">sudo port install vim +python
</pre>
</li>
<li>Now replace your old version of vim in your default path (useful for when a shell command calls vim as the editor)
<pre class="wiki">sudo cp /usr/bin/vim /usr/bin/vim7.0
sudo cp /opt/local/bin/vim /usr/bin/vim
</pre>
</li>
<li>Verify that you have python compiled by typing <tt>:python print “Hello”</tt> while in vim
</li>
</ul>
<h3 id="Enableomnicompletion">Enable omnicompletion</h3>
<ul>
<li>I’ve enabled omnicomplete for a bunch of different different completers. Add these lines to your <tt>.vimrc</tt>:</p>
<pre class="wiki">autocmd FileType python set omnifunc=pythoncomplete#Complete
autocmd FileType javascript set omnifunc=javascriptcomplete#CompleteJS
autocmd FileType html set omnifunc=htmlcomplete#CompleteTags
autocmd FileType css set omnifunc=csscomplete#CompleteCSS
</pre>
</li>
</ul>
<h3 id="Runvimsothatyoucanactuallycompletedjangofiles">Run vim so that you can actually complete django files</h3>
<ul>
<li>Omnicomplete imports the modules behind the scenes to help you with completion. Problem is, it won’t be able to import django.db since you don’t have a settings module specified… so any module that imports models won’t work. Not very useful.</p>
<pre class="wiki">DJANGO_SETTINGS_FILE=myapp.settings vim
</pre>
</li>
<li>Verify that imports will work by typing <tt>:python from django import db</tt>
</li>
<li>Now you can just press <tt>&lt;C-X&gt;&lt;C-O&gt;</tt> to omnicomplete!
</li>
</ul>
<p>
I also <strong>highly</strong> recommend the <a class="ext-link" href="http://rope.sourceforge.net/ropevim.html"><span class="icon">rope</span></a> plugin for vim. It simplifies opening files within your project, and even will organize your imports! for you!</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.fluther.com/django-vim/feed/</wfw:commentRss>
		<slash:comments>21</slash:comments>
		</item>
		<item>
		<title>mogileFS for Django</title>
		<link>http://blog.fluther.com/mogilefs-for-django/</link>
		<comments>http://blog.fluther.com/mogilefs-for-django/#comments</comments>
		<pubDate>Fri, 05 Sep 2008 06:28:45 +0000</pubDate>
		<dc:creator>Andrew</dc:creator>
				<category><![CDATA[Fluther]]></category>
		<category><![CDATA[News]]></category>
		<category><![CDATA[Technical]]></category>

		<guid isPermaLink="false">http://blog.fluther.com/blog/?p=198</guid>
		<description><![CDATA[In preparation for DjangoCon, I thought I’d release a little bit of quickly-written-code (read: easily improvable!) that we use to serve our avatars on Fluther.&#8230;]]></description>
			<content:encoded><![CDATA[<p>In preparation for DjangoCon, I thought I’d release a little bit of quickly-written-code (read: easily improvable!) that we use to serve our avatars on Fluther.</p>
<p>Here’s the basic rundown:</p>
<ol>
<li>Get mogilefs on your box</li>
<li>Patch django to support different FileBackends</li>
<li>Get data into mogile</li>
<li>Get data out of mogile</li>
</ol>
<p><strong>Getting mogile</strong></p>
<p>We use Ubuntu on our production boxes, and it just so happens that I’m the debian maintainer for mogile. So. Get the packages from <a href="https://edge.launchpad.net/~awmcclain/+archive" target="_blank">https://edge.launchpad.net/~awmcclain/+archive</a> (add the entries for your distro onto your source.list, run apt-get update then apt-get install mogilefsd mogstored). Setting up mogile is a little beyond this document, but check out the <a href="http://code.google.com/p/mogilefs/" target="_blank">google group</a> if you have any troubles.</p>
<p> </p>
<p><strong>Patch Django (optional)</strong></p>
<p>I see that Marty’s work has been incoporated into Django’s 1.0 beta (congrats, Marty!), but if you’re running an older version (like we are), you’ll need to patch your codebase to support file storage backends. See the patch at <a href="http://code.djangoproject.com/ticket/5361">http://code.djangoproject.com/ticket/5361</a></p>
<p><strong>Install the mogile backend</strong></p>
<p><a href="http://blog.fluther.com/wp-content/uploads/2008/09/mogilefs_for_django1.zip">Download</a> the zip of the files and throw filestorage.py and mogilefs.py somewhere on your pythonpath. The models.py file contains some example code, as well as an urls entry. </p>
<p><strong>Set up the mogile backend</strong></p>
<p>The mogile filestorage backend is fairly simple: it uses URLs (or, rather, parts of URLs) as keys into the mogile database. When the user requests a file stored by mogile (say, an avatar), the URL gets passed to a view which, using a client to the mogile tracker, retrieves the “correct” path (the path that points to the actual file data). The view will then either return the path(s) to perlbal to reproxy, or, if you’re not using perlbal to reproxy (which you should), it serves the data of the file directly from django.</p>
<p>In order for the backend to work, we need to add a few settings variables:</p>
<ul>
<li>MOGILEFS_DOMAIN: The mogile domain that files should read from/written to, e.g “production”</li>
<li>MOGILEFS_TRACKERS: A list of trackers to connect to, e.g. [“foo.sample.com:7001″,“bar.sample.com:7001″]</li>
<li>MOGILEFS_MEDIA_URL (optional): The prefix for URLs that point to mogile files. This is used in a similar way to MEDIA_URL, e.g. “/mogilefs/”</li>
<li>SERVE_WITH_PERLBAL: Boolean that, when True, will pass the paths back in the response in the ‘X-REPROXY-URL’ header. If False, django will serve all mogile media files itself (bad idea for production, but useful if you’re testing on a setup that doesn’t have perlbal running)</li>
<li>DEFAULT_FILE_STORAGE: This is the class that’s used for the backend. You’ll want to set this to “project.app.filestorage.MogileFSStorage” (or wherever you’ve installed the backend) </li>
</ul>
<p> </p>
<p><strong>Gettings files into mogile </strong></p>
<p>The great thing about file backends is that we just need to specify the backend in the model file and everything is taken care for us — all the default save() methods work correctly.</p>
<p>For Fluther, we have two main media types we use mogile for: avatars and thumbnails. Mogile defines “classes” that dictate how each type of file is replicated — so you can make sure you have 3 copies of the original avatar but only 1 of the thumbnail.</p>
<p>In order for classes to behave nicely with the backend framework, we’ve had to do a little tomfoolery. (This is something that may change in future versions of the filestorage framework).</p>
<p>Here’s what the models.py file looks like for the avatars:</p>
<div class="highlight">
<pre><span style="color: #007020; font-weight: bold">from</span> <span style="color: #0e84b5; font-weight: bold">django.core.filestorage</span> <span style="color: #007020; font-weight: bold">import</span> storage</pre>
<pre><span style="color: #60a0b0; font-style: italic"># TODO: Find a better way to deal with classes. Maybe a generator?</span>
<span style="color: #007020; font-weight: bold">class</span> <span style="color: #0e84b5; font-weight: bold">AvatarStorage</span>(storage<span style="color: #666666">.</span>__class__):
    mogile_class <span style="color: #666666">=</span> <span style="color: #4070a0">'avatar'</span> </pre>
<pre><span style="color: #007020; font-weight: bold">class</span> <span style="color: #0e84b5; font-weight: bold">ThumbnailStorage</span>(storage<span style="color: #666666">.</span>__class__):
    mogile_class <span style="color: #666666">=</span> <span style="color: #4070a0">'thumb'</span>

<span style="color: #007020; font-weight: bold">class</span> <span style="color: #0e84b5; font-weight: bold">Avatar</span>(models<span style="color: #666666">.</span>Model):
    user <span style="color: #666666">=</span> models<span style="color: #666666">.</span><span style="color: #007020">ForeignKey</span>(User, null<span style="color: #666666">=</span><span style="color: #007020">True</span>, blank<span style="color: #666666">=</span><span style="color: #007020">True</span>)
    image <span style="color: #666666">=</span> models<span style="color: #666666">.</span><span style="color: #007020">ImageField</span>(storage<span style="color: #666666">=</span>AvatarStorage())
    thumb <span style="color: #666666">=</span> models<span style="color: #666666">.</span><span style="color: #007020">ImageField</span>(storage<span style="color: #666666">=</span>ThumbnailStorage())</pre>
</div>
<p>Each of the custom storage classes defines a “class” attribute which gets passed to the mogile backend behind the scenes.  If you don’t want to worry about mogile classes, don’t need to define a custom storage engine or specify it in the field — the default should work just fine.</p>
<p><strong>Serving files from mogile</strong></p>
<p><strong></strong> Now, all we need to do is plug in the view that serves up mogile data. Here’s what we use:</p>
<div class="highlight">
<pre>urlpatterns <span style="color: #666666">+=</span> patterns(<span style="color: #4070a0">''</span>,
    (<span style="color: #4070a0">r'^</span><span style="color: #70a0d0; font-style: italic">%s</span><span style="color: #4070a0">(?P&lt;key&gt;.*)'</span> <span style="color: #666666">%</span> settings<span style="color: #666666">.</span>MOGILEFS_MEDIA_URL[<span style="color: #40a070">1</span>:], <span style="color: #4070a0">'panda.main.filestorage.serve_mogilefs_file'</span>)
)</pre>
</div>
<p>Any url beginning with the value of MOGILEFS_MEDIA_URL will get passed to our view. Since MOGILEFS_MEDIA_URL requires a leading slash (like MEDIA_URL), we strip that off and pass the rest of the url over to the view.</p>
<p>That’s it! Happy mogiling!</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.fluther.com/mogilefs-for-django/feed/</wfw:commentRss>
		<slash:comments>22</slash:comments>
		</item>
		<item>
		<title>“How’d they do that?” Pagination</title>
		<link>http://blog.fluther.com/howd-they-do-that-pagination/</link>
		<comments>http://blog.fluther.com/howd-they-do-that-pagination/#comments</comments>
		<pubDate>Fri, 11 Apr 2008 16:58:28 +0000</pubDate>
		<dc:creator>Ben</dc:creator>
				<category><![CDATA[Fluther]]></category>
		<category><![CDATA[How'd they do that?]]></category>
		<category><![CDATA[News]]></category>

		<guid isPermaLink="false">http://www.fluther.com/blog/?p=78</guid>
		<description><![CDATA[In this week’s installment, we show you something simple but important: pagination. If you’ve ever seen those little numbers at the bottom of a Google&#8230;]]></description>
			<content:encoded><![CDATA[<p>In this week’s installment, we show you something simple but important: pagination. If you’ve ever seen those little numbers at the bottom of a Google Search, then you’re familiar with a paginated list:</p>
<p><a href="http://blog.fluther.com/wp-content/uploads/2008/04/goog-paginate11.png"><img class="size-full wp-image-79" title="goog-paginate" src="http://blog.fluther.com/wp-content/uploads/2008/04/goog-paginate11.png" alt="" width="345" height="71" /></a></p>
<p>Now we’ll show you how we made a reusable paginator for different parts of Fluther.</p>
<p>First we had to decide how it should look, and which side would be for “Newer” and “Older”. This was surprisingly non-obvious. We thought carefully about our UI, discussed this <a href="http://ephramzerb.com/2007/06/ditching-next-and-previous-for-blog-navigation/">great article</a> by Ivan Kanevski, and came up with something we liked.</p>
<p>Now, how did we build it?</p>
<p>We wanted it to be simple and reusable, so we made a Django inclusion tag that gets called like this:</p>
<pre><span style="color: #666666;">&lt;</span>p class<span style="color: #666666;">=</span><span style="color: #4070a0;">"nav"</span><span style="color: #666666;">&gt;</span>
{<span style="color: #666666;">%</span> render_pagination <span style="color: #666666;">%</span>}
<span style="color: #666666;">&lt;/</span>p<span style="color: #666666;">&gt;</span></pre>
<p>Inside the inclusion tag is some tricky arithmetic, but the newest version of Django provides a <a href="http://www.djangoproject.com/documentation/models/pagination/">Paginator object</a>, so we’ll spare you our implementation. Basically, it takes the range of pages and your current location in the list and tells you which numbers should show up–nothing too fancy.</p>
<p>Now the template itself is pretty straightforward:</p>
<pre>{<span style="color: #666666;">%</span> <span style="font-weight: bold; color: #007020;">if</span> is_paginated <span style="color: #666666;">%</span>}
{<span style="color: #666666;">%</span> <span style="font-weight: bold; color: #007020;">if</span> has_previous <span style="color: #666666;">%</span>}
<span style="color: #666666;">&lt;</span>a href<span style="color: #666666;">=</span><span style="color: #4070a0;">"{{linkprefix}}page={{previous}}#browse"</span> <span style="color: #007020;">id</span><span style="color: #666666;">=</span><span style="color: #4070a0;">"newer"</span><span style="color: #666666;">&gt;&amp;</span><span style="font-style: italic; color: #60a0b0;">#0171; Newer&lt;/a&gt;</span>
{<span style="color: #666666;">%</span> <span style="font-weight: bold; color: #007020;">else</span> <span style="color: #666666;">%</span>}
<span style="color: #666666;">&lt;</span>span <span style="color: #007020;">id</span><span style="color: #666666;">=</span><span style="color: #4070a0;">"newer"</span><span style="color: #666666;">&gt;&amp;</span><span style="font-style: italic; color: #60a0b0;">#0171; Newer&lt;/span&gt;</span>
{<span style="color: #666666;">%</span> endif <span style="color: #666666;">%</span>}
<span style="color: #666666;">&lt;</span>span class<span style="color: #666666;">=</span><span style="color: #4070a0;">"pagination"</span><span style="color: #666666;">&gt;</span>
{<span style="color: #666666;">%</span> <span style="font-weight: bold; color: #007020;">if</span> more_new <span style="color: #666666;">%</span>}
<span style="color: #666666;">&lt;</span>a href<span style="color: #666666;">=</span><span style="color: #4070a0;">"{{linkprefix}}page=1#browse"</span> class<span style="color: #666666;">=</span><span style="color: #4070a0;">"first"</span><span style="color: #666666;">&gt;</span><span style="color: #40a070;">1</span><span style="color: #666666;">&lt;/</span>a<span style="color: #666666;">&gt;...</span>
{<span style="color: #666666;">%</span> endif <span style="color: #666666;">%</span>}
{<span style="color: #666666;">%</span> <span style="font-weight: bold; color: #007020;">for</span> i <span style="font-weight: bold; color: #007020;">in</span> page_range <span style="color: #666666;">%</span>}{<span style="color: #666666;">%</span> spaceless <span style="color: #666666;">%</span>}
{<span style="color: #666666;">%</span> ifequal i page <span style="color: #666666;">%</span>}
<span style="color: #666666;">&lt;</span>span class<span style="color: #666666;">=</span><span style="color: #4070a0;">"current"</span><span style="color: #666666;">&gt;</span>{{i}}<span style="color: #666666;">&lt;/</span>span<span style="color: #666666;">&gt;</span>
{<span style="color: #666666;">%</span> <span style="font-weight: bold; color: #007020;">else</span> <span style="color: #666666;">%</span>}
<span style="color: #666666;">&lt;</span>a href<span style="color: #666666;">=</span><span style="color: #4070a0;">"{{linkprefix}}page={{i}}#browse"</span>
{<span style="color: #666666;">%</span> <span style="font-weight: bold; color: #007020;">if</span> forloop<span style="color: #666666;">.</span>first <span style="color: #666666;">%</span>}class<span style="color: #666666;">=</span><span style="color: #4070a0;">'first'</span>{<span style="color: #666666;">%</span> endif <span style="color: #666666;">%</span>}{<span style="color: #666666;">%</span> <span style="font-weight: bold; color: #007020;">if</span> forloop<span style="color: #666666;">.</span>last<span style="color: #666666;">%</span>}class<span style="color: #666666;">=</span><span style="color: #4070a0;">"last"</span>{<span style="color: #666666;">%</span> endif <span style="color: #666666;">%</span>}
<span style="color: #666666;">&gt;</span>{{i}}<span style="color: #666666;">&lt;/</span>a<span style="color: #666666;">&gt;</span>
{<span style="color: #666666;">%</span> endifequal <span style="color: #666666;">%</span>}
{<span style="color: #666666;">%</span> endspaceless <span style="color: #666666;">%</span>}{<span style="color: #666666;">%</span> endfor <span style="color: #666666;">%</span>}{<span style="color: #666666;">%</span> <span style="font-weight: bold; color: #007020;">if</span> more_old <span style="color: #666666;">%</span>}<span style="color: #666666;">...</span>{<span style="color: #666666;">%</span> endif <span style="color: #666666;">%</span>}
<span style="color: #666666;">&lt;/</span>span<span style="color: #666666;">&gt;</span>
{<span style="color: #666666;">%</span> <span style="font-weight: bold; color: #007020;">if</span> has_next <span style="color: #666666;">%</span>}
<span style="color: #666666;">&lt;</span>a href<span style="color: #666666;">=</span><span style="color: #4070a0;">"{{linkprefix}}page={{next}}#browse"</span> <span style="color: #007020;">id</span><span style="color: #666666;">=</span><span style="color: #4070a0;">"older"</span><span style="color: #666666;">&gt;</span>Older <span style="color: #666666;">&amp;</span><span style="font-style: italic; color: #60a0b0;">#0187;&lt;/a&gt;</span>
{<span style="color: #666666;">%</span> <span style="font-weight: bold; color: #007020;">else</span> <span style="color: #666666;">%</span>}
<span style="color: #666666;">&lt;</span>span <span style="color: #007020;">id</span><span style="color: #666666;">=</span><span style="color: #4070a0;">"older"</span><span style="color: #666666;">&gt;</span>Older <span style="color: #666666;">&amp;</span><span style="font-style: italic; color: #60a0b0;">#0187;&lt;/span&gt;</span>
{<span style="color: #666666;">%</span> endif <span style="color: #666666;">%</span>}
{<span style="color: #666666;">%</span> endif <span style="color: #666666;">%</span>}</pre>
<p>A few things to note here:</p>
<ol>
<li>linkprefix. This allows us to use the paginator on different urls without any modification. Nifty.</li>
<li>We’re assuming the #browse anchor, which isn’t the most flexible solution, but it’s sufficient for now. Eventually we’ll factor this out to be a variable.</li>
</ol>
<p>You may be wondering, how does this actually display multiple pages? How does it all come together? Here’s where Django is a champ. The secret is using <code>django.views.generic.list_detail.object_list</code>. It handles everything for you. You just have to name the list in your template “yourobject_list” and render with object_list. For example, here’s how we rendered the activity page before pagination:</p>
<pre>    <span style="font-weight: bold; color: #007020;">return</span> render_to_response(<span style="color: #4070a0;">'v2/activity.html'</span>, {<span style="color: #4070a0;">'act_list'</span>:new_act,
<span style="color: #4070a0;">'bookmark_list'</span>:all_act,
<span style="color: #4070a0;">'account_title'</span>:<span style="color: #4070a0;">"Activity You’re Following"</span>
}, context_instance<span style="color: #666666;">=</span>RequestContext(request))</pre>
<p>and here’s how we render it with pagination:</p>
<pre>    <span style="font-weight: bold; color: #007020;">return</span> object_list(request, queryset<span style="color: #666666;">=</span>all_act, paginate_by<span style="color: #666666;">=</span><span style="color: #40a070;">25</span>,
template_object_name<span style="color: #666666;">=</span><span style="color: #4070a0;">"activity"</span>,
template_name<span style="color: #666666;">=</span><span style="color: #4070a0;">"v2/activity.html"</span>,
extra_context<span style="color: #666666;">=</span>{
<span style="color: #4070a0;">'new_list'</span>:new_act,
<span style="color: #4070a0;">'pagelinkprefix'</span>:<span style="color: #4070a0;">"/activity/?"</span>,
<span style="color: #4070a0;">'account_title'</span>:<span style="color: #4070a0;">"Activity You’re Following"</span>
})</pre>
<p>Presto! Just like that, you’re in pagination heaven:</p>
<p><a href="http://blog.fluther.com/wp-content/uploads/2008/04/fluth-paginate11.png"><img class="alignnone size-full wp-image-80" title="fluth-paginate" src="http://blog.fluther.com/wp-content/uploads/2008/04/fluth-paginate11.png" alt="" width="458" height="102" /></a></p>
<p style="text-align: left;">Any other mysteries of Fluther you’d like explained? Let us know and our secrets could be yours.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.fluther.com/howd-they-do-that-pagination/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>“How’d they do that?” Real-time chat</title>
		<link>http://blog.fluther.com/howd-they-do-that-real-time-chat/</link>
		<comments>http://blog.fluther.com/howd-they-do-that-real-time-chat/#comments</comments>
		<pubDate>Wed, 02 Apr 2008 16:45:54 +0000</pubDate>
		<dc:creator>Andrew</dc:creator>
				<category><![CDATA[Fluther]]></category>
		<category><![CDATA[How'd they do that?]]></category>
		<category><![CDATA[News]]></category>

		<guid isPermaLink="false">http://www.fluther.com/blog/?p=67</guid>
		<description><![CDATA[In Erik’s last entry in the series, robmandu asked how we implemented the real-time chat. So, ask and ye shall receive! There are a&#8230;]]></description>
			<content:encoded><![CDATA[<p><img src="http://blog.fluther.com/wp-content/uploads/2008/04/picture-3011.png" alt="" title="picture-30" width="348" height="358" class="aligncenter size-full wp-image-69" /></p>
<p>In Erik’s <a href="http://www.fluther.com/blog/index.php/2008/03/25/howd-they-do-that-correcting-punctuation/">last entry</a> in the series, <a href="http://www.fluther.com/users/robmandu">robmandu</a> asked how we implemented the real-time chat. So, ask and ye shall receive! There are a lot of moving parts in the chat system, so I’m going to focus mostly on the back-end for this article — which allows me to show you one of my favorite parts of <a href="http://www.djangoproject.com">Django</a>: template tags! Yeah!<br />
<span id="more-116"></span><br />
Before we get started, let’s have a quick run-down of how the chat works when you visit a question on the site:</p>
<ul>
<li>The initial page is rendered with all the responses so-far, including anyone who might be composing currently.</li>
<li>Every few seconds, you poll us to see if anyone has started writing a response, or if there are any new responses from people who have finished composing.</li>
</ul>
<p>Fairly simple! Let’s look at how we accomplish this… but first, a little history:</p>
<p>Before, when we had just started the iPhone version of the site, we built each response up from scratch using DOM utilities in Javascript. That is: when your browser polled the site, you’d get back a list of usernames (and other data) from the server, then we’d hand-build each HTML element of a response and hook in the JavaScript to make the AJAXified “Great Answer” links work correctly.</p>
<p>Since <a href="http://iphone.fluther.com">iphone.fluther.com</a> uses a slightly different version of displaying responses, when started making changes to this area of the code we realized that it was a nightmare to maintain. Every time we’d make a change we’d have to alter the code in four places: Twice for when the responses were rendered initially (one for each version), and twice when they were constructed in JavaScript.</p>
<p>Then we decided to switch to <a href="http://microformats.org/wiki/rest/ahah">AHAH</a>. Instead of receiving a flat list of data from the server, we now send out the fully-formed HTML which the client then places at the end of the list. We had to fudge a little on our 100% compliance with web-standards, but we scored a win on speed and most importantly, maintainability.</p>
<p>So, here’s how it works:</p>
<p>This is how we initially render our responses:</p>
<div class="highlight" >
<pre><span style="color: #062873; font-weight: bold">&lt;div</span> <span style="color: #4070a0">id=&quot;quiplist&quot;</span><span style="color: #062873; font-weight: bold">&gt;</span>
    <span style="color: #007020">{%</span> <span style="color: #007020; font-weight: bold">for</span> <span style="color: #bb60d5">quip</span> <span style="color: #007020; font-weight: bold">in</span> <span style="color: #bb60d5">quiplist</span> <span style="color: #007020">%}</span>
         <span style="color: #007020">{%</span> <span style="color: #007020; font-weight: bold">render_quip</span> <span style="color: #bb60d5">quip</span> <span style="color: #007020">%}</span>
    <span style="color: #007020">{%</span> <span style="color: #007020; font-weight: bold">endfor</span> <span style="color: #007020">%}</span>
<span style="color: #062873; font-weight: bold">&lt;/div&gt;</span>

<span style="color: #062873; font-weight: bold">&lt;div</span> <span style="color: #4070a0">id=&quot;composelist&quot;</span><span style="color: #062873; font-weight: bold">&gt;</span>
    <span style="color: #007020">{%</span> <span style="color: #007020; font-weight: bold">render_composers</span> <span style="color: #bb60d5">composers</span> <span style="color: #007020">%}</span>
<span style="color: #062873; font-weight: bold">&lt;/div&gt;</span>
</pre>
</div>
<p>As you can see, not much code. Django loops through all the responses in the question and calls the <code>render_quip</code> template tag. Then, it renders all the composers with another template tag. Let’s see what’s in those tags:</p>
<p>First, <code>render_composers.html</code>:</p>
<div class="highlight" >
<pre><span style="color: #007020">{%</span> <span style="color: #007020; font-weight: bold">for</span> <span style="color: #bb60d5">compose</span> <span style="color: #007020; font-weight: bold">in</span> <span style="color: #bb60d5">composers</span> <span style="color: #007020">%}</span>
<span style="color: #062873; font-weight: bold">&lt;div</span> <span style="color: #4070a0">class=&quot;compose&quot;</span><span style="color: #062873; font-weight: bold">&gt;</span>
  <span style="color: #062873; font-weight: bold">&lt;img</span> <span style="color: #4070a0">class=&quot;avatar&quot;</span> <span style="color: #4070a0">src=&quot;</span><span style="color: #007020">{{</span><span style="color: #bb60d5">compose.imageurl</span><span style="color: #007020">}}</span><span style="color: #4070a0">&quot;</span> <span style="color: #4070a0">alt=&quot;</span><span style="color: #007020">{{</span><span style="color: #bb60d5">compose.username</span><span style="color: #007020">}}</span><span style="color: #4070a0">&#39;s avatar&quot;</span><span style="color: #062873; font-weight: bold">/&gt;</span>
  <span style="color: #062873; font-weight: bold">&lt;p&gt;</span><span style="color: #007020">{{</span><span style="color: #bb60d5">compose.username</span><span style="color: #007020">}}</span> is crafting a response<span style="color: #d55537; font-weight: bold">&amp;0133;</span><span style="color: #062873; font-weight: bold">&lt;/p&gt;</span>
  <span style="color: #062873; font-weight: bold">&lt;div</span> <span style="color: #4070a0">class=&quot;qbar&quot;</span><span style="color: #062873; font-weight: bold">&gt;</span>
    <span style="color: #062873; font-weight: bold">&lt;span</span> <span style="color: #4070a0">class=&quot;qspan end&quot;</span><span style="color: #062873; font-weight: bold">&gt;</span>
      <span style="color: #062873; font-weight: bold">&lt;a</span> <span style="color: #4070a0">href=&quot;/users/</span><span style="color: #007020">{{</span><span style="color: #bb60d5">compose.username</span><span style="color: #007020">}}</span><span style="color: #4070a0">/&quot;</span><span style="color: #062873; font-weight: bold">&gt;</span><span style="color: #007020">{{</span><span style="color: #bb60d5">compose.username</span><span style="color: #007020">}}</span><span style="color: #062873; font-weight: bold">&lt;/a&gt;</span> (<span style="color: #062873; font-weight: bold">&lt;span</span> <span style="color: #4070a0">class=&quot;score&quot;</span><span style="color: #062873; font-weight: bold">&gt;</span><span style="color: #007020">{{</span><span style="color: #bb60d5">compose.score</span><span style="color: #007020">}}</span><span style="color: #062873; font-weight: bold">&lt;/span&gt;</span>
      <span style="color: #062873; font-weight: bold">&lt;img</span> <span style="color: #4070a0">width=&quot;8&quot;</span> <span style="color: #4070a0">height=&quot;8&quot;</span> <span style="color: #4070a0">class=&quot;trans-png star&quot;</span> <span style="color: #4070a0">alt=&quot; points&quot;</span> <span style="color: #4070a0">src=&quot;/static/images/v2/star.png&quot;</span><span style="color: #062873; font-weight: bold">/&gt;</span>)
    <span style="color: #062873; font-weight: bold">&lt;/span&gt;</span>
  <span style="color: #062873; font-weight: bold">&lt;/div&gt;</span>
<span style="color: #062873; font-weight: bold">&lt;/div&gt;</span>
<span style="color: #007020">{%</span> <span style="color: #007020; font-weight: bold">endfor</span> <span style="color: #007020">%}</span>
</pre>
</div>
<p>This django template snippet takes a list of user and generates the “Crafting a response” entries.</p>
<p>There’s one aspect of the template tag I’m glossing over: when you call <code>{% render_composers %}</code>, it actually calls a python method “render_composers” which can perform any calculations you need and then pass the variables on to the template I just showed you.</p>
<p>Here’s the method for <code>render_quip</code>:</p>
<div class="highlight" >
<pre><span style="color: #555555; font-weight: bold">@register</span><span style="color: #666666">.</span>inclusion_tag(<span style="color: #4070a0">&#39;v2/render_quip.html&#39;</span>, takes_context<span style="color: #666666">=</span><span style="color: #007020">True</span>)
<span style="color: #555555; font-weight: bold">@inherit_panda_vars</span>
<span style="color: #007020; font-weight: bold">def</span> <span style="color: #06287e">render_quip</span>(context, quip, ahah_format<span style="color: #666666">=</span><span style="color: #007020">False</span>):

    user <span style="color: #666666">=</span> context<span style="color: #666666">.</span>get(<span style="color: #4070a0">&#39;user&#39;</span>)

    marked_great <span style="color: #666666">=</span> <span style="color: #007020">False</span>

    <span style="color: #007020; font-weight: bold">if</span> render_actions <span style="color: #007020; font-weight: bold">and</span> user<span style="color: #666666">.</span>is_authenticated():
        <span style="color: #007020; font-weight: bold">if</span> user <span style="color: #666666">==</span> quip<span style="color: #666666">.</span>user:
            marked_great <span style="color: #666666">=</span> <span style="color: #007020">True</span>
        <span style="color: #007020; font-weight: bold">elif</span> quip<span style="color: #666666">.</span>quipvote_set<span style="color: #666666">.</span>filter(user<span style="color: #666666">=</span>user)<span style="color: #666666">.</span>count() <span style="color: #666666">&gt;</span> <span style="color: #40a070">0</span>:
            marked_great <span style="color: #666666">=</span> <span style="color: #007020">True</span>

    <span style="color: #007020; font-weight: bold">return</span> { <span style="color: #4070a0">&quot;quip&quot;</span>: quip,
             <span style="color: #4070a0">&quot;user&quot;</span>: user,
             <span style="color: #4070a0">&quot;disc&quot;</span>: context<span style="color: #666666">.</span>get(<span style="color: #4070a0">&#39;disc&#39;</span>),
             <span style="color: #4070a0">&quot;ahah_format&quot;</span>: ahah_format,
             <span style="color: #4070a0">&quot;marked_great&quot;</span>: marked_great,
            }
</pre>
</div>
<p>Here we calculate a few things… whether we’re in AHAH mode or not (which affects how we calculate the striping of the responses), whether the user has marked this as a Great Answer! or not, etc.</p>
<p>Now, <code>render_quip.html</code>:</p>
<div class="highlight" >
<pre><span style="color: #007020">{%</span> <span style="color: #007020; font-weight: bold">if</span> <span style="color: #007020; font-weight: bold">not</span> <span style="color: #bb60d5">ahah_format</span> <span style="color: #007020">%}</span><span style="color: #062873; font-weight: bold">&lt;div</span> <span style="color: #4070a0">id=&quot;quip</span><span style="color: #007020">{{</span><span style="color: #bb60d5">quip.id</span><span style="color: #007020">}}</span><span style="color: #4070a0">&quot;</span> <span style="color: #4070a0">class=&quot;quip </span><span style="color: #007020">{%</span> <span style="color: #007020; font-weight: bold">cycle</span> <span style="color: #bb60d5">quip1</span>,<span style="color: #bb60d5">quip2</span> <span style="color: #007020">%}</span><span style="color: #4070a0"></span>
<span style="color: #4070a0">  </span><span style="color: #007020">{%</span> <span style="color: #007020; font-weight: bold">avatar_img</span> <span style="color: #bb60d5">quip.user</span> <span style="color: #007020">%}</span><span style="color: #4070a0"></span>
<span style="color: #4070a0">    &lt;div class=&quot;message</span><span style="border: 1px solid #FF0000">&quot;</span><span style="color: #062873; font-weight: bold">&gt;</span>
      <span style="color: #007020">{{</span><span style="color: #bb60d5">quip.message</span><span style="color: #007020">}}</span>
    <span style="color: #062873; font-weight: bold">&lt;/div&gt;</span>
    <span style="color: #062873; font-weight: bold">&lt;div</span> <span style="color: #4070a0">class=&quot;qbar&quot;</span><span style="color: #062873; font-weight: bold">&gt;</span>

      <span style="color: #062873; font-weight: bold">&lt;span</span> <span style="color: #4070a0">class=&quot;qspan&quot;</span><span style="color: #062873; font-weight: bold">&gt;</span>
        <span style="color: #062873; font-weight: bold">&lt;a</span> <span style="color: #4070a0">href=&quot;</span><span style="color: #007020">{{</span><span style="color: #bb60d5">quip.user.get_absolute_url</span><span style="color: #007020">}}</span><span style="color: #4070a0">&quot;</span><span style="color: #062873; font-weight: bold">&gt;</span><span style="color: #007020">{{</span><span style="color: #bb60d5">quip.user.username</span><span style="color: #007020">}}</span><span style="color: #062873; font-weight: bold">&lt;/a&gt;</span>
        (<span style="color: #062873; font-weight: bold">&lt;span</span> <span style="color: #4070a0">class=&quot;score&quot;</span><span style="color: #062873; font-weight: bold">&gt;</span><span style="color: #007020">{{</span><span style="color: #bb60d5">quip.user.get_profile.score</span><span style="color: #007020">}}</span><span style="color: #062873; font-weight: bold">&lt;/span&gt;</span>
        <span style="color: #062873; font-weight: bold">&lt;img</span> <span style="color: #4070a0">class=&quot;trans-png star&quot;</span> <span style="color: #4070a0">width=&quot;8&quot;</span> <span style="color: #4070a0">height=&quot;8&quot;</span> <span style="color: #4070a0">src=&quot;</span><span style="color: #007020">{{</span><span style="color: #bb60d5">MEDIA_URL</span><span style="color: #007020">}}</span><span style="color: #4070a0">images/v2/star.png&quot;</span> <span style="color: #4070a0">alt=&quot; points&quot;</span><span style="color: #062873; font-weight: bold">/&gt;</span>)<span style="color: #062873; font-weight: bold">&lt;/span&gt;</span>
	<span style="color: #062873; font-weight: bold">&lt;span</span> <span style="color: #4070a0">class=&quot;qspan great-answer&quot;</span><span style="color: #062873; font-weight: bold">&gt;</span>
          <span style="color: #007020">{%</span> <span style="color: #007020; font-weight: bold">if</span> <span style="color: #bb60d5">marked_great</span> <span style="color: #007020">%}</span>
             Great Answer!
          <span style="color: #007020">{%</span> <span style="color: #007020; font-weight: bold">else</span> <span style="color: #007020">%}</span>
	     <span style="color: #062873; font-weight: bold">&lt;a</span> <span style="color: #4070a0">href=&quot;/usefulquip/?id=</span><span style="color: #007020">{{</span><span style="color: #bb60d5">quip.id</span><span style="color: #007020">}}</span><span style="color: #4070a0">&quot;</span><span style="color: #062873; font-weight: bold">&gt;</span><span style="color: #d55537; font-weight: bold">&amp;#8220;</span>Great Answer<span style="color: #d55537; font-weight: bold">&amp;#8221;</span><span style="color: #062873; font-weight: bold">&lt;/a&gt;</span>
          <span style="color: #007020">{%</span> <span style="color: #007020; font-weight: bold">endif</span> <span style="color: #007020">%}</span>
        <span style="color: #062873; font-weight: bold">&lt;/span&gt;</span>
       .
       .
       .
      <span style="color: #062873; font-weight: bold">&lt;/span&gt;</span>
  <span style="color: #062873; font-weight: bold">&lt;/div&gt;</span>
<span style="color: #007020">{%</span> <span style="color: #007020; font-weight: bold">if</span> <span style="color: #007020; font-weight: bold">not</span> <span style="color: #bb60d5">ahah_format</span> <span style="color: #007020">%}</span><span style="color: #062873; font-weight: bold">&lt;/div&gt;</span><span style="color: #007020">{%</span> <span style="color: #007020; font-weight: bold">endif</span> <span style="color: #007020">%}</span>
</pre>
</div>
<p>Here we see the striping I talked about (that’s what <code>{%&nbsp;cycle&nbsp;%}</code> does: each alternating response we render gets a different css class). We also see how the <code>marked_great</code> variable that we set in our python method affects the way we display our response.</p>
<p>So how does this all plug into the web browser?</p>
<div class="highlight" >
<pre>handle_listen<span style="color: #666666">:</span> <span style="color: #007020; font-weight: bold">function</span>(transport) {
    <span style="color: #007020; font-weight: bold">var</span> response <span style="color: #666666">=</span> <span style="color: #007020">eval</span>(<span style="color: #4070a0">&#39;(&#39;</span> <span style="color: #666666">+</span> transport.responseText <span style="color: #666666">+</span> <span style="color: #4070a0">&#39;)&#39;</span>);
    <span style="color: #007020; font-weight: bold">var</span> quips <span style="color: #666666">=</span> response.quips<span style="color: #666666">;</span>

    <span style="color: #007020; font-weight: bold">if</span> (quips.length <span style="color: #666666">&gt;</span> <span style="color: #40a070">0</span>) {
        fluther_disc.since <span style="color: #666666">=</span> response.lastid<span style="color: #666666">;</span>

        <span style="color: #60a0b0; font-style: italic">// zebra striping</span>
<span style="color: #60a0b0; font-style: italic"></span>        <span style="color: #007020; font-weight: bold">var</span> quiplist <span style="color: #666666">=</span> <span style="color: #666666">YAHOO.util.Dom</span>.getElementsByClassName(<span style="color: #4070a0">&quot;quip&quot;</span><span style="color: #666666">,</span> <span style="color: #4070a0">&quot;div&quot;</span><span style="color: #666666">,</span> <span style="color: #4070a0">&quot;quiplist&quot;</span>); <span style="color: #60a0b0; font-style: italic">//select only the divs</span>
<span style="color: #60a0b0; font-style: italic"></span>        <span style="color: #007020; font-weight: bold">if</span> (quiplist.length <span style="color: #666666">&gt;</span> <span style="color: #40a070">0</span> <span style="color: #666666">&amp;&amp;</span> quiplist<span style="color: #666666">[</span>quiplist.length<span style="border: 1px solid #FF0000">-</span><span style="color: #40a070">1</span>].className <span style="color: #666666">==</span> <span style="color: #4070a0">&quot;quip quip1&quot;</span>){
             <span style="color: #007020; font-weight: bold">var</span> zebra_classes <span style="color: #666666">=</span> <span style="color: #666666">[</span><span style="color: #4070a0">&quot;quip quip2&quot;</span><span style="color: #666666">,</span> <span style="color: #4070a0">&quot;quip quip1&quot;</span>];
        } <span style="color: #007020; font-weight: bold">else</span> {
             <span style="color: #007020; font-weight: bold">var</span> zebra_classes <span style="color: #666666">=</span> <span style="color: #666666">[</span><span style="color: #4070a0">&quot;quip quip1&quot;</span><span style="color: #666666">,</span> <span style="color: #4070a0">&quot;quip quip2&quot;</span>];
        }

        <span style="color: #60a0b0; font-style: italic">//do the building</span>
<span style="color: #60a0b0; font-style: italic"></span>        <span style="color: #007020; font-weight: bold">for</span> (<span style="color: #007020; font-weight: bold">var</span> i <span style="color: #666666">=</span> <span style="color: #40a070">0</span><span style="color: #666666">;</span> i <span style="color: #666666">&lt;</span> quips.length<span style="color: #666666">;</span> i<span style="color: #666666">++</span>){

            <span style="color: #007020; font-weight: bold">new</span> <span style="color: #666666">Q</span>uip(quips<span style="color: #666666">[</span>i].html<span style="color: #666666">,</span> quips<span style="color: #666666">[</span>i].id<span style="color: #666666">,</span> zebra_classes<span style="color: #666666">[</span>i<span style="color: #666666">%</span><span style="color: #40a070">2</span>]);
        }
    }

    <span style="color: #60a0b0; font-style: italic">//build the compose template</span>
<span style="color: #60a0b0; font-style: italic"></span>    $(<span style="color: #4070a0">&#39;composelist&#39;</span>).innerHTML <span style="color: #666666">=</span> response.compose_html<span style="color: #666666">;</span>

    <span style="color: #007020; font-weight: bold">return</span> response<span style="color: #666666">;</span>
}
</pre>
</div>
<p>When we get into the JavaScript, we have to calculate the striping for each new response that gets added, and we just add in the HTML we got back from the server into the webpage. Now, if we need to change the format of the responses (like, say, for the iPhone) we can re-use most of our code!</p>
<p>Coming up next for me: Why we chose YUI over Scriptaculous and MochiKit!</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.fluther.com/howd-they-do-that-real-time-chat/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>“How’d they do that?” Correcting punctuation</title>
		<link>http://blog.fluther.com/howd-they-do-that-correcting-punctuation/</link>
		<comments>http://blog.fluther.com/howd-they-do-that-correcting-punctuation/#comments</comments>
		<pubDate>Tue, 25 Mar 2008 15:48:04 +0000</pubDate>
		<dc:creator>erik</dc:creator>
				<category><![CDATA[Fluther]]></category>
		<category><![CDATA[How'd they do that?]]></category>
		<category><![CDATA[News]]></category>

		<guid isPermaLink="false">http://www.fluther.com/blog/index.php/2008/03/25/howd-they-do-that-correcting-punctuation-and-capitalization/</guid>
		<description><![CDATA[In my last post, I announced a new feature that automatically corrects the punctuation of questions using “fancy computer logic”.  I’d like to reveal some&#8230;]]></description>
			<content:encoded><![CDATA[<p>In my <a href="http://www.fluther.com/blog/index.php/2008/03/23/fixing-your-lousy-punctuation-and-capitalization/">last post</a>, I announced a new feature that automatically corrects the punctuation of questions using “fancy computer logic”.  I’d like to reveal some of that computer logic, in the first in a series of blog posts where we explore the technology that drives Fluther.</p>
<p><span id="more-97"></span></p>
<p>Fluther uses the <a href="http://www.djangoproject.com/">Django</a> web framework, so our application code is written in <a href="http://www.python.org/">Python</a>.  I <a href="http://twitter.com/sferik/statuses/765732850">just started learning Python</a> and really like a lot of the features of the language.</p>
<p>One of those features is the <code><a href="http://docs.python.org/lib/module-re.html">re</a></code> module, which allows you to search through text for patterns.</p>
<p>To automatically correct question punctuation, I first needed to detect two patterns:</p>
<ol>
<li>Questions that end in multiple question marks or exclaimation points</li>
<li>Questions that end in no punctuation</li>
</ol>
<p>Here is the regular expression I used to detect the first pattern:</p>
<blockquote style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 40px; border-width: initial; border-color: initial; border-style: none; padding: 0px" class="webkit-indent-blockquote"><p><span style="color: #00008b; font-family: '-webkit-monospace'; font-size: 13px" class="Apple-style-span"><code>MULTIPLE_PUNCTUATION_END = re.compile(&quot;[<span style="color: #ff0000;">?|!</span>]<span style="color: #009900;">+</span><span style="color: #0000ff;">$</span>&quot;)</code></span></p></blockquote>
<p>In English, this regular expression would read “<span style="color: #ff0000;">a question mark or exclamation point</span>, <span style="color: #009900;">repeated one or more times</span>, <span style="color: #0000ff;">at the end of the string</span>”. Note that I used <code>re.compile</code>, which compiles a regular expression for efficiency.</p>
<p>Detecting this pattern and swapping it out for a single punctuation mark can be done with just one line of code:</p>
<blockquote style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 40px; border-width: initial; border-color: initial; border-style: none; padding: 0px" class="webkit-indent-blockquote"><p><span style="color: #00008b; font-family: '-webkit-monospace'; font-size: 13px" class="Apple-style-span"><code>question = MULTIPLE_PUNCTUATION_END.sub(question[-1], question)</code></span></p></blockquote>
<p>That was easy.  My second task was to detect questions with no ending punctuation:</p>
<blockquote style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 40px; border-width: initial; border-color: initial; border-style: none; padding: 0px" class="webkit-indent-blockquote"><p><span style="color: #00008b; font-family: '-webkit-monospace'; font-size: 13px" class="Apple-style-span"><code>NO_PUNCTUATION_END = re.compile(&quot;[<span style="color: #ff0000;">^</span>(<span style="color: #009900;">?|.|!</span>)]<span style="color: #0000ff;">$</span>&quot;)</code></span></p></blockquote>
<p>You would read this regular expression as “<span style="color: #ff0000;">anything other than</span>, <span style="color: #009900;">a question mark, period, or exclaimation point</span>, <span style="color: #0000ff;">at the end of the string</span>”.</p>
<p>When no punctuation is detected at the end of a question, I simply append a question mark.</p>
<blockquote style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 40px; border-width: initial; border-color: initial; border-style: none; padding: 0px" class="webkit-indent-blockquote"><p><span style="color: #00008b; font-family: '-webkit-monospace'; font-size: 13px" class="Apple-style-span"><code>if NO_PUNCTUATION_END.search(question):<br />
&nbsp;&nbsp;&nbsp;&nbsp;question += &quot;?&quot;</code></span></p></blockquote>
<p>With just a few lines of code, I was able to pretty-up the punctuation on Fluther.</p>
<p>Please leave comments to let me know what you think of this new series.  I wanted to start with something relatively simple, but I’d like to dig even deeper into the Fluther codebase.  What are some features on Fluther where you’ve asked “How’d they do that”?</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.fluther.com/howd-they-do-that-correcting-punctuation/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
	</channel>
</rss>

<!-- Dynamic Page Served (once) in 0.729 seconds -->

